Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

/dateコマンドの追加 (#54, #94) #95

Merged
merged 13 commits into from
Apr 28, 2024
124 changes: 124 additions & 0 deletions common/CompoundCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
CacheType,
ChatInputCommandInteraction,
Client,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
} from 'discord.js';
import {
Option,
OptionValueMap,
SimpleCommand,
SimpleSlashCommandBuilder,
} from './SimpleCommand';
import { Command } from '../util/types';

export class CompoundCommandBuilder {
readonly #handle: SlashCommandBuilder;

readonly #subcommands = new Map<
string,
SimpleCommand<Option<unknown, boolean>[]>
>();

constructor(name: string, description: string) {
this.#handle = new SlashCommandBuilder()
.setName(name)
.setDescription(description);
}

subcommand(name: string, description: string): SimpleSubcommandBuilder {
const handle = new SlashCommandSubcommandBuilder();
this.#handle.addSubcommand(handle);
return new SimpleSubcommandBuilder(
name,
description,
handle,
[],
(name, subcommand) => this.#subcommands.set(name, subcommand),
);
}

build(): CompoundCommand {
return new CompoundCommand(this.#subcommands, this.#handle);
}
}

export class SimpleSubcommandBuilder<
Options extends Option<unknown, boolean>[] = [],
> extends SimpleSlashCommandBuilder<Options> {
readonly #onBuild: (
name: string,
subcommand: SimpleCommand<Option<unknown, boolean>[]>,
) => void;

constructor(
name: string,
description: string,
handle: SlashCommandSubcommandBuilder,
options: Options,
onBuild: (
name: string,
subcommand: SimpleCommand<Option<unknown, boolean>[]>,
) => void,
) {
super(name, description, handle, options);
this.#onBuild = onBuild;
}

protected override newInstance<O extends Option<unknown, boolean>[]>(
name: string,
description: string,
handle: SlashCommandSubcommandBuilder,
options: O,
): SimpleSlashCommandBuilder<O> {
return new SimpleSubcommandBuilder(
name,
description,
handle,
options,
this.#onBuild,
);
}

override build(
action: (
interaction: ChatInputCommandInteraction<CacheType>,
...options: OptionValueMap<Options>
) => Promise<void>,
): SimpleCommand<Options> {
const subcommand = super.build(action);
this.#onBuild(this.name, subcommand as any);
return subcommand;
}
}

export class CompoundCommand implements Command {
readonly #subcommands: Map<string, SimpleCommand<Option<unknown, boolean>[]>>;

data: SlashCommandBuilder;

constructor(
subcommands: Map<string, SimpleCommand<Option<unknown, boolean>[]>>,
data: SlashCommandBuilder,
) {
this.#subcommands = subcommands;
this.data = data;
}

async execute(
interaction: ChatInputCommandInteraction<CacheType>,
client: Client<true>,
): Promise<void> {
const subcommandName = interaction.options.getSubcommand(true);
const subcommand = this.#subcommands.get(subcommandName);
if (subcommand == null) {
await interaction.reply({
ephemeral: true,
content: 'Invalid subcommand: ' + subcommandName,
});
return;
}
subcommand.execute(interaction);
}
}
4 changes: 2 additions & 2 deletions common/Feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export abstract class Feature {

private unloading = false;

enabled: boolean;
abstract enabled: boolean;

async load(client: Client<boolean>) {
if (!this.loading) {
Expand Down Expand Up @@ -38,7 +38,7 @@ export abstract class Feature {
}
}

name: string;
abstract name: string;

/**
* この機能の依存先の機能
Expand Down
32 changes: 18 additions & 14 deletions common/SimpleCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ApplicationCommandOptionWithChoicesAndAutocompleteMixin,
CacheType,
SharedSlashCommandOptions,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
} from 'discord.js';
import { ChatInputCommandInteraction } from 'discord.js';
import { Command } from '../util/types';
Expand All @@ -12,7 +12,7 @@ type Value<T, Required extends boolean = boolean> = Required extends true
? T
: T | undefined;

type OptionValueMap<O extends Option<unknown>[]> = {
export type OptionValueMap<O extends Option<unknown>[]> = {
[I in keyof O]: O[I] extends Option<infer T, infer Required>
? Value<T, Required>
: never;
Expand Down Expand Up @@ -50,7 +50,7 @@ interface SimpleStringOptionData<
min_length?: number;
}

interface Option<T = unknown, Required extends boolean = boolean> {
export interface Option<T = unknown, Required extends boolean = boolean> {
/** オプションの名前 */
name: string;

Expand Down Expand Up @@ -163,23 +163,23 @@ class StringOption<
export class SimpleSlashCommandBuilder<
Options extends Option<unknown, boolean>[] = [],
> {
#name: string;
public readonly name: string;

#description: string;

handle: SlashCommandBuilder;
handle: SlashCommandSubcommandBuilder;

options: Options;

constructor(
name: string,
description: string,
handle: SlashCommandBuilder,
handle: SlashCommandSubcommandBuilder,
options: Options,
) {
handle.setName(name);
handle.setDescription(description);
this.#name = name;
this.name = name;
this.#description = description;
this.handle = handle;
this.options = options;
Expand All @@ -196,23 +196,27 @@ export class SimpleSlashCommandBuilder<
return new SimpleSlashCommandBuilder(
name,
description,
new SlashCommandBuilder(),
new SlashCommandSubcommandBuilder(),
[],
);
}

protected newInstance<O extends Option<unknown, boolean>[]>(
name: string,
description: string,
handle: SlashCommandSubcommandBuilder,
options: O,
): SimpleSlashCommandBuilder<O> {
return new SimpleSlashCommandBuilder(name, description, handle, options);
}

addOption<T, Required extends boolean = false>(option: Option<T, Required>) {
/** @type {[...Options, Option<T, Required>]} */
const options: [...Options, Option<T, Required>] = [
...this.options,
option,
];
return new SimpleSlashCommandBuilder(
this.#name,
this.#description,
this.handle,
options,
);
return this.newInstance(this.name, this.#description, this.handle, options);
}

addIntegerOption<T extends number, Required extends boolean = boolean>(
Expand Down
8 changes: 4 additions & 4 deletions discordbot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ client.on('ready', async (readyClient) => {
strFormat(LANG.discordbot.ready.loggedIn, {
cgreen,
creset,
tag: client.user.tag,
tag: readyClient.user.tag,
}),
);
client.user.setPresence({
readyClient.user.setPresence({
activities: [
{
name: LANG.discordbot.ready.presenceNameLoading,
Expand All @@ -107,13 +107,13 @@ client.on('ready', async (readyClient) => {
}),
);
const SyslogChannel = client.channels.cache.get(syslogChannel);
assert(SyslogChannel.isTextBased());
assert(SyslogChannel?.isTextBased());
SyslogChannel.send(LANG.discordbot.ready.sysLog);
});

onShutdown(async () => {
const SyslogChannel = client.channels.cache.get(syslogChannel);
assert(SyslogChannel.isTextBased());
assert(SyslogChannel?.isTextBased());
await SyslogChannel.send(LANG.discordbot.shutdown.sysLog);
const features = await featuresLoadPromise;
await Promise.all(features.map((feature) => feature.unload()));
Expand Down
14 changes: 6 additions & 8 deletions internal/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,14 @@ export function teeWrite(
const withBufferEncoding = typeof option1 == 'string';
const cb = withBufferEncoding ? option2 : option1;
const fileWritePromise = fs.appendFile(logFilename, data ?? '');
async function wrappedCallback(err: Error) {
function wrappedCallback(err: Error | undefined) {
if (err) {
return cb(err);
return cb?.(err);
} else {
try {
await fileWritePromise;
} catch (e) {
cb(err);
}
cb();
fileWritePromise.then(
() => cb?.(),
(e) => cb?.(err),
);
}
}
if (withBufferEncoding) {
Expand Down
80 changes: 55 additions & 25 deletions language/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,40 @@
},
"defaultValues": {
"graphLabel": "value"
}
},
"dayNames": [
"日曜日",
"月曜日",
"火曜日",
"水曜日",
"木曜日",
"金曜日",
"土曜日"
],
"dayLabels": ["日", "月", "火", "水", "木", "金", "土"],
"monthNames": [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月"
],
"dateFormat": "${year}年${month}${date}日${day}${holiday}",
"holiday": {
"yes": " (${0})",
"no": ""
},
"timeFormat": "${hour}:${minute}:${second}",
"dateTimeFormat": "${date} ${time}",
"yearsDaysAgo": "${years}年と${days}日前",
"yearsDaysLater": "${years}年と${days}日後"
},
"commands": {
"cal": {
Expand All @@ -134,30 +167,6 @@
"description": "週の始まりの曜日"
}
},
"dayNames": [
"日曜日",
"月曜日",
"火曜日",
"水曜日",
"木曜日",
"金曜日",
"土曜日"
],
"dayLabels": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
"monthNames": [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月"
],
"monthYear": "${year}年${month}"
},
"check": {
Expand Down Expand Up @@ -192,6 +201,27 @@
},
"invalidUrlError": "URLが間違っています"
},
"date": {
"name": "date",
"description": "日付の計算と表示",
"subcommands": {
"now": {
"name": "now",
"description": "現在時刻を表示"
},
"diff": {
"name": "diff",
"description": "今日との日付の差を計算",
"options": {
"date": {
"name": "date",
"description": "yyyy/MM/dd形式の日付"
}
},
"invalidDate": "日付の形式が無効です"
}
}
},
"discordinfo": {
"name": "info",
"description": "Lookup Server/User Info",
Expand Down
Loading