mirror of
https://iceshrimp.dev/crimekillz/trashposs
synced 2024-11-21 16:33:48 +01:00
Achievements 1.5/2
This commit is contained in:
parent
30ad1c47f0
commit
d2a86713af
@ -2,7 +2,7 @@ version: "3"
|
||||
|
||||
services:
|
||||
web:
|
||||
image: iceshrimp.dev/Crimekillz/trashposs:dev
|
||||
image: iceshrimp.dev/crimekillz/trashposs:dev
|
||||
### If you want to build the image locally
|
||||
# build: .
|
||||
### If you want to build the image locally AND use Docker 20.10
|
||||
|
@ -1140,218 +1140,218 @@ bitYou: "chomped you"
|
||||
bitYouBack: "chomped you back"
|
||||
achievements: "Achievements"
|
||||
_achievements:
|
||||
earnedAt: "獲得日時"
|
||||
earnedAt: "Earn date and time"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "Just set up my TrashPoss account"
|
||||
description: "This is my first post"
|
||||
flavor: "Screm into the void!"
|
||||
_notes10:
|
||||
title: "いくつかのノート"
|
||||
description: "ノートを10回投稿した"
|
||||
title: "Some Posts"
|
||||
description: "Posted 10 times"
|
||||
_notes100:
|
||||
title: "たくさんのノート"
|
||||
description: "ノートを100回投稿した"
|
||||
title: "Lots of Posts"
|
||||
description: "Posted 100 times"
|
||||
_notes500:
|
||||
title: "ノートまみれ"
|
||||
description: "ノートを500回投稿した"
|
||||
title: "Post Covered"
|
||||
description: "Posted 500 times"
|
||||
_notes1000:
|
||||
title: "ノートの山"
|
||||
description: "ノートを1,000回投稿した"
|
||||
title: "Pile of Posts"
|
||||
description: "Posted 1,000 times"
|
||||
_notes5000:
|
||||
title: "湧き出るノート"
|
||||
description: "ノートを5,000回投稿した"
|
||||
title: "Gushing Post"
|
||||
description: "Posted 5,000 times"
|
||||
_notes10000:
|
||||
title: "スーパーノート"
|
||||
description: "ノートを10,000回投稿した"
|
||||
title: "Super Post"
|
||||
description: "Posted 10,000 times"
|
||||
_notes20000:
|
||||
title: "ニードモアノート"
|
||||
description: "ノートを20,000回投稿した"
|
||||
title: "Need More Posts"
|
||||
description: "Posted 20,000 times"
|
||||
_notes30000:
|
||||
title: "ノートノートノート"
|
||||
description: "ノートを30,000回投稿した"
|
||||
title: "Post Post Post"
|
||||
description: "Posted 30,000 times"
|
||||
_notes40000:
|
||||
title: "ノート工場"
|
||||
description: "ノートを40,000回投稿した"
|
||||
title: "Post Factory"
|
||||
description: "Posted 40,000 times"
|
||||
_notes50000:
|
||||
title: "ノートの惑星"
|
||||
description: "ノートを50,000回投稿した"
|
||||
title: "Planet of Posts"
|
||||
description: "Posted 50,000 times"
|
||||
_notes60000:
|
||||
title: "ノートクエーサー"
|
||||
description: "ノートを60,000回投稿した"
|
||||
title: "Post Quasar"
|
||||
description: "Posted 60,000 times"
|
||||
_notes70000:
|
||||
title: "ブラックノートホール"
|
||||
description: "ノートを70,000回投稿した"
|
||||
title: "Black Post Hole"
|
||||
description: "Posted 70,000 times"
|
||||
_notes80000:
|
||||
title: "ノートギャラクシー"
|
||||
description: "ノートを80,000回投稿した"
|
||||
title: "Post Galaxy"
|
||||
description: "Posted 80,000 times"
|
||||
_notes90000:
|
||||
title: "ノートバース"
|
||||
description: "ノートを90,000回投稿した"
|
||||
title: "Postiverse"
|
||||
description: "Posted 90,000 times"
|
||||
_notes100000:
|
||||
title: "ALL YOUR POSTS ARE BELONG TO US"
|
||||
description: "ノートを100,000回投稿した"
|
||||
flavor: "そんなに書くことある?"
|
||||
description: "Posted 100,000 times"
|
||||
flavor: "Do you have much to write about?"
|
||||
_login3:
|
||||
title: "ビギナーⅠ"
|
||||
description: "通算ログイン日数が3日"
|
||||
flavor: "今日からね僕は ミスキストってことで"
|
||||
title: "Beginner I"
|
||||
description: "Total login days are 3 days"
|
||||
flavor: "From today onwards, I think they're a Misquist."
|
||||
_login7:
|
||||
title: "ビギナーⅡ"
|
||||
description: "通算ログイン日数が7日"
|
||||
flavor: "慣れてきましたか?"
|
||||
title: "Beginner II"
|
||||
description: "Total login days are 7 days"
|
||||
flavor: "Are you getting used to it?"
|
||||
_login15:
|
||||
title: "ビギナーⅢ"
|
||||
description: "通算ログイン日数が15日"
|
||||
title: "Beginner III"
|
||||
description: "Total number of login days is 15"
|
||||
_login30:
|
||||
title: "ミスキストⅠ"
|
||||
description: "通算ログイン日数が30日"
|
||||
title: "Miscist I"
|
||||
description: "Total login days are 30 days"
|
||||
_login60:
|
||||
title: "ミスキストⅡ"
|
||||
description: "通算ログイン日数が60日"
|
||||
title: "Miscist II"
|
||||
description: "Total number of login days is 60"
|
||||
_login100:
|
||||
title: "ミスキストⅢ"
|
||||
description: "通算ログイン日数が100日"
|
||||
flavor: "そのユーザー、ミスキストにつき"
|
||||
title: "Miscist III"
|
||||
description: "Total number of login days is 100"
|
||||
flavor: "For that user, Misquist"
|
||||
_login200:
|
||||
title: "常連Ⅰ"
|
||||
description: "通算ログイン日数が200日"
|
||||
title: "Regular I"
|
||||
description: "Total number of login days is 200"
|
||||
_login300:
|
||||
title: "常連Ⅱ"
|
||||
description: "通算ログイン日数が300日"
|
||||
title: "Regular II"
|
||||
description: "Total number of login days is 300"
|
||||
_login400:
|
||||
title: "常連Ⅲ"
|
||||
description: "通算ログイン日数が400日"
|
||||
title: "Regular III"
|
||||
description: "Total number of login days is 400"
|
||||
_login500:
|
||||
title: "ベテランⅠ"
|
||||
description: "通算ログイン日数が500日"
|
||||
flavor: "諸君、私はノートが好きだ"
|
||||
title: "Veteran I"
|
||||
description: "Total number of login days is 500"
|
||||
flavor: "Well, I like posts..."
|
||||
_login600:
|
||||
title: "ベテランⅡ"
|
||||
description: "通算ログイン日数が600日"
|
||||
title: "Veteran II"
|
||||
description: "Total number of login days is 600"
|
||||
_login700:
|
||||
title: "ベテランⅢ"
|
||||
description: "通算ログイン日数が700日"
|
||||
title: "Veteran III"
|
||||
description: "Total number of login days is 700"
|
||||
_login800:
|
||||
title: "ノートマスターⅠ"
|
||||
description: "通算ログイン日数が800日"
|
||||
title: "Post Master I"
|
||||
description: "Total number of login days is 800"
|
||||
_login900:
|
||||
title: "ノートマスターⅡ"
|
||||
description: "通算ログイン日数が900日"
|
||||
title: "Post Master II"
|
||||
description: "Total number of login days is 900"
|
||||
_login1000:
|
||||
title: "ノートマスターⅢ"
|
||||
description: "通算ログイン日数が1,000日"
|
||||
flavor: "Misskeyを使ってくれてありがとう!"
|
||||
title: "Post Master III"
|
||||
description: "Total number of login days is 1,000"
|
||||
flavor: "Thank you for using TrashPoss!"
|
||||
_noteClipped1:
|
||||
title: "クリップせずにはいられないな"
|
||||
description: "初めてノートをクリップした"
|
||||
title: "I can't help but clip"
|
||||
description: "Clipped a post for the first time"
|
||||
_noteFavorited1:
|
||||
title: "星をみるひと"
|
||||
description: "初めてノートをお気に入りに登録した"
|
||||
title: "People who look at the stars"
|
||||
description: "I registered a note as a favorite for the first time"
|
||||
_profileFilled:
|
||||
title: "準備万端"
|
||||
description: "プロフィール設定を行った"
|
||||
title: "Ready to go"
|
||||
description: "Profile settings were made"
|
||||
_markedAsCat:
|
||||
title: "吾輩は猫である"
|
||||
description: "アカウントをCatとして設定した"
|
||||
flavor: "名前はまだない。"
|
||||
title: "I am a kitty"
|
||||
description: "Account set up in Cat-mode"
|
||||
flavor: "Please name me :3"
|
||||
_following1:
|
||||
title: "はじめてのフォロー"
|
||||
description: "初めてフォローした"
|
||||
title: "First follow"
|
||||
description: "First time following"
|
||||
_following10:
|
||||
title: "ついてく、ついてく"
|
||||
description: "フォローが10人を超した"
|
||||
title: "Follow me, follow me"
|
||||
description: "More than 10 followers"
|
||||
_following50:
|
||||
title: "友達たくさん"
|
||||
description: "フォローが50人を超した"
|
||||
title: "Lots of friends"
|
||||
description: "Following exceeded 50 people"
|
||||
_following100:
|
||||
title: "友達100人"
|
||||
description: "フォローが100人を超した"
|
||||
title: "100 Friends"
|
||||
description: "Following exceeded 100 people"
|
||||
_following300:
|
||||
title: "友達過多"
|
||||
description: "フォローが300人を超した"
|
||||
title: "Too many friends"
|
||||
description: "Over 300 followers"
|
||||
_followers1:
|
||||
title: "はじめてのフォロワー"
|
||||
description: "初めてフォローされた"
|
||||
title: "First Follower"
|
||||
description: "First followed"
|
||||
_followers10:
|
||||
title: "フォローミー!"
|
||||
description: "フォロワーが10人を超した"
|
||||
title: "Follow me!"
|
||||
description: "More than 10 followers"
|
||||
_followers50:
|
||||
title: "ぞろぞろ"
|
||||
description: "フォロワーが50人を超した"
|
||||
title: "Zorozoro"
|
||||
description: "Over 50 followers"
|
||||
_followers100:
|
||||
title: "人気者"
|
||||
description: "フォロワーが100人を超した"
|
||||
title: "Popular"
|
||||
description: "Over 100 followers"
|
||||
_followers300:
|
||||
title: "一列でお並びください"
|
||||
description: "フォロワーが300人を超した"
|
||||
title: "Please stand in line"
|
||||
description: "Over 300 followers"
|
||||
_followers500:
|
||||
title: "基地局"
|
||||
description: "フォロワーが500人を超した"
|
||||
title: "Base Station"
|
||||
description: "Over 500 followers"
|
||||
_followers1000:
|
||||
title: "インフルエンサー"
|
||||
description: "フォロワーが1,000人を超した"
|
||||
title: "Influencer"
|
||||
description: "Over 1,000 followers"
|
||||
_collectAchievements30:
|
||||
title: "実績コレクター"
|
||||
description: "実績を30個以上獲得した"
|
||||
title: "Achievement Collector"
|
||||
description: "Obtained 30 or more achievements"
|
||||
_iLoveMisskey:
|
||||
title: "I Love TrashPoss"
|
||||
description: "\"I ❤ #TrashPoss\"を投稿した"
|
||||
flavor: "Misskeyを使ってくださりありがとうございます! by 開発チーム"
|
||||
description: "I posted \"I ❤ #TrashPoss\""
|
||||
flavor: "Thank you for using TrashPoss! by Development Team"
|
||||
_client30min:
|
||||
title: "ひとやすみ"
|
||||
description: "クライアントを起動してから30分以上経過した"
|
||||
title: "Take a break"
|
||||
description: "More than 30 minutes have passed since the client was started"
|
||||
_noteDeletedWithin1min:
|
||||
title: "いまのなし"
|
||||
description: "投稿してから1分以内にその投稿を削除した"
|
||||
title: "Now Nothing"
|
||||
description: "The post was deleted within 1 minute of posting"
|
||||
_postedAtLateNight:
|
||||
title: "夜行性"
|
||||
description: "深夜にノートを投稿した"
|
||||
flavor: "そろそろ寝よう。"
|
||||
title: "Nocturnal"
|
||||
description: "Posted a note late at night"
|
||||
flavor: "Let's go to sleep."
|
||||
_postedAt0min0sec:
|
||||
title: "時報"
|
||||
description: "0分0秒にノートを投稿した"
|
||||
flavor: "ポッ ポッ ポッ ピーン"
|
||||
title: "Time signal"
|
||||
description: "Posted a note at 0 minutes 0 seconds"
|
||||
flavor: "Pop Pop Pop Peen"
|
||||
_selfQuote:
|
||||
title: "自己言及"
|
||||
description: "自分のノートを引用した"
|
||||
title: "Self-reference"
|
||||
description: "Quoted from my own notes"
|
||||
_htl20npm:
|
||||
title: "流れるTL"
|
||||
description: "ホームタイムラインの流速が20npmを越す"
|
||||
title: "Flowing TL"
|
||||
description: "Home timeline flow rate exceeds 20npm"
|
||||
_driveFolderCircularReference:
|
||||
title: "循環参照"
|
||||
description: "ドライブのフォルダを再帰的な入れ子にしようとした"
|
||||
title: "Circular Reference"
|
||||
description: "Attempted to recursively nest drive folders"
|
||||
_reactWithoutRead:
|
||||
title: "ちゃんと読んだ?"
|
||||
description: "100文字以上のテキストを含むノートに投稿されてから3秒以内にリアクションした"
|
||||
title: "Did you read it properly?"
|
||||
description: "Reacted within 3 seconds of being posted to a note containing more than 100 characters of text."
|
||||
_clickedClickHere:
|
||||
title: "ここをクリック"
|
||||
description: "ここをクリックした"
|
||||
title: "Click here"
|
||||
description: "Clicked here"
|
||||
_justPlainLucky:
|
||||
title: "単なるラッキー"
|
||||
description: "10秒ごとに0.01%の確率で獲得"
|
||||
title: "Just Lucky"
|
||||
description: "Obtained every 10 seconds with a 0.01% chance"
|
||||
_setNameToSyuilo:
|
||||
title: "神様コンプレックス"
|
||||
description: "名前を syuilo に設定した"
|
||||
title: "Poss gang, Poss gang"
|
||||
description: "Name set to Crimekillz"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一周年"
|
||||
description: "アカウント作成から1年経過した"
|
||||
title: "One Year Anniversary"
|
||||
description: "One year has passed since account creation"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "二周年"
|
||||
description: "アカウント作成から2年経過した"
|
||||
title: "Second Anniversary"
|
||||
description: "2 years have passed since account creation"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "三周年"
|
||||
description: "アカウント作成から3年経過した"
|
||||
title: "Third Anniversary"
|
||||
description: "3 years have passed since account creation"
|
||||
_loggedInOnBirthday:
|
||||
title: "ハッピーバースデー"
|
||||
description: "誕生日にログインした"
|
||||
title: "Happy Birthday"
|
||||
description: "Logged in on my birthday"
|
||||
_cookieClicked:
|
||||
title: "クッキーをクリックするゲーム"
|
||||
description: "クッキーをクリックした"
|
||||
flavor: "ソフト間違ってない?"
|
||||
title: "Cookie Clicking Game"
|
||||
description: "You clicked on the cookie"
|
||||
flavor: "Isn't the software wrong?"
|
||||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Brain Diverへのリンクを投稿した"
|
||||
description: "Posted a link to Brain Diver"
|
||||
flavor: "TrashPoss-TrashPoss La-Tu-Ma"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Reduces the effort of server moderation through automatically recognizing
|
||||
|
@ -8,7 +8,7 @@ export class FederatedBite1705528046452 implements MigrationInterface {
|
||||
await queryRunner.query(`CREATE TABLE "bite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "uri" character varying(512), "userId" character varying(32) NOT NULL, "targetType" "public"."bite_targettype_enum" NOT NULL, "targetUserId" character varying(32), "targetBiteId" character varying(32), "replied" boolean NOT NULL DEFAULT true, CONSTRAINT "CHK_c3a20c5756ccff3133f8927500" CHECK ("targetUserId" IS NOT NULL OR "targetBiteId" IS NOT NULL), CONSTRAINT "PK_1887f3f621a4a7655a1b78bfd66" PRIMARY KEY ("id")); COMMENT ON COLUMN "bite"."uri" IS 'null if local'`);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ADD "biteId" character varying(32)`);
|
||||
await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" RENAME TO "user_profile_mutingnotificationtypes_enum_old"`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'bite')`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app', 'bite')`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum"[]`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
||||
@ -18,7 +18,7 @@ export class FederatedBite1705528046452 implements MigrationInterface {
|
||||
await queryRunner.query(`ALTER TABLE "bite" ADD CONSTRAINT "FK_5d5f68610583f2e0b6785d3c0e9" FOREIGN KEY ("targetBiteId") REFERENCES "bite"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_c54844158c1eead7042e7ca4c83" FOREIGN KEY ("biteId") REFERENCES "bite"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'bite')`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app', 'bite')`);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`);
|
||||
}
|
||||
@ -28,7 +28,7 @@ export class FederatedBite1705528046452 implements MigrationInterface {
|
||||
await queryRunner.query(`ALTER TABLE "bite" DROP CONSTRAINT "FK_5d5f68610583f2e0b6785d3c0e9"`);
|
||||
await queryRunner.query(`ALTER TABLE "bite" DROP CONSTRAINT "FK_a646fbbeb6efa2531c75fec46b9"`);
|
||||
await queryRunner.query(`ALTER TABLE "bite" DROP CONSTRAINT "FK_8d00aa79e157364ac1f60c15098"`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
||||
@ -37,7 +37,7 @@ export class FederatedBite1705528046452 implements MigrationInterface {
|
||||
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "biteId"`);
|
||||
await queryRunner.query(`DROP TABLE "bite"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."bite_targettype_enum"`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`);
|
||||
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`);
|
||||
|
@ -138,6 +138,11 @@ export class Notification {
|
||||
})
|
||||
public choice: number | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128, nullable: true,
|
||||
})
|
||||
public achievement: string | null;
|
||||
|
||||
/**
|
||||
* App notification body
|
||||
*/
|
||||
|
@ -239,6 +239,19 @@ export class UserProfile {
|
||||
})
|
||||
public mutingNotificationTypes: typeof notificationTypes[number][];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 32, array: true, default: '{}',
|
||||
})
|
||||
public loggedInDates: string[];
|
||||
|
||||
@Column('jsonb', {
|
||||
default: [],
|
||||
})
|
||||
public achievements: {
|
||||
name: string;
|
||||
unlockedAt: number;
|
||||
}[];
|
||||
|
||||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
|
@ -149,6 +149,11 @@ export const NotificationRepository = db.getRepository(Notification).extend({
|
||||
bite: notification.bite ?? await Bites.findOneBy({ id: notification.biteId! }),
|
||||
}
|
||||
: {}),
|
||||
...(notification.type === "achievementEarned"
|
||||
? {
|
||||
achievement: notification.achievement,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
} from "../index.js";
|
||||
import type { Instance } from "../entities/instance.js";
|
||||
import AsyncLock from "async-lock";
|
||||
import { UserProfile } from "../entities/user-profile.js";
|
||||
|
||||
const userInstanceCache = new Cache<Instance | null>(
|
||||
"userInstance",
|
||||
@ -412,6 +413,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
detail?: D;
|
||||
includeSecrets?: boolean;
|
||||
isPrivateMode?: boolean;
|
||||
userProfile?: UserProfile,
|
||||
},
|
||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||
const opts = Object.assign(
|
||||
@ -447,9 +449,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
.orderBy("pin.id", "DESC")
|
||||
.getMany()
|
||||
: [];
|
||||
const profile = opts.detail
|
||||
? await UserProfiles.findOneByOrFail({ userId: user.id })
|
||||
: null;
|
||||
const profile = opts.detail ? (opts.userProfile ?? await UserProfiles.findOneByOrFail({ userId: user.id })) : null;
|
||||
|
||||
const followingCount =
|
||||
profile == null
|
||||
@ -625,6 +625,8 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
mutedInstances: profile!.mutedInstances,
|
||||
mutingNotificationTypes: profile!.mutingNotificationTypes,
|
||||
emailNotificationTypes: profile!.emailNotificationTypes,
|
||||
achievements: profile!.achievements,
|
||||
loggedInDays: profile!.loggedInDates.length,
|
||||
}
|
||||
: {}),
|
||||
|
||||
|
@ -81,5 +81,10 @@ export const packedNotificationSchema = {
|
||||
optional: true,
|
||||
nullable: true,
|
||||
},
|
||||
achievement: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@ -175,6 +175,7 @@ import * as ep___i_2fa_removeKey from "./endpoints/i/2fa/remove-key.js";
|
||||
import * as ep___i_2fa_unregister from "./endpoints/i/2fa/unregister.js";
|
||||
import * as ep___i_apps from "./endpoints/i/apps.js";
|
||||
import * as ep___i_authorizedApps from "./endpoints/i/authorized-apps.js";
|
||||
import * as ep___i_claimAchievement from './endpoints/i/claim-achievement.js';
|
||||
import * as ep___i_changePassword from "./endpoints/i/change-password.js";
|
||||
import * as ep___i_deleteAccount from "./endpoints/i/delete-account.js";
|
||||
import * as ep___i_exportBlocking from "./endpoints/i/export-blocking.js";
|
||||
@ -332,6 +333,7 @@ import * as ep___users_searchByUsernameAndHost from "./endpoints/users/search-by
|
||||
import * as ep___users_search from "./endpoints/users/search.js";
|
||||
import * as ep___users_show from "./endpoints/users/show.js";
|
||||
import * as ep___users_stats from "./endpoints/users/stats.js";
|
||||
import * as ep___users_achievements from './endpoints/users/achievements.js';
|
||||
import * as ep___fetchRss from "./endpoints/fetch-rss.js";
|
||||
import * as ep___admin_driveCapOverride from "./endpoints/admin/drive-capacity-override.js";
|
||||
import * as ep___bites_create from "./endpoints/bites/create.js";
|
||||
@ -525,6 +527,7 @@ const eps = [
|
||||
["i/2fa/unregister", ep___i_2fa_unregister],
|
||||
["i/apps", ep___i_apps],
|
||||
["i/authorized-apps", ep___i_authorizedApps],
|
||||
["i/claim-achievement", ep___i_claimAchievement],
|
||||
["i/change-password", ep___i_changePassword],
|
||||
["i/delete-account", ep___i_deleteAccount],
|
||||
["i/export-blocking", ep___i_exportBlocking],
|
||||
@ -681,6 +684,7 @@ const eps = [
|
||||
["users/search", ep___users_search],
|
||||
["users/show", ep___users_show],
|
||||
["users/stats", ep___users_stats],
|
||||
["users/achievements", ep___users_achievements],
|
||||
["admin/drive-capacity-override", ep___admin_driveCapOverride],
|
||||
["fetch-rss", ep___fetchRss],
|
||||
["get-sounds", ep___sounds],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Users } from "@/models/index.js";
|
||||
import { UserProfiles, Users } from "@/models/index.js";
|
||||
import define from "../define.js";
|
||||
|
||||
export const meta = {
|
||||
@ -23,9 +23,27 @@ export const paramDef = {
|
||||
export default define(meta, paramDef, async (ps, user, token) => {
|
||||
const isSecure = token == null;
|
||||
|
||||
// ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す
|
||||
return await Users.pack<true, true>(user.id, user, {
|
||||
const now = new Date();
|
||||
const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`;
|
||||
|
||||
// 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得
|
||||
const userProfile = await UserProfiles.findOneOrFail({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
relations: ['user'],
|
||||
});
|
||||
|
||||
if (!userProfile.loggedInDates.includes(today)) {
|
||||
UserProfiles.update({ userId: user.id }, {
|
||||
loggedInDates: [...userProfile.loggedInDates, today],
|
||||
});
|
||||
userProfile.loggedInDates = [...userProfile.loggedInDates, today];
|
||||
}
|
||||
|
||||
return await Users.pack<true, true>(userProfile.user!, userProfile.user!, {
|
||||
detail: true,
|
||||
includeSecrets: isSecure,
|
||||
userProfile,
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,18 @@
|
||||
import { createAchievement } from '@/services/achievement-service.js';
|
||||
import define from "../../define.js";
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
},
|
||||
required: ['name'],
|
||||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, me) => {
|
||||
await createAchievement(me.id, ps.name);
|
||||
});
|
@ -0,0 +1,22 @@
|
||||
import { UserProfiles } from '@/models/index.js';
|
||||
import define from "../../define.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["users", "achievements"],
|
||||
requireCredential: true,
|
||||
description: "Show all achievements this user made.",
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, me) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
|
||||
|
||||
return profile.achievements;
|
||||
});
|
96
packages/backend/src/services/achievement-service.ts
Normal file
96
packages/backend/src/services/achievement-service.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { UserProfiles, Users } from '@/models/index.js';
|
||||
import type { User } from '@/models/entities/user.js';
|
||||
import { createNotification } from '@/services/create-notification.js';
|
||||
|
||||
const ACHIEVEMENT_TYPES = [
|
||||
'notes1',
|
||||
'notes10',
|
||||
'notes100',
|
||||
'notes500',
|
||||
'notes1000',
|
||||
'notes5000',
|
||||
'notes10000',
|
||||
'notes20000',
|
||||
'notes30000',
|
||||
'notes40000',
|
||||
'notes50000',
|
||||
'notes60000',
|
||||
'notes70000',
|
||||
'notes80000',
|
||||
'notes90000',
|
||||
'notes100000',
|
||||
'login3',
|
||||
'login7',
|
||||
'login15',
|
||||
'login30',
|
||||
'login60',
|
||||
'login100',
|
||||
'login200',
|
||||
'login300',
|
||||
'login400',
|
||||
'login500',
|
||||
'login600',
|
||||
'login700',
|
||||
'login800',
|
||||
'login900',
|
||||
'login1000',
|
||||
'passedSinceAccountCreated1',
|
||||
'passedSinceAccountCreated2',
|
||||
'passedSinceAccountCreated3',
|
||||
'loggedInOnBirthday',
|
||||
'noteClipped1',
|
||||
'noteFavorited1',
|
||||
'profileFilled',
|
||||
'markedAsCat',
|
||||
'following1',
|
||||
'following10',
|
||||
'following50',
|
||||
'following100',
|
||||
'following300',
|
||||
'followers1',
|
||||
'followers10',
|
||||
'followers50',
|
||||
'followers100',
|
||||
'followers300',
|
||||
'followers500',
|
||||
'followers1000',
|
||||
'collectAchievements30',
|
||||
'iLoveMisskey',
|
||||
'client30min',
|
||||
'noteDeletedWithin1min',
|
||||
'postedAtLateNight',
|
||||
'postedAt0min0sec',
|
||||
'selfQuote',
|
||||
'htl20npm',
|
||||
'driveFolderCircularReference',
|
||||
'reactWithoutRead',
|
||||
'clickedClickHere',
|
||||
'justPlainLucky',
|
||||
'setNameToSyuilo',
|
||||
'cookieClicked',
|
||||
'brainDiver',
|
||||
] as const;
|
||||
|
||||
export async function createAchievement(
|
||||
userId: User['id'],
|
||||
type: string,
|
||||
) {
|
||||
if (!ACHIEVEMENT_TYPES.includes(type)) return;
|
||||
|
||||
const date = Date.now();
|
||||
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: userId });
|
||||
|
||||
if (profile.achievements.some(a => a.name === type)) return;
|
||||
|
||||
await UserProfiles.update(userId, {
|
||||
achievements: [...profile.achievements, {
|
||||
name: type,
|
||||
unlockedAt: date,
|
||||
}],
|
||||
});
|
||||
|
||||
createNotification(userId, 'achievementEarned', {
|
||||
achievement: type,
|
||||
});
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="achievements" :class="'.root'">
|
||||
<div v-for="achievement in achievements" :key="achievement" :class="'.achievement'" class="_panel">
|
||||
<div :class="'.icon'">
|
||||
<div :class="['.iconFrame', ['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]">
|
||||
<div :class="['.iconInner']" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
|
||||
<img :class="'.iconImg'" :src="ACHIEVEMENT_BADGES[achievement.name].img">
|
||||
<div v-if="achievements" class="root">
|
||||
<div v-for="achievement in achievements" :key="achievement" class="_panel achievement">
|
||||
<div class="icon">
|
||||
<div :class="[['iconFrame'], ['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]">
|
||||
<div class="iconInner" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
|
||||
<img class="iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="'.body'">
|
||||
<div :class="'.header'">
|
||||
<span :class="'.title'">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span>
|
||||
<span :class="'.time'">
|
||||
<div class="body">
|
||||
<div class="header">
|
||||
<span class="title">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span>
|
||||
<span class="time">
|
||||
<time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time>
|
||||
</span>
|
||||
</div>
|
||||
<div :class="'.description'">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div>
|
||||
<div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" :class="'.flavor'">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
|
||||
<div class="description">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div>
|
||||
<div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" class="flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="withLocked">
|
||||
<div v-for="achievement in lockedAchievements" :key="achievement" :class="['.achievement', '.locked']" class="_panel" @click="achievement === 'clickedClickHere' ? clickHere() : () => {}">
|
||||
<div :class="'.icon'">
|
||||
<div v-for="achievement in lockedAchievements" :key="achievement" class="_panel achievement locked" @click="achievement === 'clickedClickHere' ? clickHere() : () => {}">
|
||||
<div class="icon">
|
||||
</div>
|
||||
<div :class="'.body'">
|
||||
<div :class="'.header'">
|
||||
<span :class="'.title'">???</span>
|
||||
<div class="body">
|
||||
<div class="header">
|
||||
<span class="title">???</span>
|
||||
</div>
|
||||
<div :class="'.description'">???</div>
|
||||
<div class="description">???</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -77,7 +77,7 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
<style lang="scss" scoped>
|
||||
.root {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, min(380px, 100%));
|
||||
|
@ -997,7 +997,7 @@ async function post() {
|
||||
}
|
||||
|
||||
const text = postData.text?.toLowerCase() ?? '';
|
||||
if ((text.includes('love') || text.includes('❤')) && text.includes('misskey')) {
|
||||
if ((text.includes('love') || text.includes('❤')) && text.includes('trashposs')) {
|
||||
claimAchievement('iLoveMisskey');
|
||||
}
|
||||
if (text.includes('Efrlqw8ytg4'.toLowerCase()) || text.includes('XVCwzwxdHuA'.toLowerCase())) {
|
||||
|
@ -245,7 +245,7 @@ function save() {
|
||||
speakAsCat: !!profile.speakAsCat,
|
||||
});
|
||||
claimAchievement('profileFilled');
|
||||
if (profile.name === 'syuilo' || profile.name === 'しゅいろ') {
|
||||
if (profile.name === 'Crimekillz' || profile.name === 'crimekillz') {
|
||||
claimAchievement('setNameToSyuilo');
|
||||
}
|
||||
if (profile.isCat) {
|
||||
|
@ -72,332 +72,332 @@ export const ACHIEVEMENT_TYPES = [
|
||||
|
||||
export const ACHIEVEMENT_BADGES = {
|
||||
'notes1': {
|
||||
img: '/fluent-emoji/1f4dd.png',
|
||||
img: '/twemoji/1f4dd.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes10': {
|
||||
img: '/fluent-emoji/1f4d1.png',
|
||||
img: '/twemoji/1f4d1.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes100': {
|
||||
img: '/fluent-emoji/1f4d2.png',
|
||||
img: '/twemoji/1f4d2.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes500': {
|
||||
img: '/fluent-emoji/1f4da.png',
|
||||
img: '/twemoji/1f4da.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes1000': {
|
||||
img: '/fluent-emoji/1f5c3.png',
|
||||
img: '/twemoji/1f5c3.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes5000': {
|
||||
img: '/fluent-emoji/1f304.png',
|
||||
img: '/twemoji/1f304.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'notes10000': {
|
||||
img: '/fluent-emoji/1f3d9.png',
|
||||
img: '/twemoji/1f3d9.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'notes20000': {
|
||||
img: '/fluent-emoji/1f307.png',
|
||||
img: '/twemoji/1f307.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'notes30000': {
|
||||
img: '/fluent-emoji/1f306.png',
|
||||
img: '/twemoji/1f306.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'notes40000': {
|
||||
img: '/fluent-emoji/1f303.png',
|
||||
img: '/twemoji/1f303.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'notes50000': {
|
||||
img: '/fluent-emoji/1fa90.png',
|
||||
img: '/twemoji/1fa90.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'notes60000': {
|
||||
img: '/fluent-emoji/2604.png',
|
||||
img: '/twemoji/2604.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'notes70000': {
|
||||
img: '/fluent-emoji/1f30c.png',
|
||||
img: '/twemoji/1f30c.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'notes80000': {
|
||||
img: '/fluent-emoji/1f30c.png',
|
||||
img: '/twemoji/1f30c.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'notes90000': {
|
||||
img: '/fluent-emoji/1f30c.png',
|
||||
img: '/twemoji/1f30c.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'notes100000': {
|
||||
img: '/fluent-emoji/267e.png',
|
||||
img: '/twemoji/267e.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))',
|
||||
frame: 'platinum',
|
||||
},
|
||||
'login3': {
|
||||
img: '/fluent-emoji/1f331.png',
|
||||
img: '/twemoji/1f331.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'login7': {
|
||||
img: '/fluent-emoji/1f331.png',
|
||||
img: '/twemoji/1f331.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'login15': {
|
||||
img: '/fluent-emoji/1f331.png',
|
||||
img: '/twemoji/1f331.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'login30': {
|
||||
img: '/fluent-emoji/1fab4.png',
|
||||
img: '/twemoji/1fab4.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'login60': {
|
||||
img: '/fluent-emoji/1fab4.png',
|
||||
img: '/twemoji/1fab4.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'login100': {
|
||||
img: '/fluent-emoji/1fab4.png',
|
||||
img: '/twemoji/1fab4.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'login200': {
|
||||
img: '/fluent-emoji/1f333.png',
|
||||
img: '/twemoji/1f333.svg',
|
||||
bg: null,
|
||||
frame: 'silver',
|
||||
},
|
||||
'login300': {
|
||||
img: '/fluent-emoji/1f333.png',
|
||||
img: '/twemoji/1f333.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'login400': {
|
||||
img: '/fluent-emoji/1f333.png',
|
||||
img: '/twemoji/1f333.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'login500': {
|
||||
img: '/fluent-emoji/1f304.png',
|
||||
img: '/twemoji/1f304.svg',
|
||||
bg: null,
|
||||
frame: 'silver',
|
||||
},
|
||||
'login600': {
|
||||
img: '/fluent-emoji/1f304.png',
|
||||
img: '/twemoji/1f304.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'login700': {
|
||||
img: '/fluent-emoji/1f304.png',
|
||||
img: '/twemoji/1f304.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'login800': {
|
||||
img: '/fluent-emoji/1f307.png',
|
||||
img: '/twemoji/1f307.svg',
|
||||
bg: null,
|
||||
frame: 'gold',
|
||||
},
|
||||
'login900': {
|
||||
img: '/fluent-emoji/1f307.png',
|
||||
img: '/twemoji/1f307.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'login1000': {
|
||||
img: '/fluent-emoji/1f307.png',
|
||||
img: '/twemoji/1f307.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'platinum',
|
||||
},
|
||||
'noteClipped1': {
|
||||
img: '/fluent-emoji/1f587.png',
|
||||
img: '/twemoji/1f587.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'noteFavorited1': {
|
||||
img: '/fluent-emoji/1f31f.png',
|
||||
img: '/twemoji/1f31f.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'profileFilled': {
|
||||
img: '/fluent-emoji/1f44c.png',
|
||||
img: '/twemoji/1f44c.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'markedAsCat': {
|
||||
img: '/fluent-emoji/1f408.png',
|
||||
img: '/twemoji/1f408.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'following1': {
|
||||
img: '/fluent-emoji/2618.png',
|
||||
img: '/twemoji/2618.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'following10': {
|
||||
img: '/fluent-emoji/1f6b8.png',
|
||||
img: '/twemoji/1f6b8.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'following50': {
|
||||
img: '/fluent-emoji/1f91d.png',
|
||||
img: '/twemoji/1f91d.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'following100': {
|
||||
img: '/fluent-emoji/1f4af.png',
|
||||
img: '/twemoji/1f4af.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 53 184), rgb(255 206 69))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'following300': {
|
||||
img: '/fluent-emoji/1f970.png',
|
||||
img: '/twemoji/1f970.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'followers1': {
|
||||
img: '/fluent-emoji/2618.png',
|
||||
img: '/twemoji/2618.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'followers10': {
|
||||
img: '/fluent-emoji/1f44b.png',
|
||||
img: '/twemoji/1f44b.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'followers50': {
|
||||
img: '/fluent-emoji/1f411.png',
|
||||
img: '/twemoji/1f411.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'followers100': {
|
||||
img: '/fluent-emoji/1f396.png',
|
||||
img: '/twemoji/1f396.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'followers300': {
|
||||
img: '/fluent-emoji/1f3c6.png',
|
||||
img: '/twemoji/1f3c6.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'followers500': {
|
||||
img: '/fluent-emoji/1f4e1.png',
|
||||
img: '/twemoji/1f4e1.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'followers1000': {
|
||||
img: '/fluent-emoji/1f451.png',
|
||||
img: '/twemoji/1f451.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))',
|
||||
frame: 'platinum',
|
||||
},
|
||||
'collectAchievements30': {
|
||||
img: '/fluent-emoji/1f3c5.png',
|
||||
img: '/twemoji/1f3c5.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'iLoveMisskey': {
|
||||
img: '/fluent-emoji/2764.png',
|
||||
img: '/twemoji/2764.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'client30min': {
|
||||
img: '/fluent-emoji/1f552.png',
|
||||
img: '/twemoji/1f552.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'noteDeletedWithin1min': {
|
||||
img: '/fluent-emoji/1f5d1.png',
|
||||
img: '/twemoji/1f5d1.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'postedAtLateNight': {
|
||||
img: '/fluent-emoji/1f319.png',
|
||||
img: '/twemoji/1f319.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'postedAt0min0sec': {
|
||||
img: '/fluent-emoji/1f55b.png',
|
||||
img: '/twemoji/1f55b.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'selfQuote': {
|
||||
img: '/fluent-emoji/1f4dd.png',
|
||||
img: '/twemoji/1f4dd.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'htl20npm': {
|
||||
img: '/fluent-emoji/1f30a.png',
|
||||
img: '/twemoji/1f30a.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'driveFolderCircularReference': {
|
||||
img: '/fluent-emoji/1f4c2.png',
|
||||
img: '/twemoji/1f4c2.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'reactWithoutRead': {
|
||||
img: '/fluent-emoji/2753.png',
|
||||
img: '/twemoji/2753.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'clickedClickHere': {
|
||||
img: '/fluent-emoji/2757.png',
|
||||
img: '/twemoji/2757.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'justPlainLucky': {
|
||||
img: '/fluent-emoji/1f340.png',
|
||||
img: '/twemoji/1f340.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'setNameToSyuilo': {
|
||||
img: '/fluent-emoji/1f36e.png',
|
||||
img: '/twemoji/1f36e.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'passedSinceAccountCreated1': {
|
||||
img: '/fluent-emoji/0031-20e3.png',
|
||||
img: '/twemoji/0031-20e3.svg',
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'passedSinceAccountCreated2': {
|
||||
img: '/fluent-emoji/0032-20e3.png',
|
||||
img: '/twemoji/0032-20e3.svg',
|
||||
bg: null,
|
||||
frame: 'silver',
|
||||
},
|
||||
'passedSinceAccountCreated3': {
|
||||
img: '/fluent-emoji/0033-20e3.png',
|
||||
img: '/twemoji/0033-20e3.svg',
|
||||
bg: null,
|
||||
frame: 'gold',
|
||||
},
|
||||
'loggedInOnBirthday': {
|
||||
img: '/fluent-emoji/1f382.png',
|
||||
img: '/twemoji/1f382.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'cookieClicked': {
|
||||
img: '/fluent-emoji/1f36a.png',
|
||||
img: '/twemoji/1f36a.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'brainDiver': {
|
||||
img: '/fluent-emoji/1f9e0.png',
|
||||
img: '/twemoji/1f9e0.svg',
|
||||
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user