From 96db6f77ad4d0435290be0d81a52a6497ad98811 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:17:13 +0900 Subject: [PATCH 01/31] =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E5=90=8D=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/{SimpleCommand.js => SimpleCommand.ts} | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) rename common/{SimpleCommand.js => SimpleCommand.ts} (92%) diff --git a/common/SimpleCommand.js b/common/SimpleCommand.ts similarity index 92% rename from common/SimpleCommand.js rename to common/SimpleCommand.ts index da7473dc..2bd4266d 100644 --- a/common/SimpleCommand.js +++ b/common/SimpleCommand.ts @@ -153,12 +153,14 @@ class IntegerOption extends Option { */ get(interaction) { return this.required - ? /** @type {Value} */ ( - interaction.options.getInteger(this.name, true) + ? /** @type {Value} */ interaction.options.getInteger( + this.name, + true, ) - : /** @type {Value} */ ( - interaction.options.getInteger(this.name, false) ?? void 0 - ); + : /** @type {Value} */ interaction.options.getInteger( + this.name, + false, + ) ?? void 0; } } @@ -197,11 +199,12 @@ class StringOption extends Option { */ get(interaction) { return this.required - ? /** @type {Value} */ ( - interaction.options.getString(this.name, true) + ? /** @type {Value} */ interaction.options.getString( + this.name, + true, ) - : /** @type {Value} */ ( - interaction.options.getString(this.name) + : /** @type {Value} */ interaction.options.getString( + this.name, ); } } @@ -306,6 +309,8 @@ class SimpleCommand { builder; + data; + /** * * @param {SimpleSlashCommandBuilder} builder @@ -324,9 +329,10 @@ class SimpleCommand { * @param {ChatInputCommandInteraction} interaction コマンドのインタラクション */ async execute(interaction) { - const optionValues = /** @type {OptionValueMap} */ ( - this.builder.options.map((option) => option.get(interaction)) - ); + const optionValues = + /** @type {OptionValueMap} */ this.builder.options.map( + (option) => option.get(interaction), + ); await this.action(interaction, ...optionValues); } } From fc3cad51899b3c23868b3ae175862bcbb1ed0fdc Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:42:20 +0900 Subject: [PATCH 02/31] =?UTF-8?q?JSDoc=E3=82=92TS=E3=81=AE=E5=9E=8B?= =?UTF-8?q?=E5=AE=9A=E7=BE=A9=E3=81=AB=E5=A4=89=E6=8F=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/SimpleCommand.ts | 378 ++++++++++++++++------------------------ 1 file changed, 154 insertions(+), 224 deletions(-) diff --git a/common/SimpleCommand.ts b/common/SimpleCommand.ts index 2bd4266d..bd7b1823 100644 --- a/common/SimpleCommand.ts +++ b/common/SimpleCommand.ts @@ -1,113 +1,73 @@ -// @ts-check - -const { SlashCommandBuilder } = require('discord.js'); - -/** - * @typedef {import('../util/types').Command} Command - */ - -/** - * @typedef {import('discord.js').ChatInputCommandInteraction} ChatInputCommandInteraction - */ - -/** - * @template {Function} F - * @typedef {F extends (arg: infer T) => unknown ? T : never} FirstParameter - */ - -/** - * @template {unknown} T - * @template {boolean} [Required = boolean] - * @typedef {Required extends true ? T : T | undefined} Value - */ - -/** - * @template {Option[]} O - * @typedef {({ - * [I in keyof O]: O[I] extends Option ? Value : never - * })} OptionValueMap - */ - -/** - * @template {unknown} T - * @template {boolean} [Required = boolean] - * @typedef {Object} SimpleCommandOptionData - * @property {string} name - * @property {string} description - * @property {Required} required - */ - -/** - * @template {unknown} T - * @typedef {Object} SimpleChoiceOptionData - * @property {import('discord.js').APIApplicationCommandOptionChoice[]=} choices - * @property {boolean=} autocomplete - */ +import { + APIApplicationCommandOptionChoice, + ApplicationCommandOptionWithChoicesAndAutocompleteMixin, + CacheType, + SharedSlashCommandOptions, + SlashCommandBuilder, +} from 'discord.js'; +import { ChatInputCommandInteraction } from 'discord.js'; +import { Command } from '../util/types'; + +type Value = Required extends true + ? T + : T | undefined; + +type OptionValueMap[]> = { + [I in keyof O]: O[I] extends Option + ? Value + : never; +}; -/** - * @typedef {Object} SimpleRangeOptionData - * @property {number=} max_value - * @property {number=} min_value - */ +interface SimpleCommandOptionData { + name: string; + description: string; + required: Required; +} -/** - * @template {number} [T = number] - * @template {boolean} [Required = boolean] - * @typedef {( - * SimpleCommandOptionData & - * SimpleRangeOptionData & - * SimpleChoiceOptionData - * )} SimpleIntegerOptionData - */ +interface SimpleChoiceOptionData { + choices?: APIApplicationCommandOptionChoice[]; + autocomplete?: boolean; +} -/** - * @template {string} [T = string] - * @template {boolean} [Required = boolean] - * @typedef {( - * SimpleCommandOptionData & - * SimpleChoiceOptionData & - * { - * max_length?: number; - * min_length?: number; - * } - * )} SimpleStringOptionData - */ +interface SimpleRangeOptionData { + max_value?: number; + min_value?: number; +} -/** - * @template {unknown} [T = unknown] - * @template {boolean} [Required = boolean] - */ -class Option { - name; +interface SimpleIntegerOptionData< + T extends number = number, + Required extends boolean = boolean, +> extends SimpleCommandOptionData, + SimpleRangeOptionData, + SimpleChoiceOptionData {} + +interface SimpleStringOptionData< + T extends string = string, + Required extends boolean = boolean, +> extends SimpleCommandOptionData, + SimpleChoiceOptionData { + max_length?: number; + min_length?: number; +} - required; +interface Option { + /** オプションの名前 */ + name: string; - /** - * @param {string} name オプションの名前 - * @param {Required} required 必須のオプションか - */ - constructor(name, required) { - this.name = name; - this.required = required; - } + /** 必須のオプションか */ + required: Required; /** * オプションの値を取得する。 - * @abstract - * @param {ChatInputCommandInteraction} _interaction コマンドのインタラクション - * @returns {Value} + * @param interaction コマンドのインタラクション */ - get(_interaction) { - throw new Error('Not implemented'); - } + get(interaction: ChatInputCommandInteraction): Value; } -/** - * @template {string | number} T - * @param {import('discord.js').ApplicationCommandOptionWithChoicesAndAutocompleteMixin} option - * @param {SimpleChoiceOptionData} input - */ -function setChoices(option, input) { +function setChoices( + option: ApplicationCommandOptionWithChoicesAndAutocompleteMixin, + input: SimpleChoiceOptionData, +) { const { choices, autocomplete } = input; if (choices != null) { option.addChoices(...choices); @@ -117,19 +77,20 @@ function setChoices(option, input) { } } -/** - * @template {number} T - * @template {boolean} [Required = boolean] - * @extends {Option} - */ -class IntegerOption extends Option { - /** - * @param {import('discord.js').SharedSlashCommandOptions} builder - * @param {SimpleIntegerOptionData} input - */ - constructor(builder, input) { +class IntegerOption + implements Option +{ + name: string; + + required: Required; + + constructor( + builder: SharedSlashCommandOptions, + input: SimpleIntegerOptionData, + ) { const { name, required } = input; - super(name, required); + this.name = name; + this.required = required; builder.addIntegerOption((option) => { option .setName(name) @@ -147,35 +108,31 @@ class IntegerOption extends Option { }); } - /** - * @override - * @param {ChatInputCommandInteraction} interaction - */ - get(interaction) { + get(interaction: ChatInputCommandInteraction) { return this.required - ? /** @type {Value} */ interaction.options.getInteger( - this.name, - true, - ) - : /** @type {Value} */ interaction.options.getInteger( - this.name, - false, - ) ?? void 0; + ? (interaction.options.getInteger(this.name, true) as Value) + : ((interaction.options.getInteger(this.name, false) ?? void 0) as Value< + T, + Required + >); } } -/** - * @template {string} [T = string] - * @template {boolean} [Required = boolean] - * @extends {Option} - */ -class StringOption extends Option { - /** - * @param {import('discord.js').SharedSlashCommandOptions} builder - * @param {SimpleStringOptionData} input - */ - constructor(builder, input) { - super(input.name, input.required); +class StringOption< + T extends string = string, + Required extends boolean = boolean, +> implements Option +{ + name: string; + + required: Required; + + constructor( + builder: SharedSlashCommandOptions, + input: SimpleStringOptionData, + ) { + this.name = input.name; + this.required = input.required; builder.addStringOption((option) => { option .setName(input.name) @@ -193,45 +150,33 @@ class StringOption extends Option { }); } - /** - * @override - * @param {ChatInputCommandInteraction} interaction - */ - get(interaction) { + get(interaction: ChatInputCommandInteraction) { return this.required - ? /** @type {Value} */ interaction.options.getString( - this.name, - true, - ) - : /** @type {Value} */ interaction.options.getString( - this.name, - ); + ? (interaction.options.getString(this.name, true) as Value) + : (interaction.options.getString(this.name) as Value); } } /** * シンプルな SlashCommandBuilder(?) - * @template {Option[]} [Options = []] */ -class SimpleSlashCommandBuilder { - #name; +export class SimpleSlashCommandBuilder< + Options extends Option[] = [], +> { + #name: string; - #description; + #description: string; - handle; + handle: SlashCommandBuilder; - /** - * @type {Options} - */ - options; + options: Options; - /** - * @param {string} name - * @param {string} description - * @param {SlashCommandBuilder} handle - * @param {Options} options - */ - constructor(name, description, handle, options) { + constructor( + name: string, + description: string, + handle: SlashCommandBuilder, + options: Options, + ) { handle.setName(name); handle.setDescription(description); this.#name = name; @@ -241,11 +186,13 @@ class SimpleSlashCommandBuilder { } /** - * @param {string} name コマンドの名前 - * @param {string} description コマンドの説明文 - * @returns {SimpleSlashCommandBuilder<[]>} + * @param name コマンドの名前 + * @param description コマンドの説明文 */ - static create(name, description) { + static create( + name: string, + description: string, + ): SimpleSlashCommandBuilder<[]> { return new SimpleSlashCommandBuilder( name, description, @@ -254,14 +201,12 @@ class SimpleSlashCommandBuilder { ); } - /** - * @template {unknown} T - * @template {boolean} [Required = false] - * @param {Option} option - */ - addOption(option) { + addOption(option: Option) { /** @type {[...Options, Option]} */ - const options = [...this.options, option]; + const options: [...Options, Option] = [ + ...this.options, + option, + ]; return new SimpleSlashCommandBuilder( this.#name, this.#description, @@ -270,56 +215,47 @@ class SimpleSlashCommandBuilder { ); } - /** - * @template {number} T - * @template {boolean} [Required = boolean] - * @param {SimpleIntegerOptionData} input - */ - addIntegerOption(input) { + addIntegerOption( + input: SimpleIntegerOptionData, + ) { return this.addOption(new IntegerOption(this.handle, input)); } - /** - * @template {string} T - * @template {boolean} [Required = boolean] - * @param {SimpleStringOptionData} input - * @returns - */ - addStringOption(input) { + addStringOption( + input: SimpleStringOptionData, + ) { return this.addOption(new StringOption(this.handle, input)); } - /** - * @param {( - * interaction: ChatInputCommandInteraction, - * ...options: OptionValueMap - * ) => Promise} action - */ - build(action) { + build( + action: ( + interaction: ChatInputCommandInteraction, + ...options: OptionValueMap + ) => Promise, + ) { return new SimpleCommand(this, action); } } -/** - * @template {Option[]} [Options = []] - * @implements {Command} - */ -class SimpleCommand { - action; - - builder; - - data; - - /** - * - * @param {SimpleSlashCommandBuilder} builder - * @param {( - * interaction: ChatInputCommandInteraction, - * ...options: OptionValueMap - * ) => Promise} action - */ - constructor(builder, action) { +export class SimpleCommand[]> + implements Command +{ + action: ( + interaction: ChatInputCommandInteraction, + ...options: OptionValueMap + ) => Promise; + + builder: SimpleSlashCommandBuilder; + + data: any; + + constructor( + builder: SimpleSlashCommandBuilder, + action: ( + interaction: ChatInputCommandInteraction, + ...options: OptionValueMap + ) => Promise, + ) { this.builder = builder; this.data = builder.handle; this.action = action; @@ -328,16 +264,10 @@ class SimpleCommand { /** * @param {ChatInputCommandInteraction} interaction コマンドのインタラクション */ - async execute(interaction) { - const optionValues = - /** @type {OptionValueMap} */ this.builder.options.map( - (option) => option.get(interaction), - ); + async execute(interaction: ChatInputCommandInteraction) { + const optionValues = this.builder.options.map((option) => + option.get(interaction), + ) as OptionValueMap; await this.action(interaction, ...optionValues); } } - -module.exports = { - SimpleSlashCommandBuilder, - SimpleCommand, -}; From 5e150f4e3456f2620970d2ae6740c6c9cde1c6a0 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:44:45 +0900 Subject: [PATCH 03/31] =?UTF-8?q?TS=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/{commands.js => commands.ts} | 2 -- 1 file changed, 2 deletions(-) rename internal/{commands.js => commands.ts} (99%) diff --git a/internal/commands.js b/internal/commands.ts similarity index 99% rename from internal/commands.js rename to internal/commands.ts index 1b6db4ce..8e26ee83 100644 --- a/internal/commands.js +++ b/internal/commands.ts @@ -1,5 +1,3 @@ -// @ts-check - const { strFormat, LANG } = require('../util/languages'); /** From 50519a627742e5446e0219ae6954f3266903e137 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:48:20 +0900 Subject: [PATCH 04/31] =?UTF-8?q?JSDoc=E3=82=92TS=E3=81=AE=E5=9E=8B?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/commands.ts | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/internal/commands.ts b/internal/commands.ts index 8e26ee83..8cc7a710 100644 --- a/internal/commands.ts +++ b/internal/commands.ts @@ -1,31 +1,19 @@ -const { strFormat, LANG } = require('../util/languages'); +import { strFormat, LANG } from '../util/languages'; +import { ChatInputCommandInteraction, Client } from 'discord.js'; +import { Command } from '../util/types'; -/** - * @template {boolean} [Ready = boolean] - * @typedef {import('discord.js').Client} Client - */ +export class CommandManager { + static readonly default = new CommandManager(); -/** - * @typedef {import('../util/types').Command} Command - */ + #client: Client | null = null; -class CommandManager { - /** - * @readonly - */ - static default = new CommandManager(); - - /** @type {import('discord.js').Client | null} */ - #client = null; - - /** @type {Map} */ - #commands = new Map(); + #commands: Map = new Map(); /** * クライアントにコマンドを登録する。 - * @param {Client} client ログイン済みのクライアント + * @param client ログイン済みのクライアント */ - async setClient(client) { + async setClient(client: Client) { this.#client = client; const commands = []; for (const command of this.#commands.values()) { @@ -41,9 +29,9 @@ class CommandManager { /** * コマンドを追加する。 - * @param {Command | Command[]} commands 追加するコマンド + * @param commands 追加するコマンド */ - addCommands(commands) { + addCommands(commands: Command | Command[]) { if (Array.isArray(commands)) { for (const command of commands) { this.#commands.set(command.data.name, command); @@ -63,10 +51,11 @@ class CommandManager { /** * コマンドの処理を行う。 - * @param {import('discord.js').ChatInputCommandInteraction} interaction - * @param {Client} client */ - async #handleInteraction(interaction, client) { + async #handleInteraction( + interaction: ChatInputCommandInteraction, + client: Client, + ) { const command = this.#commands.get(interaction.commandName); if (!command) { console.error( @@ -94,5 +83,3 @@ class CommandManager { } } } - -module.exports = { CommandManager }; From 0dce40d0dc3bc8b32e3d364736d43cd67bd7a5a2 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:51:26 +0900 Subject: [PATCH 05/31] =?UTF-8?q?TS=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cdn/{index.js => index.ts} | 2 -- 1 file changed, 2 deletions(-) rename packages/cdn/{index.js => index.ts} (94%) diff --git a/packages/cdn/index.js b/packages/cdn/index.ts similarity index 94% rename from packages/cdn/index.js rename to packages/cdn/index.ts index f7622006..d4b92ac6 100644 --- a/packages/cdn/index.js +++ b/packages/cdn/index.ts @@ -1,5 +1,3 @@ -// @ts-check - const { CommandManager } = require('../../internal/commands'); const upload = require('./upload'); From dc5c589613499a991a4022684cb2993a3b31abe2 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:52:10 +0900 Subject: [PATCH 06/31] =?UTF-8?q?=E9=81=A9=E5=90=88=E3=81=95=E3=81=9B?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cdn/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cdn/index.ts b/packages/cdn/index.ts index d4b92ac6..55f134ab 100644 --- a/packages/cdn/index.ts +++ b/packages/cdn/index.ts @@ -1,5 +1,5 @@ -const { CommandManager } = require('../../internal/commands'); -const upload = require('./upload'); +import { CommandManager } from '../../internal/commands'; +import upload from './upload'; class CdnFeature { onLoad() { @@ -7,4 +7,4 @@ class CdnFeature { } } -module.exports = { feature: new CdnFeature() }; +export const feature = new CdnFeature(); From a6b5253ef1a9f4540a79801535fdc55d1e30307e Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:53:08 +0900 Subject: [PATCH 07/31] TS --- packages/misc/commands/{poll.js => poll.ts} | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) rename packages/misc/commands/{poll.js => poll.ts} (91%) diff --git a/packages/misc/commands/poll.js b/packages/misc/commands/poll.ts similarity index 91% rename from packages/misc/commands/poll.js rename to packages/misc/commands/poll.ts index 2760cab2..60f3fb35 100644 --- a/packages/misc/commands/poll.js +++ b/packages/misc/commands/poll.ts @@ -1,10 +1,8 @@ -// @ts-check +import { EmbedBuilder } from 'discord.js'; +import { LANG, strFormat } from '../../../util/languages'; +import { SimpleSlashCommandBuilder } from '../../../common/SimpleCommand'; -const { EmbedBuilder } = require('discord.js'); -const { LANG, strFormat } = require('../../../util/languages'); -const { SimpleSlashCommandBuilder } = require('../../../common/SimpleCommand'); - -module.exports = SimpleSlashCommandBuilder.create( +export default SimpleSlashCommandBuilder.create( LANG.commands.poll.name, LANG.commands.poll.description, ) From 7ef17ce36e3a63e801c464a7a2d48d9c9833d639 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Thu, 14 Mar 2024 00:34:30 +0900 Subject: [PATCH 08/31] =?UTF-8?q?=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=82=92TS=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/misc/commands/poll.ts | 2 +- .../commands/{randomnum.js => randomnum.ts} | 6 ++-- packages/misc/commands/{reply.js => reply.ts} | 28 +++++++---------- packages/misc/index.js | 3 +- .../player/commands/{pause.js => pause.ts} | 8 ++--- .../player/commands/{queue.js => queue.ts} | 14 ++++----- .../player/commands/{resume.js => resume.ts} | 12 ++++---- packages/player/commands/{skip.js => skip.ts} | 10 +++---- packages/player/commands/{stop.js => stop.ts} | 8 ++--- packages/player/{index.js => index.ts} | 28 ++++++++--------- .../web-api/commands/{check.js => check.ts} | 13 ++++---- .../commands/{nettool.js => nettool.ts} | 30 +++++++++---------- .../commands/{nyanpass.js => nyanpass.ts} | 8 ++--- packages/web-api/{index.js => index.ts} | 4 +-- 14 files changed, 73 insertions(+), 101 deletions(-) rename packages/misc/commands/{randomnum.js => randomnum.ts} (93%) rename packages/misc/commands/{reply.js => reply.ts} (88%) rename packages/player/commands/{pause.js => pause.ts} (68%) rename packages/player/commands/{queue.js => queue.ts} (78%) rename packages/player/commands/{resume.js => resume.ts} (73%) rename packages/player/commands/{skip.js => skip.ts} (80%) rename packages/player/commands/{stop.js => stop.ts} (61%) rename packages/player/{index.js => index.ts} (79%) rename packages/web-api/commands/{check.js => check.ts} (95%) rename packages/web-api/commands/{nettool.js => nettool.ts} (94%) rename packages/web-api/commands/{nyanpass.js => nyanpass.ts} (93%) rename packages/web-api/{index.js => index.ts} (84%) diff --git a/packages/misc/commands/poll.ts b/packages/misc/commands/poll.ts index 60f3fb35..251eaeeb 100644 --- a/packages/misc/commands/poll.ts +++ b/packages/misc/commands/poll.ts @@ -2,7 +2,7 @@ import { EmbedBuilder } from 'discord.js'; import { LANG, strFormat } from '../../../util/languages'; import { SimpleSlashCommandBuilder } from '../../../common/SimpleCommand'; -export default SimpleSlashCommandBuilder.create( +module.exports = SimpleSlashCommandBuilder.create( LANG.commands.poll.name, LANG.commands.poll.description, ) diff --git a/packages/misc/commands/randomnum.js b/packages/misc/commands/randomnum.ts similarity index 93% rename from packages/misc/commands/randomnum.js rename to packages/misc/commands/randomnum.ts index b9f58a78..b9423da4 100755 --- a/packages/misc/commands/randomnum.js +++ b/packages/misc/commands/randomnum.ts @@ -1,7 +1,5 @@ -// @ts-check - -const { LANG, strFormat } = require('../../../util/languages'); -const { SimpleSlashCommandBuilder } = require('../../../common/SimpleCommand'); +import { LANG, strFormat } from '../../../util/languages'; +import { SimpleSlashCommandBuilder } from '../../../common/SimpleCommand'; const DEFAULT_MIN_VALUE = 0; const DEFAULT_MAX_VALUE = 99; diff --git a/packages/misc/commands/reply.js b/packages/misc/commands/reply.ts similarity index 88% rename from packages/misc/commands/reply.js rename to packages/misc/commands/reply.ts index a468ad1d..645b372c 100644 --- a/packages/misc/commands/reply.js +++ b/packages/misc/commands/reply.ts @@ -1,17 +1,12 @@ -// @ts-check +import assert from 'assert'; +import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js'; +import { LANG } from '../../../util/languages'; +import { ClientMessageHandler, ReplyPattern } from '../../../internal/messages'; +import Pager from '../../../util/pager'; +import config from '../../../config.json'; +import { Command } from '../../../util/types'; -const assert = require('assert'); -const { SlashCommandBuilder } = require('discord.js'); -const { LANG } = require('../../../util/languages'); -const { - ClientMessageHandler, - ReplyPattern, -} = require('../../../internal/messages'); -const Pager = require('../../../util/pager'); -const config = require('../../../config.json'); - -/** @type {import("../../../util/types").Command} */ -const commandReply = { +module.exports = { data: new SlashCommandBuilder() .setName(LANG.commands.reply.name) .setDescription(LANG.commands.reply.description) @@ -160,13 +155,12 @@ const commandReply = { assert.fail(subcommand); } }, -}; +} as Command; /** * 使う権限があるかをチェックする。 - * @param {import("discord.js").ChatInputCommandInteraction} interaction */ -async function checkPermission(interaction) { +async function checkPermission(interaction: ChatInputCommandInteraction) { if (!config.replyCustomizeAllowedUsers?.includes(interaction.user.id)) { await interaction.reply({ content: LANG.commands.reply.permissionError, @@ -176,5 +170,3 @@ async function checkPermission(interaction) { } return true; } - -module.exports = commandReply; diff --git a/packages/misc/index.js b/packages/misc/index.js index ad05a06c..991ce3b2 100644 --- a/packages/misc/index.js +++ b/packages/misc/index.js @@ -8,7 +8,8 @@ class MiscFeature { fs.readdirSync(path.join(__dirname, 'commands'), { withFileTypes: true, }).forEach((file) => { - if (!file.isFile() || path.extname(file.name) != '.js') return; + const ext = path.extname(file.name); + if (!file.isFile() || (ext != '.js' && ext != '.ts')) return; const cmds = require(path.join(__dirname, 'commands', file.name)); CommandManager.default.addCommands(cmds); }); diff --git a/packages/player/commands/pause.js b/packages/player/commands/pause.ts similarity index 68% rename from packages/player/commands/pause.js rename to packages/player/commands/pause.ts index fe89f6e3..7bc2a002 100644 --- a/packages/player/commands/pause.js +++ b/packages/player/commands/pause.ts @@ -1,8 +1,6 @@ -// @ts-check - -const { SlashCommandBuilder } = require('discord.js'); -const { LANG } = require('../../../util/languages'); -const { PlayerCommand } = require('../PlayerCommand'); +import { SlashCommandBuilder } from 'discord.js'; +import { LANG } from '../../../util/languages'; +import { PlayerCommand } from '../PlayerCommand'; module.exports = new PlayerCommand( new SlashCommandBuilder() diff --git a/packages/player/commands/queue.js b/packages/player/commands/queue.ts similarity index 78% rename from packages/player/commands/queue.js rename to packages/player/commands/queue.ts index 36d85919..d44a5b86 100755 --- a/packages/player/commands/queue.js +++ b/packages/player/commands/queue.ts @@ -1,11 +1,9 @@ -// @ts-check - -const { SlashCommandBuilder } = require('discord.js'); -const Pager = require('../../../util/pager'); -const { getDuration } = require('../players'); -const Timespan = require('../../../util/timespan'); -const { LANG, strFormat } = require('../../../util/languages'); -const { PlayerCommand } = require('../PlayerCommand'); +import { SlashCommandBuilder } from 'discord.js'; +import Pager from '../../../util/pager'; +import { getDuration } from '../players'; +import Timespan from '../../../util/timespan'; +import { LANG, strFormat } from '../../../util/languages'; +import { PlayerCommand } from '../PlayerCommand'; module.exports = new PlayerCommand( new SlashCommandBuilder() diff --git a/packages/player/commands/resume.js b/packages/player/commands/resume.ts similarity index 73% rename from packages/player/commands/resume.js rename to packages/player/commands/resume.ts index d35eff97..54b4ee17 100644 --- a/packages/player/commands/resume.js +++ b/packages/player/commands/resume.ts @@ -1,11 +1,9 @@ -// @ts-check +import { SlashCommandBuilder } from 'discord.js'; +import { LANG } from '../../../util/languages'; +import { getPlayableVoiceChannelId, getPlayingQueue } from '../players'; +import { Command } from '../../../util/types'; -const { SlashCommandBuilder } = require('discord.js'); -const { LANG } = require('../../../util/languages'); -const { getPlayableVoiceChannelId, getPlayingQueue } = require('../players'); - -/** @type {import("../../../util/types").Command} */ -const commandResume = { +const commandResume: Command = { data: new SlashCommandBuilder() .setName(LANG.commands.resume.name) .setDescription(LANG.commands.resume.description), diff --git a/packages/player/commands/skip.js b/packages/player/commands/skip.ts similarity index 80% rename from packages/player/commands/skip.js rename to packages/player/commands/skip.ts index 62fdefd2..2c04cc01 100755 --- a/packages/player/commands/skip.js +++ b/packages/player/commands/skip.ts @@ -1,9 +1,7 @@ -// @ts-check - -const assert = require('assert'); -const { SlashCommandBuilder } = require('discord.js'); -const { LANG, strFormat } = require('../../../util/languages'); -const { PlayerCommand } = require('../PlayerCommand'); +import assert from 'assert'; +import { SlashCommandBuilder } from 'discord.js'; +import { LANG, strFormat } from '../../../util/languages'; +import { PlayerCommand } from '../PlayerCommand'; module.exports = new PlayerCommand( new SlashCommandBuilder() diff --git a/packages/player/commands/stop.js b/packages/player/commands/stop.ts similarity index 61% rename from packages/player/commands/stop.js rename to packages/player/commands/stop.ts index 0146ce83..b6549dd6 100755 --- a/packages/player/commands/stop.js +++ b/packages/player/commands/stop.ts @@ -1,8 +1,6 @@ -// @ts-check - -const { SlashCommandBuilder } = require('discord.js'); -const { LANG } = require('../../../util/languages'); -const { PlayerCommand } = require('../PlayerCommand'); +import { SlashCommandBuilder } from 'discord.js'; +import { LANG } from '../../../util/languages'; +import { PlayerCommand } from '../PlayerCommand'; module.exports = new PlayerCommand( new SlashCommandBuilder() diff --git a/packages/player/index.js b/packages/player/index.ts similarity index 79% rename from packages/player/index.js rename to packages/player/index.ts index 77c0227c..339e6146 100644 --- a/packages/player/index.js +++ b/packages/player/index.ts @@ -1,21 +1,21 @@ -// @ts-check - -const assert = require('assert'); -const { Player } = require('discord-player'); -const { LANG, strFormat } = require('../../util/languages'); -const { CommandManager } = require('../../internal/commands'); -const { +import assert from 'assert'; +import { GuildQueue, Player } from 'discord-player'; +import { LANG, strFormat } from '../../util/languages'; +import { CommandManager } from '../../internal/commands'; +import { restoreQueues, saveQueue, getDuration, deleteSavedQueues, -} = require('./players'); +} from './players'; +import { Feature } from '../../util/types'; +import { Client } from 'discord.js'; -class PlayerFeature { +class PlayerFeature implements Feature { /** @type {Player | null} */ - #player = null; + #player: Player | null = null; - onLoad(client) { + onLoad(client: Client) { console.log(LANG.discordbot.main.playerLoading); const player = new Player(client); player.extractors.loadDefault(); @@ -82,12 +82,10 @@ class PlayerFeature { assert(player != null); for (const [guildId, queue] of player.nodes.cache) { console.log(guildId); - await saveQueue( - /** @type {import('discord-player').GuildQueue} */ (queue), - ); + await saveQueue(queue as GuildQueue); } await player.destroy(); } } -module.exports = { feature: new PlayerFeature() }; +export const feature = new PlayerFeature(); diff --git a/packages/web-api/commands/check.js b/packages/web-api/commands/check.ts similarity index 95% rename from packages/web-api/commands/check.js rename to packages/web-api/commands/check.ts index 56aa7d6f..a4d1ea52 100755 --- a/packages/web-api/commands/check.js +++ b/packages/web-api/commands/check.ts @@ -1,7 +1,5 @@ -// @ts-check - -const { LANG, strFormat } = require('../../../util/languages'); -const { +import { LANG, strFormat } from '../../../util/languages'; +import { CheckHostRequest, CheckPingOk, isValidHostname, @@ -10,9 +8,10 @@ const { CheckHttpOk, CheckHttpComplete, CheckDnsOk, -} = require('../check-host'); -const { formatTable } = require('../../../util/strings'); -const { SimpleSlashCommandBuilder } = require('../../../common/SimpleCommand'); +} from '../check-host'; +import { formatTable } from '../../../util/strings'; +import { SimpleSlashCommandBuilder } from '../../../common/SimpleCommand'; +import { Command } from '../../../util/types'; const MAX_NODES = 40; diff --git a/packages/web-api/commands/nettool.js b/packages/web-api/commands/nettool.ts similarity index 94% rename from packages/web-api/commands/nettool.js rename to packages/web-api/commands/nettool.ts index 66735ffb..a3d0ccb4 100755 --- a/packages/web-api/commands/nettool.js +++ b/packages/web-api/commands/nettool.ts @@ -1,22 +1,20 @@ -// @ts-check - -const { SlashCommandBuilder } = require('discord.js'); -const dns = require('dns'); -const axios = require('axios').default; -const ipRangeCheck = require('ip-range-check'); -const { LANG, strFormat } = require('../../../util/languages'); -const { getIpInfo } = require('../ip-api'); -const assert = require('assert'); +import { SlashCommandBuilder } from 'discord.js'; +import dns from 'dns'; +import axios from 'axios'; +import ipRangeCheck from 'ip-range-check'; +import { LANG, strFormat } from '../../../util/languages'; +import { getIpInfo } from '../ip-api'; +import assert from 'assert'; let cfIps = []; axios .get('https://www.cloudflare.com/ips-v4') + .then((res) => { + cfIps = res.data.split('\n'); + }) .catch(() => { console.log(LANG.commands.nettool.ipListFetchError); - }) - .then((res) => { - cfIps = res?.data.split('\n'); }); -const dnsTypes = /** @type {const} */ ([ +const dnsTypes = /** @type {const} */ [ 'A', 'AAAA', 'NS', @@ -24,7 +22,7 @@ const dnsTypes = /** @type {const} */ ([ 'TXT', 'MX', 'SRV', -]); +]; module.exports = { data: new SlashCommandBuilder() @@ -297,10 +295,10 @@ module.exports = { const fields = dnsTypes .filter((x) => dnsResult[x]) .map((x) => { - return /** @type {import("discord.js").APIEmbedField} */ ({ + return /** @type {import("discord.js").APIEmbedField} */ { name: x, value: dnsResult[x], - }); + }; }); await interaction.editReply({ diff --git a/packages/web-api/commands/nyanpass.js b/packages/web-api/commands/nyanpass.ts similarity index 93% rename from packages/web-api/commands/nyanpass.js rename to packages/web-api/commands/nyanpass.ts index 7d5c1449..4d9f1f11 100644 --- a/packages/web-api/commands/nyanpass.js +++ b/packages/web-api/commands/nyanpass.ts @@ -1,13 +1,11 @@ -// @ts-check - -const { +import { SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle, -} = require('discord.js'); -const { LANG, strFormat } = require('../../../util/languages'); +} from 'discord.js'; +import { LANG, strFormat } from '../../../util/languages'; const axios = require('axios').default; /** diff --git a/packages/web-api/index.js b/packages/web-api/index.ts similarity index 84% rename from packages/web-api/index.js rename to packages/web-api/index.ts index 099c2b5a..a302fe4a 100644 --- a/packages/web-api/index.js +++ b/packages/web-api/index.ts @@ -1,6 +1,4 @@ -// @ts-check - -const { CommandManager } = require('../../internal/commands'); +import { CommandManager } from '../../internal/commands'; /** * @typedef {import("../../util/types").Feature} Feature From c14575bfda1955f01c637c43ddc2469fe1ddec1d Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:07:22 +0900 Subject: [PATCH 09/31] =?UTF-8?q?ts-check=E3=81=AE=E4=BB=98=E3=81=84?= =?UTF-8?q?=E3=81=9F=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92TS?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{messages.test.js => messages.test.ts} | 4 +- internal/{messages.js => messages.ts} | 16 ++--- jest.config.js | 5 ++ package-lock.json | 64 ++++++++++++++++++- package.json | 3 +- .../{PlayerCommand.js => PlayerCommand.ts} | 6 +- packages/player/{players.js => players.ts} | 34 ++++++++-- .../web-api/{check-host.js => check-host.ts} | 4 +- packages/web-api/{ip-api.js => ip-api.ts} | 8 +-- util/{languages.test.js => languages.test.ts} | 2 +- util/{languages.js => languages.ts} | 4 +- util/{result.js => result.ts} | 4 +- util/{strings.test.js => strings.test.ts} | 2 +- util/{strings.js => strings.ts} | 20 +++--- util/types.ts | 2 - 15 files changed, 124 insertions(+), 54 deletions(-) rename internal/{messages.test.js => messages.test.ts} (90%) rename internal/{messages.js => messages.ts} (97%) create mode 100644 jest.config.js rename packages/player/{PlayerCommand.js => PlayerCommand.ts} (95%) rename packages/player/{players.js => players.ts} (96%) rename packages/web-api/{check-host.js => check-host.ts} (99%) rename packages/web-api/{ip-api.js => ip-api.ts} (91%) rename util/{languages.test.js => languages.test.ts} (93%) rename util/{languages.js => languages.ts} (97%) rename util/{result.js => result.ts} (92%) rename util/{strings.test.js => strings.test.ts} (90%) rename util/{strings.js => strings.ts} (72%) diff --git a/internal/messages.test.js b/internal/messages.test.ts similarity index 90% rename from internal/messages.test.js rename to internal/messages.test.ts index 171b61a0..d97334a8 100644 --- a/internal/messages.test.js +++ b/internal/messages.test.ts @@ -1,6 +1,4 @@ -// @ts-check - -const { ReplyPattern } = require('./messages'); +import { ReplyPattern } from './messages'; test('ReplyPattern.match', () => { const pattern1 = new ReplyPattern('それはそう', 'https://soreha.so/'); diff --git a/internal/messages.js b/internal/messages.ts similarity index 97% rename from internal/messages.js rename to internal/messages.ts index ade15bfe..0d2378d9 100644 --- a/internal/messages.js +++ b/internal/messages.ts @@ -1,5 +1,3 @@ -// @ts-check - /** * このファイルではクライアントが受け取ったメッセージに対して処理を行う。 * @@ -18,8 +16,8 @@ */ const axios = require('axios').default; -const { strFormat, LANG } = require('../util/languages'); -const mongodb = require('./mongodb'); +import { strFormat, LANG } from '../util/languages'; +import mongodb from './mongodb'; /** * @typedef {Object} ReplyGuildSchema replyGuilds のドキュメント。 @@ -53,7 +51,7 @@ function getReplyCollection() { /** * 自動応答のパターン。 */ -class ReplyPattern { +export class ReplyPattern { /** * @readonly * @type {string} @@ -141,7 +139,7 @@ class ReplyPattern { /** * サーバーのメッセージに対して処理を行うオブジェクト。 */ -class GuildMessageHandler { +export class GuildMessageHandler { /** * @readonly * @type {import("discord.js").Client} @@ -237,7 +235,7 @@ class GuildMessageHandler { /** * クライアントが受け取ったメッセージに対して処理を行うオブジェクト。 */ -class ClientMessageHandler { +export class ClientMessageHandler { /** * @type {ClientMessageHandler | null} */ @@ -479,11 +477,9 @@ async function getRedirectUrl(shortUrl) { }); const redirectUrl = response.headers.location; console.log(LANG.discordbot.getRedirectUrl.redirectURL, redirectUrl); - return /** @type {string} */ (redirectUrl); + return /** @type {string} */ redirectUrl; } catch (error) { console.error(LANG.discordbot.getRedirectUrl.error, error.message); throw error; } } - -module.exports = { ReplyPattern, GuildMessageHandler, ClientMessageHandler }; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..6231bde1 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/package-lock.json b/package-lock.json index b3b58438..285f862c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,8 @@ "devDependencies": { "@types/jest": "^29.5.12", "jest": "^29.7.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "ts-jest": "^29.1.2" } }, "node_modules/@ampproject/remapping": { @@ -1922,6 +1923,18 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -4107,6 +4120,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "node_modules/lodash.snakecase": { "version": "4.1.1", "license": "MIT" @@ -5608,6 +5627,49 @@ "version": "0.0.3", "license": "MIT" }, + "node_modules/ts-jest": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", diff --git a/package.json b/package.json index 575d5102..b81ededf 100755 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "devDependencies": { "@types/jest": "^29.5.12", "jest": "^29.7.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "ts-jest": "^29.1.2" }, "scripts": { "start": "npx ts-node .", diff --git a/packages/player/PlayerCommand.js b/packages/player/PlayerCommand.ts similarity index 95% rename from packages/player/PlayerCommand.js rename to packages/player/PlayerCommand.ts index 6455df82..fbd3d307 100644 --- a/packages/player/PlayerCommand.js +++ b/packages/player/PlayerCommand.ts @@ -1,5 +1,3 @@ -// @ts-check - const { getPlayableVoiceChannelId, getPlayingQueue } = require('./players'); const { LANG } = require('../../util/languages'); @@ -17,7 +15,7 @@ const { LANG } = require('../../util/languages'); * かつ音楽が再生されている場合に action 関数を呼び出す。 * @implements {Command} */ -class PlayerCommand { +export class PlayerCommand { data; action; @@ -59,5 +57,3 @@ class PlayerCommand { this.action(interaction, queue, voiceChannelId); } } - -module.exports = { PlayerCommand }; diff --git a/packages/player/players.js b/packages/player/players.ts similarity index 96% rename from packages/player/players.js rename to packages/player/players.ts index ab273637..c885198c 100644 --- a/packages/player/players.js +++ b/packages/player/players.ts @@ -1,5 +1,3 @@ -// @ts-check - const { GuildMember } = require('discord.js'); const { useQueue, Track, useMainPlayer } = require('discord-player'); const Timespan = require('../../util/timespan'); @@ -132,8 +130,8 @@ const functions = { return null; } const queue = - /** @type {import("discord-player").GuildQueue} */ ( - useQueue(guildId) + /** @type {import("discord-player").GuildQueue} */ useQueue( + guildId, ); if (queue?.isPlaying()) return queue; @@ -309,4 +307,30 @@ const functions = { }, }; -module.exports = functions; +const { + getPlayableVoiceChannelId, + getPlayingQueue, + getDuration, + saveVolumeSetting, + loadVolumeSetting, + play, + getNodeOptions, + saveQueue, + deleteSavedQueues, + restoreQueues, + getSavedTracks, +} = functions; + +export { + getPlayableVoiceChannelId, + getPlayingQueue, + getDuration, + saveVolumeSetting, + loadVolumeSetting, + play, + getNodeOptions, + saveQueue, + deleteSavedQueues, + restoreQueues, + getSavedTracks, +}; diff --git a/packages/web-api/check-host.js b/packages/web-api/check-host.ts similarity index 99% rename from packages/web-api/check-host.js rename to packages/web-api/check-host.ts index 0fefc71c..c79dad95 100644 --- a/packages/web-api/check-host.js +++ b/packages/web-api/check-host.ts @@ -1,5 +1,3 @@ -// @ts-check - const { setTimeout } = require('timers/promises'); const axios = require('axios').default; @@ -480,7 +478,7 @@ function isValidHostname(str) { return true; } -module.exports = { +export { CheckHostRequest, CheckHostResult, CheckPingResult, diff --git a/packages/web-api/ip-api.js b/packages/web-api/ip-api.ts similarity index 91% rename from packages/web-api/ip-api.js rename to packages/web-api/ip-api.ts index 74ee3d5b..c9d5f0bb 100644 --- a/packages/web-api/ip-api.js +++ b/packages/web-api/ip-api.ts @@ -1,5 +1,3 @@ -// @ts-check - const axios = require('axios').default; const { Ok, Err } = require('../../util/result'); @@ -60,15 +58,13 @@ const { Ok, Err } = require('../../util/result'); * @param {IpApiGeolocationOption=} params 情報を取得する項目 * @returns {Promise>>} 結果を Result 型でラップしたもの */ -async function getIpInfo(ip, params) { +export async function getIpInfo(ip, params?) { try { const res = await axios.get( `http://ip-api.com/json/${encodeURI(ip)}?${new URLSearchParams(params)}`, ); - return new Ok(/** @type {IpApiGeolocationData} */ (res.data)); + return new Ok(/** @type {IpApiGeolocationData} */ res.data); } catch (e) { return new Err(e); } } - -module.exports = { getIpInfo }; diff --git a/util/languages.test.js b/util/languages.test.ts similarity index 93% rename from util/languages.test.js rename to util/languages.test.ts index 95a18cda..53019be9 100644 --- a/util/languages.test.js +++ b/util/languages.test.ts @@ -1,4 +1,4 @@ -const { strFormat, FormatSyntaxError, assignDeep } = require('./languages'); +import { strFormat, FormatSyntaxError, assignDeep } from './languages'; test('assignDeep', () => { expect( diff --git a/util/languages.js b/util/languages.ts similarity index 97% rename from util/languages.js rename to util/languages.ts index 080ec35f..4c086139 100644 --- a/util/languages.js +++ b/util/languages.ts @@ -1,5 +1,3 @@ -// @ts-check - const path = require('path'); const config = require('../config.json'); const LANG = require('../language/default.json'); @@ -133,4 +131,4 @@ function toMap(values) { return values; } -module.exports = { LANG, FormatSyntaxError, strFormat, assignDeep }; +export { LANG, FormatSyntaxError, strFormat, assignDeep }; diff --git a/util/result.js b/util/result.ts similarity index 92% rename from util/result.js rename to util/result.ts index a3dbe6e7..1c95b6b5 100644 --- a/util/result.js +++ b/util/result.ts @@ -1,5 +1,3 @@ -// @ts-check - /** * @template T * 正常な処理結果。 @@ -54,4 +52,4 @@ class Err { * @typedef {Ok | Err} Result 処理の結果を表す型 */ -module.exports = { Ok, Err }; +export { Ok, Err }; diff --git a/util/strings.test.js b/util/strings.test.ts similarity index 90% rename from util/strings.test.js rename to util/strings.test.ts index 387d87b1..2713a446 100644 --- a/util/strings.test.js +++ b/util/strings.test.ts @@ -1,4 +1,4 @@ -const { formatTable } = require('./strings'); +import { formatTable } from './strings'; test('formatTable', () => { expect( diff --git a/util/strings.js b/util/strings.ts similarity index 72% rename from util/strings.js rename to util/strings.ts index ece13682..9f651c89 100644 --- a/util/strings.js +++ b/util/strings.ts @@ -1,18 +1,20 @@ -// @ts-check +interface FormatTableOption { + /** 列を同じ幅にするために余白を埋める文字 */ + fillString?: string; -/** - * @typedef {Object} FormatTableOption - * @property {string=} fillString 列を同じ幅にするために余白を埋める文字 - * @property {string=} margin 列の間のスペース文字 - * @property {('left' | 'right')[]=} align 各列について文字をどちら側に寄せるか - */ + /** 列の間のスペース文字 */ + margin?: string; + + /** 各列について文字をどちら側に寄せるか */ + align?: ('left' | 'right')[]; +} /** * 二次元配列を表形式の文字列に変換する。 * @param {unknown[][]} table 二次元配列 * @param {FormatTableOption} options オプション */ -function formatTable(table, options = {}) { +export function formatTable(table, options: FormatTableOption = {}) { const stringTable = table.map((row) => row.map((cell) => String(cell))); const /** @type {number[]} */ maxWidths = []; for (const row of stringTable) { @@ -48,5 +50,3 @@ function formatTable(table, options = {}) { }) .join('\n'); } - -module.exports = { formatTable }; diff --git a/util/types.ts b/util/types.ts index 304fbfa9..89802050 100644 --- a/util/types.ts +++ b/util/types.ts @@ -1,5 +1,3 @@ -// @ts-check - import { ChatInputCommandInteraction, Client, From 15a4be6cc3c87a4394bb2883f1aff1027866f238 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:15:44 +0900 Subject: [PATCH 10/31] =?UTF-8?q?main=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E6=8C=87=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cdn/package.json | 2 +- packages/player/package.json | 2 +- packages/web-api/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cdn/package.json b/packages/cdn/package.json index 972d2195..c754e3fc 100644 --- a/packages/cdn/package.json +++ b/packages/cdn/package.json @@ -2,7 +2,7 @@ "name": "cdn", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "index.ts", "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/packages/player/package.json b/packages/player/package.json index 7cb93e77..cd78ef94 100644 --- a/packages/player/package.json +++ b/packages/player/package.json @@ -2,7 +2,7 @@ "name": "player", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "index.ts", "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/packages/web-api/package.json b/packages/web-api/package.json index 791713fc..02b93ad7 100644 --- a/packages/web-api/package.json +++ b/packages/web-api/package.json @@ -2,7 +2,7 @@ "name": "web-api", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "index.ts", "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" From 29c2027d803e0bfaa2fa58522337ed3fc56b8b15 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Thu, 14 Mar 2024 02:26:26 +0900 Subject: [PATCH 11/31] =?UTF-8?q?=E6=9A=97=E9=BB=99=E3=81=AEany=E3=82=92?= =?UTF-8?q?=E7=A6=81=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/commands.ts | 4 - internal/messages.ts | 184 ++++++++------------ packages/player/PlayerCommand.ts | 38 ++--- packages/player/players.ts | 227 +++++++++++++++---------- packages/web-api/check-host.ts | 235 ++++++++++---------------- packages/web-api/commands/check.ts | 49 ++---- packages/web-api/commands/nettool.ts | 22 +-- packages/web-api/commands/nyanpass.ts | 30 ++-- packages/web-api/ip-api.ts | 100 +++++------ tsconfig.json | 3 +- util/languages.ts | 45 +++-- util/result.ts | 46 +---- util/strings.ts | 13 +- 13 files changed, 442 insertions(+), 554 deletions(-) diff --git a/internal/commands.ts b/internal/commands.ts index 8cc7a710..6109af1e 100644 --- a/internal/commands.ts +++ b/internal/commands.ts @@ -41,10 +41,6 @@ export class CommandManager { } } - get client() { - return this.client; - } - get size() { return this.#commands.size; } diff --git a/internal/messages.ts b/internal/messages.ts index 0d2378d9..c355cacc 100644 --- a/internal/messages.ts +++ b/internal/messages.ts @@ -15,36 +15,30 @@ * サーバー毎の処理は {@link GuildMessageHandler#handleMessage} において行われる。 */ -const axios = require('axios').default; +import axios from 'axios'; import { strFormat, LANG } from '../util/languages'; import mongodb from './mongodb'; +import { Collection } from 'mongoose'; +import { Client, Message } from 'discord.js'; -/** - * @typedef {Object} ReplyGuildSchema replyGuilds のドキュメント。 - * @property {string} client クライアントのユーザー ID - * @property {string} guild サーバー ID - */ +interface ReplyGuildSchema { + client: string; + guild: string; +} -/** - * @typedef {Object} ReplySchema replies コレクションのドキュメント。 - * @property {string} client クライアントのユーザー ID - * @property {string} guild サーバー ID - * @property {string} message 反応するメッセージ内容 - * @property {string} reply 返信内容 - * @property {boolean} perfectMatching 完全一致する必要があるか - */ +interface ReplySchema { + client: string; + guild: string; + message: string; + reply: string; + perfectMatching: boolean; +} -/** - * @return {import("mongoose").Collection} - */ -function getReplyGuildCollection() { +function getReplyGuildCollection(): Collection { return mongodb.connection.collection('replyGuilds'); } -/** - * @return {import("mongoose").Collection} - */ -function getReplyCollection() { +function getReplyCollection(): Collection { return mongodb.connection.collection('replies'); } @@ -52,30 +46,22 @@ function getReplyCollection() { * 自動応答のパターン。 */ export class ReplyPattern { - /** - * @readonly - * @type {string} - */ - message; + readonly message: string; - /** - * @readonly - * @type {string} - */ - reply; + readonly reply: string; - /** - * @readonly - * @type {boolean} - */ - perfectMatching; + readonly perfectMatching: boolean; /** - * @param {string} messagePattern 反応するメッセージ内容 - * @param {string} reply 返信内容 - * @param {boolean=} perfectMatching 完全一致する必要があるか + * @param messagePattern 反応するメッセージ内容 + * @param reply 返信内容 + * @param perfectMatching 完全一致する必要があるか */ - constructor(messagePattern, reply, perfectMatching = false) { + constructor( + messagePattern: string, + reply: string, + perfectMatching: boolean = false, + ) { this.message = messagePattern; this.reply = reply; this.perfectMatching = perfectMatching; @@ -83,10 +69,10 @@ export class ReplyPattern { /** * メッセージ内容がこのパターンに一致するかを調べ、一致する場合は返信内容を返す。 - * @param {string} message メッセージ内容 + * @param message メッセージ内容 * @returns メッセージ内容がパターンに一致する場合は返信内容、一致しなければ null */ - apply(message) { + apply(message: string) { if (this.perfectMatching) { if (message == this.message) { return this.reply; @@ -101,11 +87,10 @@ export class ReplyPattern { /** * replies コレクションに格納できる形式に変換する。 - * @param {string} clientUserId クライアントのユーザー ID - * @param {string} guildId サーバー ID - * @returns {ReplySchema} + * @param clientUserId クライアントのユーザー ID + * @param guildId サーバー ID */ - serialize(clientUserId, guildId) { + serialize(clientUserId: string, guildId: string): ReplySchema { const message = this.message; return { client: clientUserId, @@ -118,9 +103,9 @@ export class ReplyPattern { /** * replies コレクションからのデータを ReplyPattern に変換する。 - * @param {ReplySchema} replyDocument replies コレクションのドキュメント + * @param replyDocument replies コレクションのドキュメント */ - static deserialize(replyDocument) { + static deserialize(replyDocument: ReplySchema) { const { message, reply, perfectMatching } = replyDocument; return new ReplyPattern(message, reply, perfectMatching); } @@ -140,28 +125,17 @@ export class ReplyPattern { * サーバーのメッセージに対して処理を行うオブジェクト。 */ export class GuildMessageHandler { - /** - * @readonly - * @type {import("discord.js").Client} - */ - client; + readonly client: Client; - /** - * @readonly - * @type {string} - */ - guildId; + readonly guildId: string; - /** - * @type {Promise>} - */ - replyPatternsPromise; + replyPatternsPromise: Promise>; /** - * @param {import("discord.js").Client} client ログイン済みのクライアント - * @param {string} guildId サーバー ID + * @param client ログイン済みのクライアント + * @param guildId サーバー ID */ - constructor(client, guildId) { + constructor(client: Client, guildId: string) { this.client = client; this.guildId = guildId; this.replyPatternsPromise = loadReplies(client.user.id, guildId); @@ -169,10 +143,10 @@ export class GuildMessageHandler { /** * サーバー内でメッセージを受け取ったときの処理。 - * @param {import("discord.js").Message} message メッセージ - * @returns {Promise} メッセージに反応したかどうか + * @param message メッセージ + * @returns メッセージに反応したかどうか */ - async handleMessage(message) { + async handleMessage(message: Message): Promise { const messageContent = message.content; for (const replyPattern of (await this.replyPatternsPromise).values()) { const replyContent = replyPattern.apply(messageContent); @@ -186,10 +160,10 @@ export class GuildMessageHandler { /** * 自動応答のパターンを追加する。 - * @param {ReplyPattern} replyPattern 自動応答のパターン + * @param replyPattern 自動応答のパターン * @returns 新たに追加した場合は true */ - async addReplyPattern(replyPattern) { + async addReplyPattern(replyPattern: ReplyPattern) { const replyPatterns = await this.replyPatternsPromise; const message = replyPattern.message; if (replyPatterns.has(message)) { @@ -204,10 +178,10 @@ export class GuildMessageHandler { /** * 自動応答のパターンを削除する。 - * @param {string} message 反応するメッセージ内容 + * @param message 反応するメッセージ内容 * @returns 削除した ReplyPattern または、存在しなかった場合 null */ - async removeReplyPattern(message) { + async removeReplyPattern(message: string) { const replyPatterns = await this.replyPatternsPromise; const replyPattern = replyPatterns.get(message); if (replyPattern == null) { @@ -224,9 +198,9 @@ export class GuildMessageHandler { /** * 自動応答のパターンを全て取得する。 - * @returns {Promise} {@link ReplyPattern} の配列 + * @returns {@link ReplyPattern} の配列 */ - async getReplyPatterns() { + async getReplyPatterns(): Promise { const replyPatterns = await this.replyPatternsPromise; return [...replyPatterns.values()]; } @@ -236,35 +210,25 @@ export class GuildMessageHandler { * クライアントが受け取ったメッセージに対して処理を行うオブジェクト。 */ export class ClientMessageHandler { - /** - * @type {ClientMessageHandler | null} - */ - static instance = null; + static instance: ClientMessageHandler | null = null; - /** - * @readonly - * @type {import("discord.js").Client} - */ - client; + readonly client: Client; - /** - * @type {Map} - */ - guildMessageHandlerMap = new Map(); + guildMessageHandlerMap: Map = new Map(); /** - * @param {import("discord.js").Client} client ログイン済みのクライアント + * @param client ログイン済みのクライアント */ - constructor(client) { + constructor(client: Client) { this.client = client; ClientMessageHandler.instance = this; } /** * サーバーに対応する {@link GuildMessageHandler} を取得するか、存在しない場合は新規に作成する。 - * @param {string} guildId サーバー ID + * @param guildId サーバー ID */ - getGuildMessageHandler(guildId) { + getGuildMessageHandler(guildId: string) { const guildMessageHandlerMap = this.guildMessageHandlerMap; const existing = guildMessageHandlerMap.get(guildId); if (existing != null) { @@ -277,10 +241,10 @@ export class ClientMessageHandler { /** * メッセージを受け取ったときの処理。 - * @param {import("discord.js").Message} message メッセージ - * @returns {Promise} メッセージに反応したかどうか + * @param message メッセージ + * @returns メッセージに反応したかどうか */ - async handleMessage(message) { + async handleMessage(message: Message): Promise { if (message.author.bot) { return; } @@ -307,10 +271,10 @@ const defaultReplyPatterns = [ /** * サーバーの自動応答パターンを取得する。 - * @param {string} clientUserId クライアントのユーザー ID - * @param {string} guildId サーバー ID + * @param clientUserId クライアントのユーザー ID + * @param guildId サーバー ID */ -async function loadReplies(clientUserId, guildId) { +async function loadReplies(clientUserId: string, guildId: string) { const replyGuildCollection = getReplyGuildCollection(); const replyCollection = getReplyCollection(); const replyGuildDocument = await replyGuildCollection.findOne({ @@ -329,8 +293,7 @@ async function loadReplies(clientUserId, guildId) { ), ); } - /** @type {Map} */ - const result = new Map(); + const result: Map = new Map(); const replyDocuments = replyCollection.find({ client: clientUserId, guild: guildId, @@ -345,10 +308,9 @@ async function loadReplies(clientUserId, guildId) { /** * 動画の埋め込みに対応した vxtwitter.com, fxtwitter.com, vxtiktok.com の * URL を返信する可能性がある。 - * @param {import("discord.js").Message} message メッセージ - * @returns {Promise} + * @param message メッセージ */ -async function replyAlternativeUrl(message) { +async function replyAlternativeUrl(message: Message): Promise { const urls = message.content.match(/https?:\/\/[^\s]+/g); if (urls == null) { return; @@ -419,10 +381,10 @@ async function replyAlternativeUrl(message) { } /** - * @param {string} url URL + * @param url URL * @returns 動画の埋め込みに対応した代替 URL があるか */ -function isAlternativeUrlAvailable(url) { +function isAlternativeUrlAvailable(url: string) { try { const { hostname } = new URL(url); return ( @@ -438,10 +400,10 @@ function isAlternativeUrlAvailable(url) { /** * 動画の埋め込みに対応した代替 URL を取得する。 - * @param {string} url X または TikTok の URL - * @returns {Promise} 代替 URL + * @param url X または TikTok の URL + * @returns 代替 URL */ -async function getAlternativeUrl(url) { +async function getAlternativeUrl(url: string): Promise { const compiledUrl = new URL(url); const hostname = compiledUrl.hostname; if (hostname == 'twitter.com' || hostname == 'x.com') { @@ -466,10 +428,10 @@ async function getAlternativeUrl(url) { /** * リダイレクト先の URL を取得する。 * 与えられた URL からの応答がリダイレクト先を示さなければ Promise を reject する。 - * @param {string} shortUrl 短縮 URL + * @param shortUrl 短縮 URL * @returns リダイレクト先の URL */ -async function getRedirectUrl(shortUrl) { +async function getRedirectUrl(shortUrl: string) { try { const response = await axios.head(shortUrl, { maxRedirects: 0, @@ -477,7 +439,7 @@ async function getRedirectUrl(shortUrl) { }); const redirectUrl = response.headers.location; console.log(LANG.discordbot.getRedirectUrl.redirectURL, redirectUrl); - return /** @type {string} */ redirectUrl; + return redirectUrl as string; } catch (error) { console.error(LANG.discordbot.getRedirectUrl.error, error.message); throw error; diff --git a/packages/player/PlayerCommand.ts b/packages/player/PlayerCommand.ts index fbd3d307..eae0aa97 100644 --- a/packages/player/PlayerCommand.ts +++ b/packages/player/PlayerCommand.ts @@ -1,41 +1,39 @@ -const { getPlayableVoiceChannelId, getPlayingQueue } = require('./players'); -const { LANG } = require('../../util/languages'); - -/** - * @typedef {import("../../util/types").Command} Command - */ - -/** - * @typedef {import('./players').QueueMetadata} QueueMetadata - */ +import { getPlayableVoiceChannelId, getPlayingQueue } from './players'; +import { LANG } from '../../util/languages'; +import { Command } from '../../util/types'; +import { QueueMetadata } from './players'; +import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js'; +import { GuildQueue } from 'discord-player'; /** * 音楽プレイヤーの操作を行うコマンド。 * コマンドを実行したユーザーがボイスチャンネルに参加していて、 * かつ音楽が再生されている場合に action 関数を呼び出す。 - * @implements {Command} */ -export class PlayerCommand { +export class PlayerCommand implements Command { data; action; /** - * - * @param {import("discord.js").SlashCommandBuilder} data - * @param {(interaction: import("discord.js").ChatInputCommandInteraction, - * queue: import("discord-player").GuildQueue, - * voiceChannelId: string) => Promise} action 音楽プレイヤーの操作。チェックを行った後に呼び出される。 + * @param action 音楽プレイヤーの操作。チェックを行った後に呼び出される。 */ - constructor(data, action) { + constructor( + data: SlashCommandBuilder, + action: ( + interaction: ChatInputCommandInteraction, + queue: GuildQueue, + voiceChannelId: string, + ) => Promise, + ) { this.data = data; this.action = action; } /** - * @param {import("discord.js").ChatInputCommandInteraction} interaction + * @param interaction */ - async execute(interaction) { + async execute(interaction: ChatInputCommandInteraction) { const voiceChannelId = getPlayableVoiceChannelId(interaction); if (voiceChannelId == null) { await interaction.reply({ diff --git a/packages/player/players.ts b/packages/player/players.ts index c885198c..0db112d2 100644 --- a/packages/player/players.ts +++ b/packages/player/players.ts @@ -1,47 +1,91 @@ -const { GuildMember } = require('discord.js'); -const { useQueue, Track, useMainPlayer } = require('discord-player'); -const Timespan = require('../../util/timespan'); -const mongodb = require('../../internal/mongodb'); +import { + BaseInteraction, + GuildMember, + GuildVoiceChannelResolvable, + TextBasedChannel, + User, +} from 'discord.js'; +import { + useQueue, + Track, + useMainPlayer, + Player, + GuildQueue, + TrackLike, + GuildNodeCreateOptions, +} from 'discord-player'; +import Timespan from '../../util/timespan'; +import mongodb from '../../internal/mongodb'; +import { Collection } from 'mongoose'; -/** - * @typedef {Object} VolumeSchema volumes コレクションのドキュメント - * @property {string} guild ギルド ID - * @property {number} volume サーバーで設定されている音量 - */ +/** volumes コレクションのドキュメント */ +interface VolumeSchema { + /** ギルド ID */ + guild: string; -/** - * @typedef {Object} GuildQueueSchema guild_queue コレクションのドキュメント - * @property {string} _id ギルド ID - * @property {string | null} voiceChannel キューのボイスチャンネルの ID - * @property {boolean} is_paused 一時停止中か - * @property {ReturnType | null} current_track このキューの現在の曲 - * @property {number | null} current_time 曲の現在の再生位置 (ミリ秒) - * @property {string | null} textChannel コマンドが実行されたテキストチャンネルの ID - * @property {string | null} client コマンドを受け取ったクライアントの ID - * @property {string} requested_by コマンドの実行者の ID - */ + /** サーバーで設定されている音量 */ + volume: number; +} -/** - * @typedef {Object} GuildQueueTrackSchema guild_queue_tracks コレクションのドキュメント - * @property {string} guild ギルド ID - * @property {number} index 曲のキュー内での番号 (0始まり) - * @property {ReturnType} track キュー内の曲 - */ +/** guild_queue コレクションのドキュメント */ +interface GuildQueueSchema { + /** ギルド ID */ + _id: string; -/** - * @typedef {Object} QueueMetadata キューに付加するメタデータ - * @property {import('discord.js').TextBasedChannel | null} channel コマンドが実行されたテキストチャンネル - * @property {GuildMember | null} client コマンドを受け取ったクライアント - * @property {import('discord.js').User} requestedBy コマンドの実行者 - */ + /** キューのボイスチャンネルの ID */ + voiceChannel: string | null; + + /** 一時停止中か */ + is_paused: boolean; + + /** このキューの現在の曲 */ + current_track: ReturnType | null; + + /** 曲の現在の再生位置 (ミリ秒) */ + current_time: number | null; + + /** コマンドが実行されたテキストチャンネルの ID */ + textChannel: string | null; + + /** コマンドを受け取ったクライアントの ID */ + client: string | null; + + /** コマンドの実行者の ID */ + requested_by: string; +} + +/** guild_queue_tracks コレクションのドキュメント */ +interface GuildQueueTrackSchema { + /** ギルド ID */ + guild: string; + + /** 曲のキュー内での番号 (0始まり) */ + index: number; + + /** キュー内の曲 */ + track: ReturnType; +} + +/** キューに付加するメタデータ */ +export interface QueueMetadata { + /** コマンドが実行されたテキストチャンネル */ + channel: TextBasedChannel | null; + + /** コマンドを受け取ったクライアント */ + client: GuildMember | null; + + /** コマンドの実行者 */ + requestedBy: User; +} /** * データベースから読み出したドキュメントを元にキューの復元する。 - * @param {import("discord-player").Player} player - * @param {GuildQueueSchema} guildQueueDocument * @returns キューが復元されたか */ -async function restoreQueue(player, guildQueueDocument) { +async function restoreQueue( + player: Player, + guildQueueDocument: GuildQueueSchema, +) { const currentTrackSerialized = guildQueueDocument.current_track; const currentTime = guildQueueDocument.current_time; const voiceChannelId = guildQueueDocument?.voiceChannel; @@ -103,10 +147,10 @@ async function restoreQueue(player, guildQueueDocument) { const functions = { /** * 対話を起こしたメンバーが接続していて、この bot が参加しているか参加できるボイスチャンネルの ID を取得する。 - * @param {import('discord.js').BaseInteraction} interaction 対話オブジェクト + * @param interaction 対話オブジェクト * @returns メンバーが接続しているボイスチャンネルの ID。この bot が接続できる状態にない場合は null */ - getPlayableVoiceChannelId(interaction) { + getPlayableVoiceChannelId(interaction: BaseInteraction) { const member = interaction.member; if (!(member instanceof GuildMember)) { return null; @@ -121,40 +165,41 @@ const functions = { /** * 対話が起こったサーバーで再生されている楽曲のキューを取得する。 - * @param {import('discord.js').BaseInteraction} interaction 対話オブジェクト + * @param interaction 対話オブジェクト * @returns 楽曲を再生している場合、楽曲のキュー。再生していない場合、null */ - getPlayingQueue(interaction) { + getPlayingQueue( + interaction: BaseInteraction, + ): GuildQueue | null { const guildId = interaction.guild; if (guildId == null) { return null; } - const queue = - /** @type {import("discord-player").GuildQueue} */ useQueue( - guildId, - ); - if (queue?.isPlaying()) return queue; + const queue = useQueue(guildId) as GuildQueue; + if (queue?.isPlaying()) { + return queue; + } return null; }, /** * トラックの長さを求める。 - * @param {Track} track トラック + * @param track トラック * @returns トラックの長さ */ - getDuration(track) { + getDuration(track: Track) { return new Timespan({ millis: track.durationMS }); }, /** * サーバーでの音量設定を保存する。 - * @param {string} guildId ギルド ID - * @param {number} volume 音量 + * @param guildId ギルド ID + * @param volume 音量 */ - async saveVolumeSetting(guildId, volume) { - /** @type {import("mongoose").Collection} */ - const volumeCollection = mongodb.connection.collection('volumes'); + async saveVolumeSetting(guildId: string, volume: number) { + const volumeCollection = + mongodb.connection.collection('volumes'); await volumeCollection.updateOne( { guild: guildId }, { @@ -169,12 +214,12 @@ const functions = { /** * サーバーでの音量設定を取得する。 - * @param {string} guildId ギルド ID - * @returns {Promise} 音量 + * @param guildId ギルド ID + * @returns 音量 */ - async loadVolumeSetting(guildId) { - /** @type {import("mongoose").Collection} */ - const volumeCollection = mongodb.connection.collection('volumes'); + async loadVolumeSetting(guildId: string): Promise { + const volumeCollection = + mongodb.connection.collection('volumes'); const result = await volumeCollection.findOne({ guild: guildId }); if (result != null) { return result.volume; @@ -183,13 +228,17 @@ const functions = { /** * 音楽の再生を開始する。 - * @template [T=unknown] - * @param {string} guild ギルド ID - * @param {import('discord.js').GuildVoiceChannelResolvable} channel 再生するボイスチャンネル - * @param {import('discord-player').TrackLike} query 再生する曲または音源 - * @param {T=} metadata 付加するメタデータ + * @param guild ギルド ID + * @param channel 再生するボイスチャンネル + * @param query 再生する曲または音源 + * @param metadata 付加するメタデータ */ - async play(guild, channel, query, metadata) { + async play( + guild: string, + channel: GuildVoiceChannelResolvable, + query: TrackLike, + metadata?: T, + ) { return await useMainPlayer().play(channel, query, { nodeOptions: await functions.getNodeOptions(guild, metadata), }); @@ -197,12 +246,14 @@ const functions = { /** * ノードのオプションを取得する。 - * @template [T=unknown] - * @param {string} guild ギルド - * @param {T=} metadata 付加するメタデータ - * @returns {Promise>} ノードのオプション + * @param guild ギルド + * @param metadata 付加するメタデータ + * @returns ノードのオプション */ - async getNodeOptions(guild, metadata) { + async getNodeOptions( + guild: string, + metadata?: T, + ): Promise> { const volume = await functions.loadVolumeSetting(guild); return { metadata, @@ -219,14 +270,14 @@ const functions = { /** * 現在のキューの状態をデータベースに保存する。 - * @param {import("discord-player").GuildQueue} queue キュー + * @param queue キュー */ - async saveQueue(queue) { + async saveQueue(queue: GuildQueue) { const guild = queue.guild.id; const metadata = queue.metadata; - /** @type {import("mongoose").Collection} */ - const guildQueueCollection = mongodb.connection.collection('guild_queues'); + const guildQueueCollection = + mongodb.connection.collection('guild_queues'); await guildQueueCollection.deleteOne({ _id: guild }); await guildQueueCollection.insertOne({ _id: guild, @@ -239,9 +290,10 @@ const functions = { requested_by: metadata.requestedBy.id, }); - /** @type {import("mongoose").Collection} */ const guildQueueTrackCollection = - mongodb.connection.collection('guild_queue_tracks'); + mongodb.connection.collection( + 'guild_queue_tracks', + ); const tracks = queue.tracks.toArray(); await guildQueueTrackCollection.deleteMany({ guild }); await guildQueueTrackCollection.insertMany( @@ -257,15 +309,17 @@ const functions = { * データベースに保存されたキューを削除する。 * @param {string[]} guilds ギルド ID */ - async deleteSavedQueues(...guilds) { - /** @type {import("mongoose").Collection} */ - const guildQueueCollection = mongodb.connection.collection('guild_queues'); + async deleteSavedQueues(...guilds: string[]) { + const guildQueueCollection = + mongodb.connection.collection('guild_queues'); await guildQueueCollection.deleteMany({ _id: { $in: guilds }, }); /** @type {import("mongoose").Collection} */ const guildQueueTrackCollection = - mongodb.connection.collection('guild_queue_tracks'); + mongodb.connection.collection( + 'guild_queue_tracks', + ); await guildQueueTrackCollection.deleteMany({ guild: { $in: guilds }, }); @@ -273,11 +327,11 @@ const functions = { /** * データベースに保存されたキューの状態を復元する。 - * @param {import("discord-player").Player} player プレイヤー + * @param player プレイヤー */ - async restoreQueues(player) { - /** @type {import("mongoose").Collection} */ - const guildQueueCollection = mongodb.connection.collection('guild_queues'); + async restoreQueues(player: Player) { + const guildQueueCollection = + mongodb.connection.collection('guild_queues'); const guildQueueDocuments = guildQueueCollection.find({}); const guildsToDeleteQueues = []; for await (const guildQueueDocument of guildQueueDocuments) { @@ -291,13 +345,14 @@ const functions = { /** * データベースに保存された曲を取得する。 - * @param {import("discord-player").Player} player プレイヤー - * @param {string} guild ギルド ID + * @param player プレイヤー + * @param guild ギルド ID */ - async getSavedTracks(player, guild) { - /** @type {import("mongoose").Collection} */ + async getSavedTracks(player: Player, guild: string) { const guildQueueTrackCollection = - mongodb.connection.collection('guild_queue_tracks'); + mongodb.connection.collection( + 'guild_queue_tracks', + ); const guildQueueTrackDocuments = guildQueueTrackCollection.find({ guild }); const result = []; for await (const { index, track } of guildQueueTrackDocuments) { diff --git a/packages/web-api/check-host.ts b/packages/web-api/check-host.ts index c79dad95..1d7f7964 100644 --- a/packages/web-api/check-host.ts +++ b/packages/web-api/check-host.ts @@ -1,34 +1,27 @@ -const { setTimeout } = require('timers/promises'); -const axios = require('axios').default; +import { setTimeout } from 'timers/promises'; +import axios from 'axios'; -/** - * @template {CheckHostResult} R - * @typedef {Object} CheckHostType - * @property {(data: any) => R} castResult - */ +interface CheckHostType { + castResult: (data: any) => R; +} -/** - * @typedef {[string, string, string]} CheckHostNodeLocation - */ +type CheckHostNodeLocation = [string, string, string]; -/** - * @typedef {Object} CheckHostNode - * @property {string} name - * @property {string} asn - * @property {string} ip - * @property {CheckHostNodeLocation} location - */ +interface CheckHostNode { + name: string; + asn: string; + ip: string; + location: CheckHostNodeLocation; +} -/** - * @typedef {Object} RequestData - * @property {1} ok - * @property {string} request_id - * @property {string} permanent_link - * @property {{[key: string]: string[]}} nodes - */ +interface RequestData { + ok: 1; + request_id: string; + permanent_link: string; + nodes: { [key: string]: string[] }; +} -/** @type {Map} */ -const nodeMap = new Map(); +const nodeMap = new Map(); const axiosCheckHost = axios.create({ baseURL: 'https://check-host.net/', @@ -39,9 +32,9 @@ const axiosCheckHost = axios.create({ /** * 名前からノードを取得する。 - * @param {string} name ノードの名前 + * @param name ノードの名前 */ -async function getCheckHostNode(name) { +async function getCheckHostNode(name: string) { const value = nodeMap.get(name); if (value != null) { return value; @@ -51,7 +44,7 @@ async function getCheckHostNode(name) { for (const [key, value] of Object.entries(nodes)) { const mapValue = nodeMap.get(key); if (mapValue == null) { - nodeMap.set(key, value); + nodeMap.set(key, value as CheckHostNode); } } const result = nodeMap.get(name); @@ -61,27 +54,16 @@ async function getCheckHostNode(name) { return result; } -/** - * @template {CheckHostResult} R - */ -class CheckHostRequest { - /** @type {CheckHostType} */ - checkType; +class CheckHostRequest { + checkType: CheckHostType; - /** @type {string} */ - requestId; + requestId: string; - /** @type {string} */ - permanentLink; + permanentLink: string; - /** @type {CheckHostNode[]} */ - nodes; + nodes: CheckHostNode[]; - /** - * @param {CheckHostType} checkType - * @param {RequestData} data - */ - constructor(checkType, data) { + constructor(checkType: CheckHostType, data: RequestData) { this.checkType = checkType; this.requestId = data.request_id; this.permanentLink = data.permanent_link; @@ -91,8 +73,7 @@ class CheckHostRequest { if (existingNode != null) { nodes.push(existingNode); } else { - /** @type {CheckHostNode} */ - const node = { + const node: CheckHostNode = { name: key, asn: value[4], ip: value[3], @@ -107,14 +88,17 @@ class CheckHostRequest { /** * 結果を問い合わせる。 - * @param {number} successRate この割合のノードが成功したら終了 - * @param {number} time 問い合わせる最大の回数 - * @param {number} period 問い合わせる周期 + * @param successRate この割合のノードが成功したら終了 + * @param time 問い合わせる最大の回数 + * @param period 問い合わせる周期 * @returns */ - async checkResult(successRate = 0, time = 1, period = 2000) { - /** @type {CheckHostResultMap} */ - const result = new CheckHostResultMap(); + async checkResult( + successRate: number = 0, + time: number = 1, + period: number = 2000, + ) { + const result = new CheckHostResultMap(); const successThreshold = this.nodes.length * successRate; for (let i = 0; i < time; i++) { await setTimeout(period); @@ -171,7 +155,11 @@ class CheckHostRequest { * @param {number} maxNodes チェックに用いる最大ノード数 * @returns {Promise>} リクエストを表すオブジェクト */ - static async get(checkType, host, maxNodes) { + static async get( + checkType: 'ping' | 'http' | 'tcp' | 'dns' | 'udp', + host: string, + maxNodes: number, + ): Promise> { const checkTypeObject = checkTypes[checkType]; const res = await axiosCheckHost.get(`/check-${checkType}`, { params: { host, maxNodes }, @@ -180,16 +168,15 @@ class CheckHostRequest { } } -/** - * @template {CheckHostResult} R - * @extends {Map} - */ -class CheckHostResultMap extends Map { +class CheckHostResultMap extends Map< + CheckHostNode, + R +> { /** * 条件に一致するノードの個数を返す。 * @param {(node: CheckHostNode, result: R) => boolean} predicate */ - count(predicate) { + count(predicate: (node: CheckHostNode, result: R) => boolean) { let count = 0; for (const [node, result] of this.entries()) { if (predicate(node, result)) { @@ -215,10 +202,7 @@ class CheckHostResultMap extends Map { class CheckHostResult { state; - /** - * @param {'ok' | 'error' | 'processing'} state - */ - constructor(state) { + constructor(state: 'ok' | 'error' | 'processing') { this.state = state; } } @@ -226,37 +210,28 @@ class CheckHostResult { // check-ping class CheckPingResult extends CheckHostResult { - /** - * @param {'ok' | 'error' | 'processing'} state - */ - constructor(state) { + constructor(state: 'ok' | 'error' | 'processing') { super(state); } } class CheckPingOk extends CheckPingResult { - /** @type {string} */ - host; + host: string; - /** @type {{reply: 'OK' | 'TIMEOUT' | 'MALFORMED', ping: number}[]} */ - values; + values: { reply: 'OK' | 'TIMEOUT' | 'MALFORMED'; ping: number }[]; - /** - * @param {any} payload - */ - constructor(payload) { + constructor(payload: any) { super('ok'); this.host = payload[0][2]; - this.values = payload.map((/** @type {any[]} */ a) => ({ + this.values = payload.map((a: any[]) => ({ reply: a[0], ping: a[1], })); } } -/** @type {CheckHostType} */ -const CHECK_PING = { - castResult(/** @type {any} */ data) { +const CHECK_PING: CheckHostType = { + castResult(data: any) { if (data == null) { return new CheckPingResult('processing'); } @@ -271,28 +246,24 @@ const CHECK_PING = { // check-http class CheckHttpResult extends CheckHostResult { - constructor(state) { + constructor(state: 'ok' | 'error' | 'processing') { super(state); } } class CheckHttpComplete extends CheckHttpResult { - /** @type {boolean} */ - success; + success: boolean; - /** @type {number} */ - time; + time: number; - /** @type {string} */ - statusMessage; + statusMessage: string; - /** - * @param {'ok' | 'error' | 'processing'} state - * @param {number} success - * @param {number} time - * @param {string} statusMessage - */ - constructor(state, success, time, statusMessage) { + constructor( + state: 'ok' | 'error' | 'processing', + success: number, + time: number, + statusMessage: string, + ) { super(state); this.success = success != 0; this.time = time; @@ -301,20 +272,17 @@ class CheckHttpComplete extends CheckHttpResult { } class CheckHttpOk extends CheckHttpComplete { - /** @type {number} */ - statusCode; + statusCode: number; - /** @type {string} */ - host; + host: string; - /** - * @param {number} success - * @param {number} time - * @param {string} statusMessage - * @param {string} statusCode - * @param {string} host - */ - constructor(success, time, statusMessage, statusCode, host) { + constructor( + success: number, + time: number, + statusMessage: string, + statusCode: string, + host: string, + ) { super('ok', success, time, statusMessage); this.statusCode = Number.parseInt(statusCode); this.host = host; @@ -322,18 +290,13 @@ class CheckHttpOk extends CheckHttpComplete { } class CheckHttpError extends CheckHttpComplete { - /** - * @param {number} success - * @param {number} time - * @param {string} statusMessage - */ - constructor(success, time, statusMessage) { + constructor(success: number, time: number, statusMessage: string) { super('error', success, time, statusMessage); } } /** @type {CheckHostType} */ -const CHECK_HTTP = { +const CHECK_HTTP: CheckHostType = { castResult(data) { if (!(data instanceof Array)) { return new CheckHttpResult('processing'); @@ -350,25 +313,17 @@ const CHECK_HTTP = { // check-tcp, check-udp class CheckTcpUdpResult extends CheckHostResult { - /** - * @param {'ok' | 'error' | 'processing'} state - */ - constructor(state) { + constructor(state: 'ok' | 'error' | 'processing') { super(state); } } class CheckTcpUdpOk extends CheckTcpUdpResult { - /** @type {number} */ - time; + time: number; - /** @type {string} */ - address; + address: string; - /** - * @param {any} payload - */ - constructor(payload) { + constructor(payload: any) { super('ok'); this.time = payload.time; this.address = payload.address; @@ -378,18 +333,14 @@ class CheckTcpUdpOk extends CheckTcpUdpResult { class CheckTcpUdpError extends CheckTcpUdpResult { description; - /** - * @param {string} description - */ - constructor(description) { + constructor(description: string) { super('error'); this.description = description; } } -/** @type {CheckHostType} */ -const CHECK_TCP_UDP = { - castResult(/** @type {any} */ data) { +const CHECK_TCP_UDP: CheckHostType = { + castResult(data: any) { if (data == null) { return new CheckTcpUdpResult('processing'); } @@ -407,10 +358,7 @@ const CHECK_TCP_UDP = { // check-dns class CheckDnsResult extends CheckHostResult { - /** - * @param {'ok' | 'error' | 'processing'} state - */ - constructor(state) { + constructor(state: 'ok' | 'error' | 'processing') { super(state); } } @@ -422,13 +370,7 @@ class CheckDnsOk extends CheckDnsResult { ttl; - /** - * - * @param {string[]} a - * @param {string[]} aaaa - * @param {number} ttl - */ - constructor(a, aaaa, ttl) { + constructor(a: string[], aaaa: string[], ttl: number) { super('ok'); this.a = a; this.aaaa = aaaa; @@ -436,8 +378,7 @@ class CheckDnsOk extends CheckDnsResult { } } -/** @type {CheckHostType} */ -const CHECK_DNS = { +const CHECK_DNS: CheckHostType = { castResult(data) { if (data == null) { return new CheckDnsResult('processing'); @@ -466,7 +407,7 @@ const ipv4Regex = const hostnameRegex = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*\.?$/; -function isValidHostname(str) { +function isValidHostname(str: string) { if (!ipv4Regex.test(str) && !hostnameRegex.test(str)) { try { new URL(str); diff --git a/packages/web-api/commands/check.ts b/packages/web-api/commands/check.ts index a4d1ea52..2892f466 100755 --- a/packages/web-api/commands/check.ts +++ b/packages/web-api/commands/check.ts @@ -8,20 +8,18 @@ import { CheckHttpOk, CheckHttpComplete, CheckDnsOk, + CheckHostResult, } from '../check-host'; -import { formatTable } from '../../../util/strings'; +import { FormatTableOption, formatTable } from '../../../util/strings'; import { SimpleSlashCommandBuilder } from '../../../common/SimpleCommand'; -import { Command } from '../../../util/types'; const MAX_NODES = 40; -/** - * @template {import('../check-host').CheckHostResult} T - * @param {CheckHostRequest} request - * @param {(result: T) => unknown[]} rowFormat - * @param {import('../../../util/strings').FormatTableOption} options - */ -async function getFormattedResult(request, rowFormat, options) { +async function getFormattedResult( + request: CheckHostRequest, + rowFormat: (result: T) => unknown[], + options: FormatTableOption, +) { const resultMap = await request.checkResult(1.0, 7); const table = [...resultMap.entries()].map(([node, result]) => { const nodeName = node.name.replace('.node.check-host.net', ''); @@ -39,10 +37,7 @@ async function getFormattedResult(request, rowFormat, options) { }); } -/** - * @param {string} hostname - */ -async function checkPing(hostname) { +async function checkPing(hostname: string) { const request = await CheckHostRequest.get('ping', hostname, MAX_NODES); return async () => await getFormattedResult( @@ -70,17 +65,13 @@ async function checkPing(hostname) { ); } -/** - * @param {string} hostname - */ -async function checkHttp(hostname) { +async function checkHttp(hostname: string) { const request = await CheckHostRequest.get('http', hostname, MAX_NODES); return async () => await getFormattedResult( request, (result) => { - /** @type {unknown[]} */ - const row = [result.state + ',']; + const row: unknown[] = [result.state + ',']; if (result instanceof CheckHttpComplete) { const { time, statusMessage } = result; row.push(time + ','); @@ -99,11 +90,7 @@ async function checkHttp(hostname) { ); } -/** - * @param {'tcp' | 'udp'} type - * @param {string} hostname - */ -async function checkTcpUdp(type, hostname) { +async function checkTcpUdp(type: 'tcp' | 'udp', hostname: string) { const request = await CheckHostRequest.get(type, hostname, MAX_NODES); return async () => await getFormattedResult( @@ -128,10 +115,7 @@ async function checkTcpUdp(type, hostname) { ); } -/** - * @param {string} hostname - */ -async function checkDns(hostname) { +async function checkDns(hostname: string) { const request = await CheckHostRequest.get('dns', hostname, MAX_NODES); return async () => await getFormattedResult( @@ -154,11 +138,10 @@ async function checkDns(hostname) { ); } -/** - * @param {'ping' | 'http' | 'tcp' | 'dns' | 'udp'} type - * @param {string} hostname - */ -function check(type, hostname) { +function check( + type: 'ping' | 'http' | 'tcp' | 'dns' | 'udp', + hostname: string, +) { switch (type) { case 'ping': return checkPing(hostname); diff --git a/packages/web-api/commands/nettool.ts b/packages/web-api/commands/nettool.ts index a3d0ccb4..8cbf014d 100755 --- a/packages/web-api/commands/nettool.ts +++ b/packages/web-api/commands/nettool.ts @@ -5,7 +5,7 @@ import ipRangeCheck from 'ip-range-check'; import { LANG, strFormat } from '../../../util/languages'; import { getIpInfo } from '../ip-api'; import assert from 'assert'; -let cfIps = []; +let cfIps: string[] = []; axios .get('https://www.cloudflare.com/ips-v4') .then((res) => { @@ -14,15 +14,7 @@ axios .catch(() => { console.log(LANG.commands.nettool.ipListFetchError); }); -const dnsTypes = /** @type {const} */ [ - 'A', - 'AAAA', - 'NS', - 'CNAME', - 'TXT', - 'MX', - 'SRV', -]; +const dnsTypes = ['A', 'AAAA', 'NS', 'CNAME', 'TXT', 'MX', 'SRV'] as const; module.exports = { data: new SlashCommandBuilder() @@ -72,7 +64,7 @@ module.exports = { ), execute: async function ( - /** @type {import("discord.js").ChatInputCommandInteraction} */ interaction, + interaction: import('discord.js').ChatInputCommandInteraction, ) { const subcommand = interaction.options.getSubcommand(); if (subcommand === LANG.commands.nettool.subcommands.isProxy.name) { @@ -236,7 +228,7 @@ module.exports = { true, ); try { - const dnsResult = {}; + const dnsResult: Record = {}; await Promise.all( dnsTypes.map(async (type) => { @@ -245,7 +237,9 @@ module.exports = { assert(res instanceof Array); if (res.length > 0) { if (type == 'MX') { - res = res.sort((a, b) => b.priority - a.priority); + res = (res as dns.MxRecord[]).sort( + (a, b) => b.priority - a.priority, + ); dnsResult[type] = '```\n' + res @@ -266,7 +260,7 @@ module.exports = { '```\n' + res .map((x) => { - const isCf = ipRangeCheck(x, cfIps); + const isCf = ipRangeCheck(String(x), cfIps); return strFormat( LANG.commands.nettool.subcommands.nsLookup.record, { diff --git a/packages/web-api/commands/nyanpass.ts b/packages/web-api/commands/nyanpass.ts index 4d9f1f11..0ce2cc54 100644 --- a/packages/web-api/commands/nyanpass.ts +++ b/packages/web-api/commands/nyanpass.ts @@ -4,27 +4,25 @@ import { ButtonBuilder, ActionRowBuilder, ButtonStyle, + InteractionReplyOptions, } from 'discord.js'; import { LANG, strFormat } from '../../../util/languages'; -const axios = require('axios').default; +import axios from 'axios'; +import { Command } from '../../../util/types'; -/** - * @typedef {Object} NyanpassData - * @property {string} time - * @property {string} count - */ +interface NyanpassData { + time: string; + count: string; +} async function getNyanpass() { - /** @type {import('axios').AxiosResponse} */ - const res = await axios.get('https://nyanpass.com/api/get_count'); + const res = await axios.get( + 'https://nyanpass.com/api/get_count', + ); return res.data; } -/** - * - * @returns {Promise} - */ -async function createReply() { +async function createReply(): Promise { const { time, count } = await getNyanpass(); const embed = new EmbedBuilder() .setTitle(LANG.commands.nyanpass.title) @@ -38,8 +36,7 @@ async function createReply() { .setEmoji('✋') .setLabel(LANG.commands.nyanpass.button) .setURL('https://nyanpass.com/'); - /** @type {ActionRowBuilder} */ - const row = new ActionRowBuilder(); + const row = new ActionRowBuilder(); row.addComponents(component); return { embeds: [embed], @@ -47,8 +44,7 @@ async function createReply() { }; } -/** @type {import("../../../util/types").Command} */ -const commandNyanpass = { +const commandNyanpass: Command = { data: new SlashCommandBuilder() .setName(LANG.commands.nyanpass.name) .setDescription(LANG.commands.nyanpass.description), diff --git a/packages/web-api/ip-api.ts b/packages/web-api/ip-api.ts index c9d5f0bb..3ce70799 100644 --- a/packages/web-api/ip-api.ts +++ b/packages/web-api/ip-api.ts @@ -1,67 +1,59 @@ +import { Result } from '../../util/result'; +import { Split } from '../../util/types'; + const axios = require('axios').default; const { Ok, Err } = require('../../util/result'); -/** - * @typedef {Object} IpApiGeolocationFullData - * @property {string} status - * @property {string} message - * @property {string} continent - * @property {string} continentCode - * @property {string} country - * @property {string} countryCode - * @property {string} region - * @property {string} regionName - * @property {string} city - * @property {string} district - * @property {string} zip - * @property {number} lat - * @property {number} lon - * @property {string} timezone - * @property {number} offset - * @property {string} isp - * @property {string} org - * @property {string} as - * @property {string} asname - * @property {string} reverse - * @property {boolean} mobile - * @property {boolean} proxy - * @property {boolean} hosting - * @property {string} query - */ +interface IpApiGeolocationFullData { + status: string; + message: string; + continent: string; + continentCode: string; + country: string; + countryCode: string; + region: string; + regionName: string; + city: string; + district: string; + zip: string; + lat: number; + lon: number; + timezone: string; + offset: number; + isp: string; + org: string; + as: string; + asname: string; + reverse: string; + mobile: boolean; + proxy: boolean; + hosting: boolean; + query: string; +} -/** - * @template {string} F - * @typedef {Object} IpApiGeolocationOption - * @property {F=} fields - * @property {( - * "en" | - * "de" | - * "es" | - * "pt-BR" | - * "fr" | - * "ja" | - * "zh-CN" | - * "ru" - * )=} lang - */ +interface IpApiGeolocationOption { + fields?: F; + lang?: 'en' | 'de' | 'es' | 'pt-BR' | 'fr' | 'ja' | 'zh-CN' | 'ru'; +} -/** - * @template {string} T - * @typedef {Partial & ({ - * [K in import("../../util/types").Split & keyof IpApiGeolocationFullData]: IpApiGeolocationFullData[K] - * })} IpApiGeolocationData - */ +type IpApiGeolocationData = + Partial & { + [K in Split & + keyof IpApiGeolocationFullData]: IpApiGeolocationFullData[K]; + }; /** - * @template {string} F - * @param {string} ip IP アドレス - * @param {IpApiGeolocationOption=} params 情報を取得する項目 - * @returns {Promise>>} 結果を Result 型でラップしたもの + * @param ip IP アドレス + * @param params 情報を取得する項目 + * @returns 結果を Result 型でラップしたもの */ -export async function getIpInfo(ip, params?) { +export async function getIpInfo( + ip: string, + params?: IpApiGeolocationOption, +): Promise>> { try { const res = await axios.get( - `http://ip-api.com/json/${encodeURI(ip)}?${new URLSearchParams(params)}`, + `http://ip-api.com/json/${encodeURI(ip)}?${new URLSearchParams(params as Record)}`, ); return new Ok(/** @type {IpApiGeolocationData} */ res.data); } catch (e) { diff --git a/tsconfig.json b/tsconfig.json index 67976fc2..eee70e04 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "dist", - "skipLibCheck": true + "skipLibCheck": true, + "noImplicitAny": true } } diff --git a/util/languages.ts b/util/languages.ts index 4c086139..2ec90095 100644 --- a/util/languages.ts +++ b/util/languages.ts @@ -1,11 +1,8 @@ -const path = require('path'); -const config = require('../config.json'); -const LANG = require('../language/default.json'); +import * as path from 'path'; +import * as config from '../config.json'; +import * as LANG from '../language/default.json'; -/** - * @type {typeof import('../language/default.json')} - */ -const configLANG = require( +const configLANG: typeof import('../language/default.json') = require( path.join( __dirname, '..', @@ -14,10 +11,12 @@ const configLANG = require( ), ); -function assignDeep(target, source) { +function assignDeep(target: Record, source: unknown) { for (const [key, value] of Object.entries(source)) { - if (target[key] instanceof Object) assignDeep(target[key], value); - else if (value instanceof Object) target[key] = assignDeep({}, value); + const targetValue = target[key]; + if (targetValue instanceof Object) { + assignDeep(targetValue as Record, value); + } else if (value instanceof Object) target[key] = assignDeep({}, value); else target[key] = value; } return target; @@ -26,10 +25,7 @@ function assignDeep(target, source) { assignDeep(LANG, configLANG); class FormatSyntaxError extends SyntaxError { - /** - * @param {string} message - */ - constructor(message) { + constructor(message: string) { super(message); } } @@ -40,7 +36,9 @@ const State = { ESCAPED: 1, AFTER_DOLLAR: 2, IN_PLACEHOLDER: 3, -}; +} as const; + +type State = (typeof State)[keyof typeof State]; /** * 文字列中のプレースホルダを指定された値で置き換える。 @@ -65,17 +63,17 @@ const State = { * strFormat("text ${0} abc", 123); // == "text 123 abc" * ``` * - * @param {string} str 文字列のフォーマット - * @param {unknown[]} values プレースホルダを置き換える値 + * @param str 文字列のフォーマット + * @param values プレースホルダを置き換える値 */ -function strFormat(str, ...values) { +function strFormat(str: string, ...values: unknown[]) { const map = toMap(values); let result = ''; let placeholder = ''; // 入力を str, 状態を PLAIN, ESCAPED, AFTER_DOLLAR, IN_PLACEHOLDER とした有限オートマトンを構成 // 入力は常に1文字ずつ読み進められる。 - let state = State.PLAIN; + let state: State = State.PLAIN; for (const c of str) { switch (state) { case State.PLAIN: @@ -118,17 +116,14 @@ function strFormat(str, ...values) { return result; } -/** - * @param {unknown[]} values - */ -function toMap(values) { +function toMap(values: unknown[]): Record | null { if (values.length == 0) { return null; } if (values.length == 1 && values[0] instanceof Object) { - return values[0]; + return values[0] as Record; } - return values; + return values as object as Record; } export { LANG, FormatSyntaxError, strFormat, assignDeep }; diff --git a/util/result.ts b/util/result.ts index 1c95b6b5..c30a865a 100644 --- a/util/result.ts +++ b/util/result.ts @@ -1,55 +1,27 @@ /** - * @template T * 正常な処理結果。 */ -class Ok { - /** - * @readonly - */ - status = 'ok'; +export class Ok { + readonly status = 'ok'; - /** - * @readonly - * @type {T} - */ - value; + readonly value: T; - /** - * @param {T} value - */ - constructor(value) { + constructor(value: T) { this.value = value; } } /** - * @template E * 処理中のエラー。 */ -class Err { - /** - * @readonly - */ - status = 'err'; +export class Err { + readonly status = 'err'; - /** - * @readonly - * @type {E} - */ - value; + readonly value: E; - /** - * @param {E} value - */ - constructor(value) { + constructor(value: E) { this.value = value; } } -/** - * @template T - * @template [E=any] - * @typedef {Ok | Err} Result 処理の結果を表す型 - */ - -export { Ok, Err }; +export type Result = Ok | Err; diff --git a/util/strings.ts b/util/strings.ts index 9f651c89..0d294f02 100644 --- a/util/strings.ts +++ b/util/strings.ts @@ -1,4 +1,4 @@ -interface FormatTableOption { +export interface FormatTableOption { /** 列を同じ幅にするために余白を埋める文字 */ fillString?: string; @@ -11,12 +11,15 @@ interface FormatTableOption { /** * 二次元配列を表形式の文字列に変換する。 - * @param {unknown[][]} table 二次元配列 - * @param {FormatTableOption} options オプション + * @param table 二次元配列 + * @param options オプション */ -export function formatTable(table, options: FormatTableOption = {}) { +export function formatTable( + table: unknown[][], + options: FormatTableOption = {}, +) { const stringTable = table.map((row) => row.map((cell) => String(cell))); - const /** @type {number[]} */ maxWidths = []; + const /** @type {number[]} */ maxWidths: number[] = []; for (const row of stringTable) { const length = row.length; for (let j = 0; j < length; j++) { From be60ad61c2348e8979ea5fb858a2e9dfe2de9870 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:04:57 +0900 Subject: [PATCH 12/31] Apply Prettier --- packages/admin/commands/globalban.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 9a591bd8..4edc4671 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -360,15 +360,15 @@ module.exports = { const modal = new ModalBuilder() .setCustomId('gbanReport') .setTitle('レポートしたいユーザーの情報') - .setStyle(TextInputStyle.Short) - + .setStyle(TextInputStyle.Short); + const targetid = new TextInputBuilder() .setCustomId('reportuserid') - .setLabel('通報したいユーザーのID') + .setLabel('通報したいユーザーのID'); const reason = new TextInputBuilder() .setCustomId('reason') - .setTitle('通報理由') + .setTitle('通報理由'); } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From 619dd4375031666944b5a6c28c28590e5fd0f8a4 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Thu, 14 Mar 2024 16:04:59 +0900 Subject: [PATCH 13/31] owo --- packages/admin/commands/globalban.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 4edc4671..5ec57b58 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -360,15 +360,20 @@ module.exports = { const modal = new ModalBuilder() .setCustomId('gbanReport') .setTitle('レポートしたいユーザーの情報') - .setStyle(TextInputStyle.Short); const targetid = new TextInputBuilder() .setCustomId('reportuserid') - .setLabel('通報したいユーザーのID'); + .setLabel('通報したいユーザーのID') + .setStyle(TextInputStyle.Short) + .setMinLength(17) + .setMaxLength(18) + .setValue('開発者ツールを使用して、ユーザーidを入手してください!') const reason = new TextInputBuilder() .setCustomId('reason') - .setTitle('通報理由'); + .setTitle('通報理由') + .setStyle(TextInputStyle.Paragraph) + .setValue('グローバルBANするべきである理由を記入してください。') } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From 0f332da5dfd9ad9f2d8b8ebd6ba00460ce8ed058 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 15:48:43 +0900 Subject: [PATCH 14/31] complete report system --- packages/admin/commands/globalban.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 5ec57b58..9c5fe416 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -374,6 +374,16 @@ module.exports = { .setTitle('通報理由') .setStyle(TextInputStyle.Paragraph) .setValue('グローバルBANするべきである理由を記入してください。') + modal.addComponents(targetid, reason) + await interaction.showModal(modal) + const filter = (mInteraction) => mInteraction.customId === 'gbanreport' + interaction.awaitModalSubmit({ filter, time: 60000 }) + .then(async mInteraction => { + const resultid = mInteraction.fields.getTextInputValue('reportuserid') + const resultreason = mInteraction.fields.getTextInputValue('reason') + await mInteraction.reply(`Result: ${resultid}, ${resultreason}`) + }) + .catch(console.error) } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From ff68badfbf41c5bf099baa94b8a5c67f3e16eb29 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 15:52:46 +0900 Subject: [PATCH 15/31] oopsie --- packages/admin/commands/globalban.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 9c5fe416..ea21504f 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -55,6 +55,11 @@ module.exports = { .setRequired(true), ), ) + .addSubcommand((subcommand) => + subcommand + .setName('report') + .setDescription('危険なユーザーを通報できます。'), //TODO: 18n + ) .addSubcommand((subcommand) => subcommand .setName(LANG.commands.globalban.subcommands.list.name) From 3f5541d55c26c0582b3ed7e35852190ca5b115ea Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 15:56:36 +0900 Subject: [PATCH 16/31] my brain issue --- packages/admin/commands/globalban.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index ea21504f..c1cb1bfe 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -376,14 +376,14 @@ module.exports = { const reason = new TextInputBuilder() .setCustomId('reason') - .setTitle('通報理由') + .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) .setValue('グローバルBANするべきである理由を記入してください。') modal.addComponents(targetid, reason) await interaction.showModal(modal) const filter = (mInteraction) => mInteraction.customId === 'gbanreport' interaction.awaitModalSubmit({ filter, time: 60000 }) - .then(async mInteraction => { + .then(async mInteraction => { //TODO: 通報された情報をどこかに送信 const resultid = mInteraction.fields.getTextInputValue('reportuserid') const resultreason = mInteraction.fields.getTextInputValue('reason') await mInteraction.reply(`Result: ${resultid}, ${resultreason}`) From def2aedb08cd6c1dedd29cd9eb88b66a3dfe02c2 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:02:03 +0900 Subject: [PATCH 17/31] fix interaction --- packages/admin/commands/globalban.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index c1cb1bfe..a04cb106 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -76,7 +76,10 @@ module.exports = { ); } - await interaction.deferReply(); + if (subcommand !== 'report') { + console.log('Defering') + await interaction.deferReply(); + } if (subcommand === LANG.commands.globalban.subcommands.sync.name) { const member = interaction.guild.members.cache.get(interaction.user.id); if (!member.permissions.has([PermissionsBitField.Flags.Administrator])) { From f295b6d5288afb3b0eba527610ff164610845a0b Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:12:37 +0900 Subject: [PATCH 18/31] okay, I fixed modal issue --- packages/admin/commands/globalban.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index a04cb106..8f4f7c27 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -4,6 +4,7 @@ const { ModalBuilder, TextInputBuilder, TextInputStyle, + ActionRowBuilder, } = require('discord.js'); const mongodb = require('../../../internal/mongodb'); //*MongoDB const { AdminUserIDs } = require('../../../config.json'); @@ -382,7 +383,9 @@ module.exports = { .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) .setValue('グローバルBANするべきである理由を記入してください。') - modal.addComponents(targetid, reason) + const firstRow = new ActionRowBuilder().addComponents(targetid) + const secondRow = new ActionRowBuilder().addComponents(reason) + modal.addComponents(firstRow, secondRow) await interaction.showModal(modal) const filter = (mInteraction) => mInteraction.customId === 'gbanreport' interaction.awaitModalSubmit({ filter, time: 60000 }) From c55359056025deb6ca2a54de4e7a28715eaa5456 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:12:53 +0900 Subject: [PATCH 19/31] prettier moment --- packages/admin/commands/globalban.js | 42 +++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 8f4f7c27..698ce5de 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -56,10 +56,11 @@ module.exports = { .setRequired(true), ), ) - .addSubcommand((subcommand) => - subcommand - .setName('report') - .setDescription('危険なユーザーを通報できます。'), //TODO: 18n + .addSubcommand( + (subcommand) => + subcommand + .setName('report') + .setDescription('危険なユーザーを通報できます。'), //TODO: 18n ) .addSubcommand((subcommand) => subcommand @@ -78,7 +79,7 @@ module.exports = { } if (subcommand !== 'report') { - console.log('Defering') + console.log('Defering'); await interaction.deferReply(); } if (subcommand === LANG.commands.globalban.subcommands.sync.name) { @@ -368,7 +369,7 @@ module.exports = { } else if (subcommand === 'report') { const modal = new ModalBuilder() .setCustomId('gbanReport') - .setTitle('レポートしたいユーザーの情報') + .setTitle('レポートしたいユーザーの情報'); const targetid = new TextInputBuilder() .setCustomId('reportuserid') @@ -376,25 +377,28 @@ module.exports = { .setStyle(TextInputStyle.Short) .setMinLength(17) .setMaxLength(18) - .setValue('開発者ツールを使用して、ユーザーidを入手してください!') + .setValue('開発者ツールを使用して、ユーザーidを入手してください!'); const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) - .setValue('グローバルBANするべきである理由を記入してください。') - const firstRow = new ActionRowBuilder().addComponents(targetid) - const secondRow = new ActionRowBuilder().addComponents(reason) - modal.addComponents(firstRow, secondRow) - await interaction.showModal(modal) - const filter = (mInteraction) => mInteraction.customId === 'gbanreport' - interaction.awaitModalSubmit({ filter, time: 60000 }) - .then(async mInteraction => { //TODO: 通報された情報をどこかに送信 - const resultid = mInteraction.fields.getTextInputValue('reportuserid') - const resultreason = mInteraction.fields.getTextInputValue('reason') - await mInteraction.reply(`Result: ${resultid}, ${resultreason}`) + .setValue('グローバルBANするべきである理由を記入してください。'); + const firstRow = new ActionRowBuilder().addComponents(targetid); + const secondRow = new ActionRowBuilder().addComponents(reason); + modal.addComponents(firstRow, secondRow); + await interaction.showModal(modal); + const filter = (mInteraction) => mInteraction.customId === 'gbanreport'; + interaction + .awaitModalSubmit({ filter, time: 60000 }) + .then(async (mInteraction) => { + //TODO: 通報された情報をどこかに送信 + const resultid = + mInteraction.fields.getTextInputValue('reportuserid'); + const resultreason = mInteraction.fields.getTextInputValue('reason'); + await mInteraction.reply(`Result: ${resultid}, ${resultreason}`); }) - .catch(console.error) + .catch(console.error); } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From 8c326a2823087d0e78843abf42bfe934c924bdd8 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:20:49 +0900 Subject: [PATCH 20/31] =?UTF-8?q?=E6=96=87=E5=AD=97=E6=95=B0=E5=88=B6?= =?UTF-8?q?=E9=99=90=E3=81=AB=E4=B8=AD=E6=8C=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/admin/commands/globalban.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 698ce5de..8979eaa2 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -369,21 +369,21 @@ module.exports = { } else if (subcommand === 'report') { const modal = new ModalBuilder() .setCustomId('gbanReport') - .setTitle('レポートしたいユーザーの情報'); + .setTitle('通報したいユーザーについて'); const targetid = new TextInputBuilder() .setCustomId('reportuserid') - .setLabel('通報したいユーザーのID') + .setLabel('ユーザーID') .setStyle(TextInputStyle.Short) .setMinLength(17) .setMaxLength(18) - .setValue('開発者ツールを使用して、ユーザーidを入手してください!'); + .setValue('1063527758292070591'); const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) - .setValue('グローバルBANするべきである理由を記入してください。'); + .setValue('理由を記入'); const firstRow = new ActionRowBuilder().addComponents(targetid); const secondRow = new ActionRowBuilder().addComponents(reason); modal.addComponents(firstRow, secondRow); From 08722020eac4e7521df58095d5460789f6cdac19 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:23:39 +0900 Subject: [PATCH 21/31] fuckfuckfuckfuck --- packages/admin/commands/globalban.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 8979eaa2..279e4238 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -377,7 +377,7 @@ module.exports = { .setStyle(TextInputStyle.Short) .setMinLength(17) .setMaxLength(18) - .setValue('1063527758292070591'); + .setValue('1063527758292...'); const reason = new TextInputBuilder() .setCustomId('reason') From 2f09f0a82cd3043ad8581d83b81fde34dd8c2856 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:26:31 +0900 Subject: [PATCH 22/31] =?UTF-8?q?=E3=81=93=E3=82=8C=E3=81=A7=E7=B5=B6?= =?UTF-8?q?=E5=AF=BE=E3=81=84=E3=81=91=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/admin/commands/globalban.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 279e4238..89db5a27 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -377,7 +377,7 @@ module.exports = { .setStyle(TextInputStyle.Short) .setMinLength(17) .setMaxLength(18) - .setValue('1063527758292...'); + .setValue('10635277582...'); const reason = new TextInputBuilder() .setCustomId('reason') From 0b670c0552b55277410630301e32baedb97b4e4a Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:42:06 +0900 Subject: [PATCH 23/31] idk --- packages/admin/commands/globalban.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 89db5a27..b2592d47 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -375,8 +375,8 @@ module.exports = { .setCustomId('reportuserid') .setLabel('ユーザーID') .setStyle(TextInputStyle.Short) - .setMinLength(17) - .setMaxLength(18) + // .setMinLength(17) + // .setMaxLength(18) .setValue('10635277582...'); const reason = new TextInputBuilder() From 5cc2c0be1e1bc0ab22612525a6f24cf431ceaf04 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:42:51 +0900 Subject: [PATCH 24/31] =?UTF-8?q?husky=E3=81=A8lint-staged=E5=B0=8E?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 1 + .lintstagedrc | 3 + package-lock.json | 680 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 9 +- 4 files changed, 690 insertions(+), 3 deletions(-) create mode 100644 .husky/pre-commit create mode 100644 .lintstagedrc diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..2312dc58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 00000000..9c9dd921 --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,3 @@ +{ + "*.{js,ts,json}": "prettier --list-different" +} diff --git a/package-lock.json b/package-lock.json index a38292e0..2100bdb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,9 @@ "devDependencies": { "@types/jest": "^29.5.12", "@types/node": "^20.11.27", + "husky": "^9.0.11", "jest": "^29.7.0", + "lint-staged": "^15.2.2", "prettier": "^3.2.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", @@ -2133,6 +2135,87 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2188,6 +2271,12 @@ "color-support": "bin.js" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, "node_modules/colors": { "version": "1.4.0", "license": "MIT", @@ -2205,6 +2294,15 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/concat-map": { "version": "0.0.1", "license": "MIT" @@ -2672,6 +2770,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2983,6 +3087,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -3192,6 +3308,21 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4110,12 +4241,313 @@ "libsodium": "^0.7.13" } }, + "node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/lint-staged": { + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", + "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.1.0", + "debug": "4.3.4", + "execa": "8.0.1", + "lilconfig": "3.0.0", + "listr2": "8.0.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.4" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", + "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4143,6 +4575,162 @@ "version": "4.1.1", "license": "MIT" }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, + "dependencies": { + "type-fest": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/long": { "version": "3.2.0", "license": "Apache-2.0", @@ -4868,6 +5456,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -5148,6 +5748,22 @@ "node": ">=10" } }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reverbnation-scraper": { "version": "2.0.0", "license": "MIT", @@ -5155,6 +5771,12 @@ "node-fetch": "^2.6.0" } }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, "node_modules/rimraf": { "version": "3.0.2", "license": "ISC", @@ -5360,6 +5982,46 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/soundcloud.ts": { "version": "0.5.2", "license": "MIT", @@ -5444,6 +6106,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6014,6 +6685,15 @@ "version": "4.0.0", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index ab9f8ad9..3c1d0745 100755 --- a/package.json +++ b/package.json @@ -32,15 +32,18 @@ "devDependencies": { "@types/jest": "^29.5.12", "@types/node": "^20.11.27", + "husky": "^9.0.11", "jest": "^29.7.0", + "lint-staged": "^15.2.2", "prettier": "^3.2.5", + "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typescript": "^5.4.2", - "ts-jest": "^29.1.2" + "typescript": "^5.4.2" }, "scripts": { "start": "npx ts-node .", - "test": "jest" + "test": "jest", + "prepare": "husky" }, "workspaces": [ "packages/*" From 0691d2ea3c6eca15e188addff68502207e7bb934 Mon Sep 17 00:00:00 2001 From: takejohn <105504345+takejohn@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:48:42 +0900 Subject: [PATCH 25/31] =?UTF-8?q?Prettier=E3=81=A7=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E8=BE=BC=E3=81=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .lintstagedrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.lintstagedrc b/.lintstagedrc index 9c9dd921..621e03e2 100644 --- a/.lintstagedrc +++ b/.lintstagedrc @@ -1,3 +1,3 @@ { - "*.{js,ts,json}": "prettier --list-different" + "*.{js,ts,json}": "prettier --write" } From d2d7d07f4b7be7a77134e4ba833e2ee35673079b Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:53:15 +0900 Subject: [PATCH 26/31] maybe it works --- packages/admin/commands/globalban.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index b2592d47..51855b02 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -375,30 +375,28 @@ module.exports = { .setCustomId('reportuserid') .setLabel('ユーザーID') .setStyle(TextInputStyle.Short) - // .setMinLength(17) - // .setMaxLength(18) - .setValue('10635277582...'); + .setMinLength(17) const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) - .setValue('理由を記入'); const firstRow = new ActionRowBuilder().addComponents(targetid); const secondRow = new ActionRowBuilder().addComponents(reason); modal.addComponents(firstRow, secondRow); await interaction.showModal(modal); - const filter = (mInteraction) => mInteraction.customId === 'gbanreport'; - interaction - .awaitModalSubmit({ filter, time: 60000 }) - .then(async (mInteraction) => { - //TODO: 通報された情報をどこかに送信 - const resultid = - mInteraction.fields.getTextInputValue('reportuserid'); - const resultreason = mInteraction.fields.getTextInputValue('reason'); - await mInteraction.reply(`Result: ${resultid}, ${resultreason}`); - }) - .catch(console.error); + // const filter = (mInteraction) => mInteraction.customId === 'gbanreport'; + const submitted = await interaction.awaitModalSubmit({ + time: 60000, + filter: i => i.user.id === interaction.user.id + }).catch(e => console.error(e)) + if (submitted) { + //TODO: 通報された情報をどこかに送信 + const resultid = submitted.fields.getTextInputValue('reportuserid'); + const resultreason = submitted.fields.getTextInputValue('reason'); + await submitted.reply(`Result: ${resultid}, ${resultreason}`); + } + } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From b6e6fca28b867194f4e6002bf5dea9c9059aced1 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 16:53:25 +0900 Subject: [PATCH 27/31] prettier --- packages/admin/commands/globalban.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 51855b02..cbbb6723 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -375,28 +375,29 @@ module.exports = { .setCustomId('reportuserid') .setLabel('ユーザーID') .setStyle(TextInputStyle.Short) - .setMinLength(17) + .setMinLength(17); const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') - .setStyle(TextInputStyle.Paragraph) + .setStyle(TextInputStyle.Paragraph); const firstRow = new ActionRowBuilder().addComponents(targetid); const secondRow = new ActionRowBuilder().addComponents(reason); modal.addComponents(firstRow, secondRow); await interaction.showModal(modal); // const filter = (mInteraction) => mInteraction.customId === 'gbanreport'; - const submitted = await interaction.awaitModalSubmit({ - time: 60000, - filter: i => i.user.id === interaction.user.id - }).catch(e => console.error(e)) + const submitted = await interaction + .awaitModalSubmit({ + time: 60000, + filter: (i) => i.user.id === interaction.user.id, + }) + .catch((e) => console.error(e)); if (submitted) { //TODO: 通報された情報をどこかに送信 const resultid = submitted.fields.getTextInputValue('reportuserid'); const resultreason = submitted.fields.getTextInputValue('reason'); await submitted.reply(`Result: ${resultid}, ${resultreason}`); } - } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError, From 70498e8d9dac79b7f7ab14e817a8a741c8faddbc Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 17:05:54 +0900 Subject: [PATCH 28/31] owo --- packages/admin/commands/globalban.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index cbbb6723..ade589b6 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -375,12 +375,16 @@ module.exports = { .setCustomId('reportuserid') .setLabel('ユーザーID') .setStyle(TextInputStyle.Short) - .setMinLength(17); + .setMinLength(17) + .setPlaceholder('1063527758292070591') + .setRequired(true) const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') - .setStyle(TextInputStyle.Paragraph); + .setStyle(TextInputStyle.Paragraph) + .setPlaceholder('通報理由をここに記入') + .setRequired(true) const firstRow = new ActionRowBuilder().addComponents(targetid); const secondRow = new ActionRowBuilder().addComponents(reason); modal.addComponents(firstRow, secondRow); @@ -397,6 +401,8 @@ module.exports = { const resultid = submitted.fields.getTextInputValue('reportuserid'); const resultreason = submitted.fields.getTextInputValue('reason'); await submitted.reply(`Result: ${resultid}, ${resultreason}`); + const SyslogChannel = client.channels.cache.get(syslogChannel); + await SyslogChannel.send(LANG.discordbot.shutdown.sysLog); } } else { return await interaction.editReply( From 867ad1d4d709fe108c0eac05543e179e9d7fe928 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 17:06:01 +0900 Subject: [PATCH 29/31] owo --- packages/admin/commands/globalban.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index ade589b6..dba81f10 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -377,14 +377,14 @@ module.exports = { .setStyle(TextInputStyle.Short) .setMinLength(17) .setPlaceholder('1063527758292070591') - .setRequired(true) + .setRequired(true); const reason = new TextInputBuilder() .setCustomId('reason') .setLabel('通報理由') .setStyle(TextInputStyle.Paragraph) .setPlaceholder('通報理由をここに記入') - .setRequired(true) + .setRequired(true); const firstRow = new ActionRowBuilder().addComponents(targetid); const secondRow = new ActionRowBuilder().addComponents(reason); modal.addComponents(firstRow, secondRow); From b700b6ac5e4ad29c649236e39368865f421c9980 Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 17:41:09 +0900 Subject: [PATCH 30/31] cool visual, cool system --- config.json.example | 1 + packages/admin/commands/globalban.js | 39 +++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/config.json.example b/config.json.example index e8169da8..52beef05 100755 --- a/config.json.example +++ b/config.json.example @@ -25,5 +25,6 @@ "rconport1": "25575", "rconpass1": "yourpassword1234", "syslogChannel": "1151139585791901746", + "notificationChannel": "1151139585791901746", "language": "default" } \ No newline at end of file diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index dba81f10..6fe537fb 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -10,6 +10,7 @@ const mongodb = require('../../../internal/mongodb'); //*MongoDB const { AdminUserIDs } = require('../../../config.json'); const Pager = require('../../../util/pager'); const { LANG, strFormat } = require('../../../util/languages'); +const config = require('../../../config.json'); module.exports = { data: new SlashCommandBuilder() @@ -69,6 +70,7 @@ module.exports = { ), execute: async function ( /** @type {import("discord.js").CommandInteraction} */ interaction, + /** @type {Client} */ client, ) { const executorID = interaction.user.id; // executed by const subcommand = interaction.options.getSubcommand(); @@ -400,9 +402,40 @@ module.exports = { //TODO: 通報された情報をどこかに送信 const resultid = submitted.fields.getTextInputValue('reportuserid'); const resultreason = submitted.fields.getTextInputValue('reason'); - await submitted.reply(`Result: ${resultid}, ${resultreason}`); - const SyslogChannel = client.channels.cache.get(syslogChannel); - await SyslogChannel.send(LANG.discordbot.shutdown.sysLog); + await submitted.reply({ + embeds: [ + { + title: '通報が完了しました!', + description: `ご報告ありがとうございます!\n<@${resultid}>(${resultid})の通報が完了しました。`, + }, + ], + }); + if (!config.notificationChannel) { + throw new Error('configのnotificationChannelを定義しなさい。'); + } + const channel = client.channels.cache.get(config.notificationChannel); + const d = new Date(); + const u = d.getTime(); + const fxunix = Math.floor(u / 1000); + await channel.send({ + embeds: [ + { + title: `レポートが届きました!`, + description: `通報者: ${interaction.user.username} | 通報時刻: ${fxunix}`, + color: 0xff0000, + fields: [ + { + name: 'ユーザー名', + value: `<@${resultid}>(${resultid})`, + }, + { + name: '理由', + value: resultreason, + }, + ], + }, + ], + }); } } else { return await interaction.editReply( From bbfd70f94599884640c5a69a7288bcb917242a6e Mon Sep 17 00:00:00 2001 From: ringoXD Date: Sat, 16 Mar 2024 17:48:56 +0900 Subject: [PATCH 31/31] okay --- packages/admin/commands/globalban.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/admin/commands/globalban.js b/packages/admin/commands/globalban.js index 6fe537fb..f664d31f 100755 --- a/packages/admin/commands/globalban.js +++ b/packages/admin/commands/globalban.js @@ -409,6 +409,7 @@ module.exports = { description: `ご報告ありがとうございます!\n<@${resultid}>(${resultid})の通報が完了しました。`, }, ], + ephemeral: true, }); if (!config.notificationChannel) { throw new Error('configのnotificationChannelを定義しなさい。'); @@ -417,11 +418,11 @@ module.exports = { const d = new Date(); const u = d.getTime(); const fxunix = Math.floor(u / 1000); - await channel.send({ + return await channel.send({ embeds: [ { title: `レポートが届きました!`, - description: `通報者: ${interaction.user.username} | 通報時刻: ${fxunix}`, + description: `通報者: ${interaction.user.username} | 通報時刻: `, color: 0xff0000, fields: [ { @@ -437,6 +438,7 @@ module.exports = { ], }); } + return; } else { return await interaction.editReply( LANG.commands.globalban.unsupportedSubcommandError,