diff options
Diffstat (limited to 'packages/backend/src/server')
4 files changed, 173 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f77c50012d..1f37c74c01 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -377,6 +377,8 @@ import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___fetchExternalResources from './endpoints/fetch-external-resources.js'; import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; +import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; +import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import type { Provider } from '@nestjs/common'; @@ -752,6 +754,8 @@ const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.d const $fetchExternalResources: Provider = { provide: 'ep:fetch-external-resources', useClass: ep___fetchExternalResources.default }; const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default }; const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.default }; +const $bubbleGame_register: Provider = { provide: 'ep:bubble-game/register', useClass: ep___bubbleGame_register.default }; +const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useClass: ep___bubbleGame_ranking.default }; @Module({ imports: [ @@ -1131,6 +1135,8 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $fetchExternalResources, $retention, $sponsors, + $bubbleGame_register, + $bubbleGame_ranking, ], exports: [ $admin_meta, @@ -1501,6 +1507,8 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $fetchExternalResources, $retention, $sponsors, + $bubbleGame_register, + $bubbleGame_ranking, ], }) export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index a7be81eb18..7510228bb9 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -378,6 +378,8 @@ import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___fetchExternalResources from './endpoints/fetch-external-resources.js'; import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; +import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; +import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; const eps = [ ['admin/meta', ep___admin_meta], @@ -751,6 +753,8 @@ const eps = [ ['fetch-external-resources', ep___fetchExternalResources], ['retention', ep___retention], ['sponsors', ep___sponsors], + ['bubble-game/register', ep___bubbleGame_register], + ['bubble-game/ranking', ep___bubbleGame_ranking], ]; interface IEndpointMetaBase { diff --git a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts new file mode 100644 index 0000000000..0cba129a09 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BubbleGameRecordsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; + +export const meta = { + tags: [], + + allowGet: true, + cacheSec: 60, + + errors: { + }, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { type: 'string', format: 'misskey:id' }, + score: { type: 'integer' }, + user: { ref: 'UserLite' }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameMode: { type: 'string' }, + }, + required: ['gameMode'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.bubbleGameRecordsRepository) + private bubbleGameRecordsRepository: BubbleGameRecordsRepository, + + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps) => { + const records = await this.bubbleGameRecordsRepository.find({ + where: { + gameMode: ps.gameMode, + seededAt: MoreThan(new Date(Date.now() - 1000 * 60 * 60 * 24 * 7)), + }, + order: { + score: 'DESC', + }, + take: 10, + relations: ['user'], + }); + + const users = await this.userEntityService.packMany(records.map(r => r.user!), null, { detail: false }); + + return records.map(r => ({ + id: r.id, + score: r.score, + user: users.find(u => u.id === r.user!.id), + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/bubble-game/register.ts b/packages/backend/src/server/api/endpoints/bubble-game/register.ts new file mode 100644 index 0000000000..af0f69e4ad --- /dev/null +++ b/packages/backend/src/server/api/endpoints/bubble-game/register.ts @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { IdService } from '@/core/IdService.js'; +import type { BubbleGameRecordsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: [], + + requireCredential: true, + + kind: 'write:account', + + limit: { + duration: ms('1hour'), + max: 120, + minInterval: ms('30sec'), + }, + + errors: { + invalidSeed: { + message: 'Provided seed is invalid.', + code: 'INVALID_SEED', + id: 'eb627bc7-574b-4a52-a860-3c3eae772b88', + }, + }, + + res: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + score: { type: 'integer', minimum: 0 }, + seed: { type: 'string', minLength: 1, maxLength: 1024 }, + logs: { type: 'array' }, + gameMode: { type: 'string' }, + gameVersion: { type: 'integer' }, + }, + required: ['score', 'seed', 'logs', 'gameMode', 'gameVersion'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.bubbleGameRecordsRepository) + private bubbleGameRecordsRepository: BubbleGameRecordsRepository, + + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const seedDate = new Date(parseInt(ps.seed, 10)); + const now = new Date(); + + // シードが未来なのは通常のプレイではありえないので弾く + if (seedDate.getTime() > now.getTime()) { + throw new ApiError(meta.errors.invalidSeed); + } + + // シードが古すぎる(1時間以上前)のも弾く + if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60) { + throw new ApiError(meta.errors.invalidSeed); + } + + await this.bubbleGameRecordsRepository.insert({ + id: this.idService.gen(now.getTime()), + seed: ps.seed, + seededAt: seedDate, + userId: me.id, + score: ps.score, + logs: ps.logs, + gameMode: ps.gameMode, + gameVersion: ps.gameVersion, + isVerified: false, + }); + }); + } +} |