import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.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', 'loggedInOnNewYearsDay', 'noteClipped1', 'noteFavorited1', 'myNoteFavorited1', 'profileFilled', 'markedAsCat', 'following1', 'following10', 'following50', 'following100', 'following300', 'followers1', 'followers10', 'followers50', 'followers100', 'followers300', 'followers500', 'followers1000', 'collectAchievements30', 'viewAchievements3min', 'iLoveMisskey', 'foundTreasure', 'client30min', 'noteDeletedWithin1min', 'postedAtLateNight', 'postedAt0min0sec', 'selfQuote', 'htl20npm', 'viewInstanceChart', 'outputHelloWorldOnScratchpad', 'open3windows', 'driveFolderCircularReference', 'reactWithoutRead', 'clickedClickHere', 'justPlainLucky', 'setNameToSyuilo', 'cookieClicked', 'brainDiver', ] as const; @Injectable() export class AchievementService { constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, private createNotificationService: CreateNotificationService, ) { } @bindThis public async create( userId: User['id'], type: typeof ACHIEVEMENT_TYPES[number], ): Promise { if (!ACHIEVEMENT_TYPES.includes(type)) return; const date = Date.now(); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: userId }); if (profile.achievements.some(a => a.name === type)) return; await this.userProfilesRepository.update(userId, { achievements: [...profile.achievements, { name: type, unlockedAt: date, }], }); this.createNotificationService.createNotification(userId, 'achievementEarned', { achievement: type, }); } }