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: はらちょ粛清機能の追加 #158

Merged
merged 14 commits into from
May 6, 2022
25 changes: 25 additions & 0 deletions src/adaptor/discord-sheriff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Client } from 'discord.js';
import type { Sheriff } from '../service/s**t-the-f**k-up';
import type { Snowflake } from '../model/id';

export class DiscordSheriff implements Sheriff {
constructor(private readonly client: Client) {}

async executeMessage(
channelId: Snowflake,
historyRange: number
): Promise<void> {
const harachoId = this.client.user?.id;
if (!harachoId) throw new Error('haracho is not found');

const channel = await this.client.channels.fetch(channelId);
if (!channel) throw new Error(`channel: (${channelId}) not found`);
if (!channel.isText()) throw new Error('this channel is not text channel.');

const messages = await channel.messages.fetch({ limit: historyRange });

const targetMessage = messages.get(harachoId);
if (!targetMessage) return;
await targetMessage.delete();
}
}
1 change: 1 addition & 0 deletions src/adaptor/transformer/message-convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const converterWithPrefix =
const command: CommandMessage = {
senderId: getAuthorSnowflake(message),
senderGuildId: message.guildId as Snowflake,
senderChannelId: message.channelId as Snowflake,
get senderVoiceChannelId(): Snowflake | null {
const id = message.member?.voice.channelId ?? null;
return id ? (id as Snowflake) : null;
Expand Down
4 changes: 3 additions & 1 deletion src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
import type { AssetKey } from '../service/party';
import { DiscordMemberStats } from '../adaptor/discord-member-stats';
import { DiscordRoleManager } from '../adaptor/discord-role';
import { DiscordSheriff } from '../adaptor/discord-sheriff';
import type { KaereMusicKey } from '../service/kaere';
import { Snowflake } from '../model/id';
import dotenv from 'dotenv';
Expand Down Expand Up @@ -115,7 +116,8 @@ registerAllCommandResponder(
new MathRandomGenerator(),
new DiscordVoiceRoomController(client),
commandRunner,
new DiscordMemberStats(client, GUILD_ID as Snowflake)
new DiscordMemberStats(client, GUILD_ID as Snowflake),
new DiscordSheriff(client)
);

const provider = new VoiceRoomProxy<VoiceChannelParticipant>(
Expand Down
9 changes: 9 additions & 0 deletions src/service/command-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export interface CommandMessage {
*/
senderGuildId: Snowflake;

/**
* コマンドの送信者が発信したチャンネルのID。
*
* @type {Snowflake}
* @memberOf CommandMessage
*/
senderChannelId: Snowflake;

/**
* コマンドの送信者が接続しているボイスチャンネルの ID。
*
Expand Down Expand Up @@ -87,6 +95,7 @@ export const createMockMessage = (
): CommandMessage => ({
senderId: '279614913129742338' as Snowflake,
senderGuildId: '683939861539192860' as Snowflake,
senderChannelId: '711127633810817026' as Snowflake,
senderVoiceChannelId: '683939861539192865' as Snowflake,
senderName: 'Mikuroさいな',
args: [],
Expand Down
7 changes: 5 additions & 2 deletions src/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from './kaere';
import { KawaemonHasAllRoles, RoleManager } from './kawaemon-has-all-roles';
import { KokuseiChousa, MemberStats } from './kokusei-chousa';
import { Sheriff, SheriffCommand } from './s**t-the-f**k-up';
import {
type TypoObservable,
TypoRecorder,
Expand Down Expand Up @@ -57,7 +58,8 @@ export const registerAllCommandResponder = (
random: PartyRng & RandomGenerator,
roomController: VoiceRoomController,
commandRunner: MessageResponseRunner<CommandMessage, CommandResponder>,
stats: MemberStats
stats: MemberStats,
sheriff: Sheriff
) => {
const allResponders = [
new TypoReporter(typoRepo, clock, scheduleRunner),
Expand All @@ -72,7 +74,8 @@ export const registerAllCommandResponder = (
new JudgementCommand(random),
new Hukueki(),
new HelpCommand(commandRunner),
new KokuseiChousa(stats)
new KokuseiChousa(stats),
new SheriffCommand(sheriff)
];
for (const responder of allResponders) {
commandRunner.addResponder(responder);
Expand Down
61 changes: 61 additions & 0 deletions src/service/s**t-the-f**k-up.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { type Sheriff, SheriffCommand } from './s**t-the-f**k-up';
import { type Snowflake } from '../model/id';
import { createMockMessage } from './command-message';

test('use case of stfu', async () => {
const executeMessage = jest.fn<Promise<void>, [Snowflake, number]>(() =>
Promise.resolve()
);
const sheriff: Sheriff = { executeMessage };
const responder = new SheriffCommand(sheriff);
const fn = jest.fn();
await responder.on(
'CREATE',
createMockMessage({
args: ['sftu'],
reply: fn
})
);

expect(fn).not.toHaveBeenCalled();
expect(executeMessage).not.toHaveBeenCalledWith(
MikuroXina marked this conversation as resolved.
Show resolved Hide resolved
'711127633810817026' as Snowflake,
50
);
});

test('delete message', async () => {
const executeMessage = jest.fn<Promise<void>, [Snowflake, number]>(() =>
Promise.resolve()
);
const sheriff: Sheriff = { executeMessage };
const responder = new SheriffCommand(sheriff);
const fn = jest.fn();
await responder.on(
'DELETE',
createMockMessage({
args: ['sftu'],
reply: fn
})
);

expect(fn).not.toHaveBeenCalled();
});

test('other command', async () => {
const executeMessage = jest.fn<Promise<void>, [Snowflake, number]>(() =>
Promise.resolve()
);
const sheriff: Sheriff = { executeMessage };
const responder = new SheriffCommand(sheriff);
const fn = jest.fn();
await responder.on(
'CREATE',
createMockMessage({
args: ['sft'],
reply: fn
})
);

expect(fn).not.toHaveBeenCalled();
});
54 changes: 54 additions & 0 deletions src/service/s**t-the-f**k-up.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type {
CommandMessage,
CommandResponder,
HelpInfo
} from './command-message';
import type { MessageEvent } from '../runner';
import type { Snowflake } from '../model/id';

/**
* 'SheriffCommandのための削除機能。
*
* @export
* @interface Sheriff
*/
export interface Sheriff {
/**
* 'channel' 内の 'historyRange' 件のメッセージ中の自身のメッセージを削除する。
*
* @param {Snowflake} channel
* @param {number} historyRange
*/
executeMessage(channel: Snowflake, historyRange: number): Promise<void>;
}
/**
* 'sftu' コマンドではらちょの直近のメッセージを削除する・
*
* @export
* @class Sheriff
*
*/
export class SheriffCommand implements CommandResponder {
help: Readonly<HelpInfo> = {
title: '治安統率機構',
description: 'はらちょの治安維持コマンドだよ',
commandName: ['stfu'],
argsFormat: []
};

constructor(private readonly sheriff: Sheriff) {}

async on(event: MessageEvent, message: CommandMessage): Promise<void> {
if (event !== 'CREATE') {
return;
}

const [commandName] = message.args;
if (!this.help.commandName.includes(commandName)) {
return;
}

const channel = message.senderChannelId;
await this.sheriff.executeMessage(channel, 50);
}
}