Skip to content

Commit

Permalink
Achievements (#9665)
Browse files Browse the repository at this point in the history
* wip

* Update ja-JP.yml

* wip

* wip

* Update MkAchievements.vue

* wip

* 🎨

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
  • Loading branch information
syuilo committed Jan 21, 2023
1 parent b8afabd commit 65cd605
Show file tree
Hide file tree
Showing 34 changed files with 1,385 additions and 18 deletions.
217 changes: 217 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,222 @@ cannotPerformTemporary: "一時的に利用できません"
cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。"
preset: "プリセット"
selectFromPresets: "プリセットから選択"
achievements: "実績"

_achievements:
earnedAt: "獲得日時"
_types:
_notes1:
title: "just setting up my msky"
description: "初めてノートを投稿した"
flavor: "良いMisskeyライフを!"
_notes10:
title: "いくつかのノート"
description: "ノートを10回投稿した"
_notes100:
title: "たくさんのノート"
description: "ノートを100回投稿した"
_notes500:
title: "ノートまみれ"
description: "ノートを500回投稿した"
_notes1000:
title: "ノートの山"
description: "ノートを1,000回投稿した"
_notes5000:
title: "湧き出るノート"
description: "ノートを5,000回投稿した"
_notes10000:
title: "スーパーノート"
description: "ノートを10,000回投稿した"
_notes20000:
title: "ニードモアノート"
description: "ノートを20,000回投稿した"
_notes30000:
title: "ノートノートノート"
description: "ノートを30,000回投稿した"
_notes40000:
title: "ノート工場"
description: "ノートを40,000回投稿した"
_notes50000:
title: "ノートの惑星"
description: "ノートを50,000回投稿した"
_notes60000:
title: "ノートクエーサー"
description: "ノートを60,000回投稿した"
_notes70000:
title: "ブラックノートホール"
description: "ノートを70,000回投稿した"
_notes80000:
title: "ノートギャラクシー"
description: "ノートを80,000回投稿した"
_notes90000:
title: "ノートバース"
description: "ノートを90,000回投稿した"
_notes100000:
title: "ALL YOUR NOTE ARE BELONG TO US"
description: "ノートを100,000回投稿した"
flavor: "そんなに書くことある?"
_login3:
title: "ビギナーⅠ"
description: "通算ログイン日数が3日"
flavor: "今日からね僕は ミスキストってことで"
_login7:
title: "ビギナーⅡ"
description: "通算ログイン日数が7日"
flavor: "慣れてきましたか?"
_login15:
title: "ビギナーⅢ"
description: "通算ログイン日数が15日"
_login30:
title: "ミスキストⅠ"
description: "通算ログイン日数が30日"
_login60:
title: "ミスキストⅡ"
description: "通算ログイン日数が60日"
_login100:
title: "ミスキストⅢ"
description: "通算ログイン日数が100日"
flavor: "そのユーザー、ミスキストにつき"
_login200:
title: "常連Ⅰ"
description: "通算ログイン日数が200日"
_login300:
title: "常連Ⅱ"
description: "通算ログイン日数が300日"
_login400:
title: "常連Ⅲ"
description: "通算ログイン日数が400日"
_login500:
title: "ベテランⅠ"
description: "通算ログイン日数が500日"
flavor: "諸君、私はノートが好きだ"
_login600:
title: "ベテランⅡ"
description: "通算ログイン日数が600日"
_login700:
title: "ベテランⅢ"
description: "通算ログイン日数が700日"
_login800:
title: "ノートマスターⅠ"
description: "通算ログイン日数が800日"
_login900:
title: "ノートマスターⅡ"
description: "通算ログイン日数が900日"
_login1000:
title: "ノートマスターⅢ"
description: "通算ログイン日数が1,000日"
flavor: "Misskeyを使ってくれてありがとう!"
_noteClipped1:
title: "クリップせずにはいられないな"
description: "初めてノートをクリップした"
_noteFavorited1:
title: "星をみるひと"
description: "初めてノートをお気に入りに登録した"
_profileFilled:
title: "準備万端"
description: "プロフィール設定を行った"
_markedAsCat:
title: "吾輩は猫である"
description: "アカウントをCatとして設定した"
flavor: "名前はまだない。"
_following1:
title: "はじめてのフォロー"
description: "初めてフォローした"
_following10:
title: "ついてく、ついてく"
description: "フォローが10人を超した"
_following50:
title: "友達たくさん"
description: "フォローが50人を超した"
_following100:
title: "友達100人"
description: "フォローが100人を超した"
_following300:
title: "友達過多"
description: "フォローが300人を超した"
_followers1:
title: "はじめてのフォロワー"
description: "初めてフォローされた"
_followers10:
title: "フォローミー!"
description: "フォロワーが10人を超した"
_followers50:
title: "ぞろぞろ"
description: "フォロワーが50人を超した"
_followers100:
title: "人気者"
description: "フォロワーが100人を超した"
_followers300:
title: "一列でお並びください"
description: "フォロワーが300人を超した"
_followers500:
title: "基地局"
description: "フォロワーが500人を超した"
_followers1000:
title: "インフルエンサー"
description: "フォロワーが1,000人を超した"
_collectAchievements30:
title: "実績コレクター"
description: "実績を30個以上獲得した"
_iLoveMisskey:
title: "I Love Misskey"
description: "\"I ❤ #Misskey\"を投稿した"
flavor: "Misskeyを使ってくださりありがとうございます! by 開発チーム"
_client30min:
title: "ひとやすみ"
description: "クライアントを起動してから30分以上経過した"
_noteDeletedWithin1min:
title: "いまのなし"
description: "投稿してから1分以内にその投稿を削除した"
_postedAtLateNight:
title: "夜行性"
description: "深夜にノートを投稿した"
flavor: "そろそろ寝よう。"
_postedAt0min0sec:
title: "時報"
description: "0分0秒にノートを投稿した"
flavor: "ポッ ポッ ポッ ピーン"
_selfQuote:
title: "自己言及"
description: "自分のノートを引用した"
_htl20npm:
title: "流れるTL"
description: "ホームタイムラインの流速が20npmを越す"
_driveFolderCircularReference:
title: "循環参照"
description: "ドライブのフォルダを再帰的な入れ子にしようとした"
_reactWithoutRead:
title: "ちゃんと読んだ?"
description: "100文字以上のテキストを含むノートに投稿されてから3秒以内にリアクションした"
_clickedClickHere:
title: "ここをクリック"
description: "ここをクリックした"
_justPlainLucky:
title: "単なるラッキー"
description: "10秒ごとに0.01%の確率で獲得"
_setNameToSyuilo:
title: "神様コンプレックス"
description: "名前を syuilo に設定した"
_passedSinceAccountCreated1:
title: "一周年"
description: "アカウント作成から1年経過した"
_passedSinceAccountCreated2:
title: "二周年"
description: "アカウント作成から2年経過した"
_passedSinceAccountCreated3:
title: "三周年"
description: "アカウント作成から3年経過した"
_loggedInOnBirthday:
title: "ハッピーバースデー"
description: "誕生日にログインした"
_cookieClicked:
title: "クッキーをクリックするゲーム"
description: "クッキーをクリックした"
flavor: "ソフト間違ってない?"
_brainDiver:
title: "Brain Diver"
description: "Brain Diverへのリンクを投稿した"
flavor: "Misskey-Misskey La-Tu-Ma"

_role:
new: "ロールの作成"
Expand Down Expand Up @@ -1635,6 +1851,7 @@ _notification:
pollEnded: "アンケートの結果が出ました"
unreadAntennaNote: "アンテナ {name}"
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
achievementEarned: "実績を獲得"

_types:
all: "すべて"
Expand Down
33 changes: 33 additions & 0 deletions packages/backend/migration/1674118260469-achievement.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/backend/migration/1674255666603-loggedInDates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export class loggedInDates1674255666603 {
name = 'loggedInDates1674255666603'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "loggedInDates" character varying(32) array NOT NULL DEFAULT '{}'`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "loggedInDates"`);
}
}
114 changes: 114 additions & 0 deletions packages/backend/src/core/AchievementService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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',
'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;

@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: string,
): Promise<void> {
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,
});
}
}
Loading

0 comments on commit 65cd605

Please sign in to comment.