mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-22 01:47:39 -07:00
Achievements 1.5/2
Signed-off-by: limepotato <limepot@protonmail.ch>
This commit is contained in:
parent
ba834bf349
commit
00e517ab6c
14 changed files with 266 additions and 78 deletions
|
@ -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,
|
||||
});
|
||||
}
|
|
@ -912,7 +912,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 a new issue