Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: こるくの玉音放送機能の追加 #658

Merged
merged 11 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/gyokuon/gyokuon.m4a
Binary file not shown.
22 changes: 12 additions & 10 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { DiscordRoleManager } from '../adaptor/discord/role.js';
import { DiscordSheriff } from '../adaptor/discord/sheriff.js';
import { DiscordWS } from '../adaptor/discord/ws.js';
import { GenVersionFetcher } from '../adaptor/version/fetch.js';
import type { GyokuonAssetKey } from '../service/command/gyokuon.js';
import type { KaereMusicKey } from '../service/command/kaere.js';
import type { Snowflake } from '../model/id.js';
import dotenv from 'dotenv';
Expand Down Expand Up @@ -137,16 +138,17 @@ if (features.includes('COMMAND')) {
registerAllCommandResponder({
typoRepo,
reservationRepo,
factory: new DiscordVoiceConnectionFactory<AssetKey | KaereMusicKey>(
client,
{
COFFIN_INTRO: join('assets', 'party', 'coffin-intro.mp3'),
COFFIN_DROP: join('assets', 'party', 'coffin-drop.mp3'),
KAKAPO: join('assets', 'party', 'kakapo.mp3'),
KAKUSIN_DAISUKE: join('assets', 'party', 'kakusin-daisuke.mp3'),
NEROYO: join('assets', 'kaere', 'neroyo.mp3')
}
),
factory: new DiscordVoiceConnectionFactory<
AssetKey | KaereMusicKey | GyokuonAssetKey
>(client, {
COFFIN_INTRO: join('assets', 'party', 'coffin-intro.mp3'),
COFFIN_DROP: join('assets', 'party', 'coffin-drop.mp3'),
KAKAPO: join('assets', 'party', 'kakapo.mp3'),
KAKUSIN_DAISUKE: join('assets', 'party', 'kakusin-daisuke.mp3'),
NEROYO: join('assets', 'kaere', 'neroyo.mp3'),
// FIXME: 元々映像ファイルだったものをMacで変換しただけなので、他のアセットと合わせるためにmp3に変換すべきかもしれない
GYOKUON: join('assets', 'gyokuon', 'gyokuon.m4a')
}),
clock,
scheduleRunner,
random: new MathRandomGenerator(),
Expand Down
7 changes: 6 additions & 1 deletion src/service/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DebugCommand, MessageRepository } from './command/debug.js';
import { DiceCommand, DiceQueen } from './command/dice.js';
import { GetVersionCommand, VersionFetcher } from './command/version.js';
import { GuildInfo, GuildStatsRepository } from './command/guild-info.js';
import { GyokuonAssetKey, GyokuonCommand } from './command/gyokuon.js';
import { JudgingCommand, RandomGenerator } from './command/judging.js';
import {
KaereCommand,
Expand Down Expand Up @@ -54,7 +55,7 @@ export const registerAllCommandResponder = ({
}: {
typoRepo: TypoRepository;
reservationRepo: ReservationRepository;
factory: VoiceConnectionFactory<AssetKey | KaereMusicKey>;
factory: VoiceConnectionFactory<AssetKey | KaereMusicKey | GyokuonAssetKey>;
clock: Clock;
scheduleRunner: ScheduleRunner;
random: PartyRng & RandomGenerator;
Expand Down Expand Up @@ -82,6 +83,10 @@ export const registerAllCommandResponder = ({
scheduleRunner,
repo: reservationRepo
}),
new GyokuonCommand({
connectionFactory: factory,
controller: roomController
}),
new JudgingCommand(random),
new Meme(),
new HelpCommand(commandRunner),
Expand Down
21 changes: 21 additions & 0 deletions src/service/command/gyokuon.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GyokuonAssetKey, GyokuonCommand } from './gyokuon.js';
import { it, vi } from 'vitest';

import { MockVoiceConnectionFactory } from '../../adaptor/index.js';
import { createMockMessage } from './command-message.js';
import { parseStringsOrThrow } from '../../adaptor/proxy/command/schema.js';

it('use case of gyokuon', async () => {
const fn = vi.fn();
const connectionFactory = new MockVoiceConnectionFactory<GyokuonAssetKey>();
const responder = new GyokuonCommand({
connectionFactory,
controller: {
disconnectAllUsersIn: fn
}
});

await responder.on(
createMockMessage(parseStringsOrThrow(['gyokuon'], responder.schema))
);
});
76 changes: 76 additions & 0 deletions src/service/command/gyokuon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type {
CommandMessage,
CommandResponder,
HelpInfo
} from './command-message.js';
import type { Snowflake } from '../../model/id.js';
import type { VoiceConnectionFactory } from '../voice-connection.js';
import type { VoiceRoomController } from './kaere.js';

export type GyokuonAssetKey = 'GYOKUON';

const SCHEMA = {
names: ['gyokuon'],
subCommands: {}
};
m1sk9 marked this conversation as resolved.
Show resolved Hide resolved

/**
* gyokuon コマンドでこるくの玉音放送をボイスチャンネルに再生する機能
*
* @export
*/
export class GyokuonCommand implements CommandResponder<typeof SCHEMA> {
help: Readonly<HelpInfo> = {
title: 'こるくの玉音放送',
description:
'VC内にこるくの玉音放送を再生するよ。引数無しで即起動。どの方式でもコマンド発行者がVCに居ないと動かないよ。'
};
readonly schema = SCHEMA;

constructor(
private readonly deps: {
connectionFactory: VoiceConnectionFactory<GyokuonAssetKey>;
controller: VoiceRoomController;
}
) {}

async on(message: CommandMessage<typeof SCHEMA>): Promise<void> {
const roomId = message.senderVoiceChannelId;
if (!roomId) {
await message.reply({
title: 'Gyokuon安全装置が作動したよ。',
description:
'起動した本人がボイスチャンネルに居ないのでキャンセルしておいた。悪く思わないでね。'
});
return;
}

await this.start(message.senderGuildId, roomId);
return;
}

// 玉音放送がすでに行われているか
private doingGyokuon = false;
private async start(guildId: Snowflake, roomId: Snowflake): Promise<void> {
if (this.doingGyokuon) {
return;
}

this.doingGyokuon = true;
const connectionVC = await this.deps.connectionFactory.connectTo(
guildId,
roomId
);

connectionVC.connect();
connectionVC.onDisconnected(() => {
this.doingGyokuon = false;
return false;
});

await connectionVC.playToEnd('GYOKUON');

connectionVC.destroy();
this.doingGyokuon = false;
}
}