From c12dbff3a210818640ae4c818172edb00fba6099 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Thu, 18 Nov 2021 16:16:04 +0700 Subject: [PATCH 01/34] chore(deps): regenerate lockfile --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 4de1208..bb11a5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "gorou", "version": "2.14.3", "license": "AGPL-3.0", "dependencies": { From 67dc993bc7bd2b8bbb98efb732914b476dec3834 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Thu, 18 Nov 2021 16:16:25 +0700 Subject: [PATCH 02/34] fix(PlayCommand): remove `select` params --- src/commands/music/PlayCommand.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/commands/music/PlayCommand.ts b/src/commands/music/PlayCommand.ts index 5bb0223..065c178 100644 --- a/src/commands/music/PlayCommand.ts +++ b/src/commands/music/PlayCommand.ts @@ -56,7 +56,7 @@ export class PlayCommand extends BaseCommand { embeds: [createEmbed("error", `This command is restricted to <#${ctx.guild!.music.playerChannel}>.`)] }); } - const { max_queue: maxQueue, duplicate_song: duplicateSong } = await this.client.databases.guilds.get(ctx.guild!.id, { select: ["max_queue", "duplicate_song"] }); + const { max_queue: maxQueue, duplicate_song: duplicateSong } = await this.client.databases.guilds.get(ctx.guild!.id); if (maxQueue <= (ctx.guild!.music.player?.queue.totalSize || 0)) { const msg = await ctx.send({ embeds: [ @@ -111,8 +111,11 @@ export class PlayCommand extends BaseCommand { } if (!ctx.guild!.music.player) await ctx.guild!.music.join(vc!.id, ctx.channel as TextChannel); if (response.loadType === "PLAYLIST_LOADED") { - const duplicated = response.tracks.filter(x => ctx.guild!.music.player!.queue.find(y => y.identifier === x.identifier)); - const toAdd = response.tracks.filter(x => duplicateSong && !ctx.guild!.music.player?.queue.find(y => y.identifier === x.identifier)); + const duplicated = response.tracks.filter(x => ctx.guild!.music.player!.queue.some(y => y.identifier === x.identifier) || ctx.guild!.music.player!.queue.current?.identifier === x.identifier); + const toAdd = response.tracks.filter(x => { + if (duplicateSong) return !duplicated.some(y => y.identifier === x.identifier); + return true; + }); for (const trck of toAdd) await ctx.guild!.music.player!.queue.add(trck); if (duplicateSong && duplicated.length) { const duplicateMessage = await ctx.send({ From 50c5a87255ccbfb15837b2626a04a89788739193 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Thu, 18 Nov 2021 16:17:34 +0700 Subject: [PATCH 03/34] chore(config): remove `databaseCacheLifetime` --- src/config.ts | 1 - src/structures/BotClient.ts | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/config.ts b/src/config.ts index ffbe34e..88197dc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,7 +32,6 @@ export const leftTimeout = 120000; export const defaultBanner = process.env.DEFAULT_BANNER!; export const databaseName = process.env.DATABASE_NAME; export const enableProgressBar = process.env.ENABLE_PROGRESS_BAR === "yes"; -export const databaseCacheLifetime = 60000; if (typeof databaseName !== "string") throw new Error("config#databaseName must be a string."); if (typeof defaultBanner !== "string") throw new Error("config#defaultBanner must be a string."); diff --git a/src/structures/BotClient.ts b/src/structures/BotClient.ts index 6bd466e..d4e3f24 100644 --- a/src/structures/BotClient.ts +++ b/src/structures/BotClient.ts @@ -53,9 +53,6 @@ export class BotClient extends Client { this.on("ready", async () => { await this.music.init(this.user!.id); this.connection = await createConnection({ - cache: { - duration: this.config.databaseCacheLifetime - }, database: this.config.databaseName, entities: [GuildSetting], logging: this.config.isDev ? "all" : ["info"], From e9582bc0e22120fe08f1443d1d620d7835e5cfa5 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Thu, 18 Nov 2021 16:25:31 +0700 Subject: [PATCH 04/34] fix(MusicHandler): add return to `reset` function --- src/utils/MusicHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index 8248012..2099c61 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -81,6 +81,7 @@ export class MusicHandler { this.oldExceptionMessage = null; this.oldVoiceStateUpdateMessage = null; this.skipVotes = []; + return undefined; } public async play(): Promise { From e6dbd433aa6ef0b3cc48b926e207f00bd9c9006d Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 12:59:16 +0700 Subject: [PATCH 05/34] feat(SetupCommand): add `MANAGE_MESSAGE` to perms check --- src/commands/administrator/SetupCommand.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/administrator/SetupCommand.ts b/src/commands/administrator/SetupCommand.ts index 95b2bb9..6e78942 100644 --- a/src/commands/administrator/SetupCommand.ts +++ b/src/commands/administrator/SetupCommand.ts @@ -42,9 +42,9 @@ export class SetupCommand extends BaseCommand { embeds: [createEmbed("error", `Already setup in ${old.toString()}`)] }); } - if (!channel.permissionsFor(this.client.user!.id)?.has(["SEND_MESSAGES", "ATTACH_FILES"])) { + if (!channel.permissionsFor(this.client.user!.id)?.has(["SEND_MESSAGES", "ATTACH_FILES", "MANAGE_MESSAGES"])) { return ctx.send({ - embeds: [createEmbed("error", "I need these permissions to make requester channel: `SEND_MESSAGES`, `ATTACH_FILES`")] + embeds: [createEmbed("error", "I need these permissions to make requester channel: `SEND_MESSAGES`, `ATTACH_FILES`, `MANAGE_MESSAGES`")] }); } if (channel.isText()) { From 216cc5c35362acd7392a426ba144d7e9909080db Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 13:02:23 +0700 Subject: [PATCH 06/34] feat(SetupCommand): change to reaction --- src/commands/administrator/SetupCommand.ts | 33 ++-------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/commands/administrator/SetupCommand.ts b/src/commands/administrator/SetupCommand.ts index 6e78942..80eaf42 100644 --- a/src/commands/administrator/SetupCommand.ts +++ b/src/commands/administrator/SetupCommand.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-base-to-string */ -import { GuildChannel, MessageActionRow, MessageButton } from "discord.js"; +import { GuildChannel } from "discord.js"; import { BaseCommand } from "../../structures/BaseCommand"; import { CommandContext } from "../../structures/CommandContext"; import { createEmbed } from "../../utils/createEmbed"; @@ -49,6 +49,7 @@ export class SetupCommand extends BaseCommand { } if (channel.isText()) { data.requesterChannel = channel.id; + const emojis = ["โฏ", "โญ", "๐Ÿ”", "โน", "๐Ÿ”€"]; const msg = await channel.send({ embeds: [ createEmbed("info") @@ -56,33 +57,9 @@ export class SetupCommand extends BaseCommand { .setImage(this.client.config.defaultBanner) .setDescription("Join a voice channel and queue songs by name or url in here.") .setFooter(`Prefix for this server is: ${data.prefix}`) - ], - components: [ - new MessageActionRow() - .addComponents( - new MessageButton() - .setCustomId(this.encode(`player_resumepause`)) - .setEmoji("โฏ") - .setStyle("SECONDARY"), - new MessageButton() - .setCustomId(this.encode(`player_skip`)) - .setEmoji("โญ") - .setStyle("SECONDARY"), - new MessageButton() - .setCustomId(this.encode(`player_loop`)) - .setEmoji("๐Ÿ”") - .setStyle("SECONDARY"), - new MessageButton() - .setCustomId(this.encode(`player_stop`)) - .setEmoji("โน") - .setStyle("DANGER"), - new MessageButton() - .setCustomId(this.encode(`player_shuffle`)) - .setEmoji("๐Ÿ”€") - .setStyle("SUCCESS") - ) ] }); + for (const emoji of emojis) { await msg.react(emoji); } ctx.guild!.music.playerMessage = msg.id; ctx.guild!.music.playerChannel = msg.channelId; data.requesterMessage = msg.id; @@ -92,8 +69,4 @@ export class SetupCommand extends BaseCommand { embeds: [createEmbed("info", `Set requester channel to: <#${data.requesterChannel!}>`)] }); } - - public encode(string: string): string { - return Buffer.from(string).toString("base64"); - } } From eb8ac5d3fd1861dab128a3cfd538266a548e4832 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 13:03:13 +0700 Subject: [PATCH 07/34] chore(config): add `GUILD_MESSAGE_REACTIONS` intent --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 88197dc..069fe1e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,7 +4,7 @@ export const defaultPrefix = "."; export const devs: UserResolvable[] = ["725331428962992131", "740075062190669884", "736943755344609301"]; export const clientOptions: ClientOptions = { allowedMentions: { parse: ["users"] }, - intents: [Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_MEMBERS], + intents: [Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_MEMBERS, Intents.FLAGS.GUILD_MESSAGE_REACTIONS], makeCache: () => new Collection(), restTimeOffset: 300, retryLimit: 3 From 298f436c6a24566ed5ee4b74989b46cdbdf1d34e Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 13:16:03 +0700 Subject: [PATCH 08/34] chore(config): add player emojis --- src/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.ts b/src/config.ts index 069fe1e..51bb7d9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,6 +32,7 @@ export const leftTimeout = 120000; export const defaultBanner = process.env.DEFAULT_BANNER!; export const databaseName = process.env.DATABASE_NAME; export const enableProgressBar = process.env.ENABLE_PROGRESS_BAR === "yes"; +export const emojis = ["โฏ", "โญ", "๐Ÿ”", "โน", "๐Ÿ”€"]; if (typeof databaseName !== "string") throw new Error("config#databaseName must be a string."); if (typeof defaultBanner !== "string") throw new Error("config#defaultBanner must be a string."); From 9ed4f004766e25a12b85d4671452be1648df9961 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 13:16:16 +0700 Subject: [PATCH 09/34] feat(SetupCommand): import from config instead --- src/commands/administrator/SetupCommand.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/commands/administrator/SetupCommand.ts b/src/commands/administrator/SetupCommand.ts index 80eaf42..bce16f9 100644 --- a/src/commands/administrator/SetupCommand.ts +++ b/src/commands/administrator/SetupCommand.ts @@ -49,7 +49,6 @@ export class SetupCommand extends BaseCommand { } if (channel.isText()) { data.requesterChannel = channel.id; - const emojis = ["โฏ", "โญ", "๐Ÿ”", "โน", "๐Ÿ”€"]; const msg = await channel.send({ embeds: [ createEmbed("info") @@ -59,7 +58,7 @@ export class SetupCommand extends BaseCommand { .setFooter(`Prefix for this server is: ${data.prefix}`) ] }); - for (const emoji of emojis) { await msg.react(emoji); } + for (const emoji of this.client.config.emojis) { await msg.react(emoji); } ctx.guild!.music.playerMessage = msg.id; ctx.guild!.music.playerChannel = msg.channelId; data.requesterMessage = msg.id; From c81476428b05121c80e4525541429d12be4de76c Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Sat, 13 Nov 2021 13:21:00 +0700 Subject: [PATCH 10/34] feat(GuildSettingManager): check reaction cache on boot --- src/utils/GuildSettingManager.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/GuildSettingManager.ts b/src/utils/GuildSettingManager.ts index 2e0517e..7c064c9 100644 --- a/src/utils/GuildSettingManager.ts +++ b/src/utils/GuildSettingManager.ts @@ -19,6 +19,10 @@ export class GuildSettingManager { const message = await channel.messages.fetch(data.requesterMessage!).catch(() => null); if (message) { this.client.logger.info(`Fetched ${(message.channel as TextChannel).name} [${message.channelId}] on ${message.guild!.name}`); + const cached = message.reactions.cache.filter(x => this.client.config.emojis.includes(x.emoji.name!)); + if (cached.size !== this.client.config.emojis.length) { + for (const e of this.client.config.emojis) await message.react(e); + } guild.music.playerMessage = message.id; guild.music.playerChannel = message.channelId; } else { From 68ad1f0528e8f986bd2aae53a434d134b433e231 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Thu, 18 Nov 2021 14:40:57 +0700 Subject: [PATCH 11/34] feat(InteractionCreateEvent): completely remove interaction player support --- src/listeners/InteractionCreateEvent.ts | 94 +------------------------ 1 file changed, 1 insertion(+), 93 deletions(-) diff --git a/src/listeners/InteractionCreateEvent.ts b/src/listeners/InteractionCreateEvent.ts index 0273989..ad42f53 100644 --- a/src/listeners/InteractionCreateEvent.ts +++ b/src/listeners/InteractionCreateEvent.ts @@ -1,10 +1,8 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ -import { Interaction, VoiceChannel } from "discord.js"; +import { Interaction } from "discord.js"; import { BaseListener } from "../structures/BaseListener"; import { CommandContext } from "../structures/CommandContext"; import { createEmbed } from "../utils/createEmbed"; import { DefineListener } from "../utils/decorators/DefineListener"; -import { LoopType } from "../utils/MusicHandler"; @DefineListener("interactionCreate") export class InteractionCreateEvent extends BaseListener { @@ -46,96 +44,6 @@ export class InteractionCreateEvent extends BaseListener { } } } - if (interaction.isButton()) { - const src = this.decode(interaction.customId || ""); - if (src.startsWith("player")) { - const action: "resumepause"|"stop"|"skip"|"loop"|"shuffle" = src.split("_")[1] as any; - const { music } = interaction.guild!; - if (!music.player) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", "I'm not playing anything right now", true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - const member = interaction.guild!.members.cache.get(interaction.user.id); - const vc = interaction.guild!.channels.cache.get(member!.voice.channelId!) as VoiceChannel|undefined; - if (!vc) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", "Please join a voice channel", true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - if (!vc.permissionsFor(interaction.guild!.me!)!.has(["CONNECT", "SPEAK"])) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", "I'm missing `CONNECT` or `SPEAK` permission in your voice!", true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - if (!vc.joinable) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", "I can't join your voice channel", true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - if (interaction.guild!.me!.voice.channelId && interaction.guild!.me!.voice.channelId !== member!.voice.channelId) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", `I'm already used on ${interaction.guild!.me!.voice.channel!.toString()}`, true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - const data = await this.client.databases.guilds.get(interaction.guild!.id); - if (data.dj_only && data.dj_role) { - const djRole = interaction.guild!.roles.resolve(data.dj_role); - if (djRole && !member?.roles.cache.has(djRole.id)) { - await interaction.deferReply({ ephemeral: true }); - const msg = await interaction.followUp({ - ephemeral: true, - embeds: [createEmbed("error", `Sorry, but my commands are restricted only for those who has ${djRole.name} role`, true)] - }); - setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); - return undefined; - } - } - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${interaction.user.tag} [${interaction.user.id}] executed "${action}" on ${interaction.guild!.name} [${interaction.guildId}]`); - if (action === "resumepause") { - await music.player.pause(!music.player.paused); - await interaction.deferUpdate(); - await music.updatePlayerEmbed(); - } else if (action === "loop") { - await interaction.deferReply({ ephemeral: true }); - const loopModes = { - [LoopType.ONE]: "track", - [LoopType.ALL]: "queue", - [LoopType.NONE]: "off" - }; - context.args = [loopModes[(music.loopType + 1) as 0|1|2] || loopModes[LoopType.NONE]]; - void this.client.commands.get("loop")!.execute(context); - } else if (action === "stop") { - await music.player!.destroy(); - await music.reset(); - await interaction.deferUpdate(); - } else { - await interaction.deferReply({ ephemeral: true }); - const cmd = this.client.commands.find(x => x.meta.name === action); - if (cmd) void cmd.execute(context); - } - } - } } private decode(string: string): string { From 458e2dabc8a94668067f9dc0c96ee45ba8a66abc Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Fri, 19 Nov 2021 19:05:58 +0700 Subject: [PATCH 12/34] feat(Music): completely move to reaction --- src/listeners/MessageReactionAddEvent.ts | 91 ++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/listeners/MessageReactionAddEvent.ts diff --git a/src/listeners/MessageReactionAddEvent.ts b/src/listeners/MessageReactionAddEvent.ts new file mode 100644 index 0000000..ecc15cb --- /dev/null +++ b/src/listeners/MessageReactionAddEvent.ts @@ -0,0 +1,91 @@ +import { MessageReaction, Message, User, VoiceChannel } from "discord.js"; +import { BaseListener } from "../structures/BaseListener"; +import { createEmbed } from "../utils/createEmbed"; +import { DefineListener } from "../utils/decorators/DefineListener"; +import { LoopType } from "../utils/MusicHandler"; +import { CommandContext } from "../structures/CommandContext"; + +@DefineListener("messageReactionAdd") +export class MessageReactionAddEvent extends BaseListener { + public async execute(messageReaction: MessageReaction, user: User): Promise { + if (!messageReaction.message.guild || user.equals(this.client.user!)) return undefined; + const { message, message: { guild }, message: { guild: { music } } } = messageReaction; + const data = await this.client.databases.guilds.get(guild.id, { select: ["requesterMessage", "dj_only", "dj_role"] }); + if (data.requesterMessage === message.id) { + const emojis = [ + { + emoji: "โฏ", + command: "resumepause" + }, + { + emoji: "โญ", + command: "skip" + }, + { + emoji: "๐Ÿ”", + command: "loop" + }, + { + emoji: "โน", + command: "stop" + }, + { + emoji: "๐Ÿ”€", + command: "shuffle" + } + ]; + await messageReaction.users.remove(user); + if (!music.player) { + const msg = await message.channel.send({ + embeds: [createEmbed("error", "I'm not playing anything right now", true)] + }); + setTimeout(() => msg.delete().catch(() => null), 5000); + return undefined; + } + const member = guild.members.cache.get(user.id)!; + const vc = guild.channels.cache.get(member.voice.channelId!) as VoiceChannel|null; + if (!vc) { + const msg = await message.channel.send({ + embeds: [createEmbed("error", "Please join a voice channel", true)] + }); + setTimeout(() => msg.delete().catch(() => null), 5000); + return undefined; + } + if (guild.me!.voice.channelId && guild.me!.voice.channelId !== member.voice.channelId) { + const msg = await message.channel.send({ + embeds: [createEmbed("error", `I'm already used on ${guild.me!.voice.channel!.toString()}`, true)] + }); + setTimeout(() => msg.delete().catch(() => null), 5000); + return undefined; + } + if (data.dj_only && data.dj_role) { + const djRole = guild.roles.resolve(data.dj_role); + if (djRole && !member.roles.cache.has(djRole.id)) { + const msg = await message.channel.send({ + embeds: [createEmbed("error", `Sorry, but my commands are restricted only for those who has ${djRole.name} role`, true)] + }); + setTimeout(() => msg.delete().catch(() => null), 5000); + return undefined; + } + } + this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${user.tag} [${user.id}] executed "${messageReaction.emoji.name!.toString()}" on ${guild.name} [${guild.id}]`); + messageReaction.message.author = user; + const context = new CommandContext(messageReaction.message as Message); + const action = emojis.find(x => x.emoji === messageReaction.emoji.name!.toString()); + if (!action) return; + if (action.command === "resumepause") { + await music.player.pause(!music.player.paused); + return music.updatePlayerEmbed(); + } else if (action.command === "loop") { + const loopModes = { + [LoopType.ONE]: "track", + [LoopType.ALL]: "queue", + [LoopType.NONE]: "off" + }; + context.args = [loopModes[(music.loopType + 1) as 0|1|2] || loopModes[LoopType.NONE]]; + return this.client.commands.get("loop")!.execute(context); + } + return this.client.commands.get(action.command)!.execute(context); + } + } +} From cf514c8c1cb59bea8c976dc4812fd55147846fc3 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Fri, 19 Nov 2021 19:07:02 +0700 Subject: [PATCH 13/34] fix(SetupCommand): eslint error --- src/commands/administrator/SetupCommand.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/administrator/SetupCommand.ts b/src/commands/administrator/SetupCommand.ts index bce16f9..adffef5 100644 --- a/src/commands/administrator/SetupCommand.ts +++ b/src/commands/administrator/SetupCommand.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-base-to-string */ -import { GuildChannel } from "discord.js"; +import { GuildChannel, EmojiIdentifierResolvable } from "discord.js"; import { BaseCommand } from "../../structures/BaseCommand"; import { CommandContext } from "../../structures/CommandContext"; import { createEmbed } from "../../utils/createEmbed"; @@ -58,7 +58,7 @@ export class SetupCommand extends BaseCommand { .setFooter(`Prefix for this server is: ${data.prefix}`) ] }); - for (const emoji of this.client.config.emojis) { await msg.react(emoji); } + for (const emoji of this.client.config.emojis) { await msg.react(emoji as EmojiIdentifierResolvable); } ctx.guild!.music.playerMessage = msg.id; ctx.guild!.music.playerChannel = msg.channelId; data.requesterMessage = msg.id; From 6c5457dd7bf7110f713e5de25874b36479d7ec89 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Fri, 19 Nov 2021 19:28:52 +0700 Subject: [PATCH 14/34] fix(MessageReactionAddEvent): remove database `select` --- src/listeners/MessageReactionAddEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/listeners/MessageReactionAddEvent.ts b/src/listeners/MessageReactionAddEvent.ts index ecc15cb..72038b5 100644 --- a/src/listeners/MessageReactionAddEvent.ts +++ b/src/listeners/MessageReactionAddEvent.ts @@ -10,7 +10,7 @@ export class MessageReactionAddEvent extends BaseListener { public async execute(messageReaction: MessageReaction, user: User): Promise { if (!messageReaction.message.guild || user.equals(this.client.user!)) return undefined; const { message, message: { guild }, message: { guild: { music } } } = messageReaction; - const data = await this.client.databases.guilds.get(guild.id, { select: ["requesterMessage", "dj_only", "dj_role"] }); + const data = await this.client.databases.guilds.get(guild.id); if (data.requesterMessage === message.id) { const emojis = [ { From c56f84a5ce0cf691710ca217c2799064e0724e55 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 08:06:15 +0700 Subject: [PATCH 15/34] feat(MusicHandler): make updateInterval undefined --- src/utils/MusicHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index 2099c61..3389177 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -77,6 +77,7 @@ export class MusicHandler { if (this.timeout) clearTimeout(this.timeout); if (this.updateInterval) clearInterval(this.updateInterval); this.timeout = undefined; + this.updateInterval = undefined; this.oldMusicMessage = null; this.oldExceptionMessage = null; this.oldVoiceStateUpdateMessage = null; From 5d4e0a6ad68b548d2049b743db90237b07b2d04f Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 09:32:08 +0700 Subject: [PATCH 16/34] feat(Music): better `updatePlayerEmbed` handling --- src/listeners/NodeRawEvent.ts | 14 ++++++++++++++ src/listeners/QueueEndEvent.ts | 4 ++-- src/listeners/TrackStartEvent.ts | 1 - src/listeners/VoiceStateUpdateEvent.ts | 2 +- src/utils/MusicHandler.ts | 7 ++----- 5 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 src/listeners/NodeRawEvent.ts diff --git a/src/listeners/NodeRawEvent.ts b/src/listeners/NodeRawEvent.ts new file mode 100644 index 0000000..4a65394 --- /dev/null +++ b/src/listeners/NodeRawEvent.ts @@ -0,0 +1,14 @@ +import { BaseListener } from "../structures/BaseListener"; +import { DefineListener } from "../utils/decorators/DefineListener"; + +@DefineListener("nodeRaw", "erela") +export class NodeRawEvent extends BaseListener { + public async execute(payload: any): Promise { + if (payload?.op === "playerUpdate" && payload.state.connected) { + const manager = this.client._music.fetch(String(payload.guildId)); + if (Boolean(manager)) { + await manager.updatePlayerEmbed(); + } + } + } +} diff --git a/src/listeners/QueueEndEvent.ts b/src/listeners/QueueEndEvent.ts index acc56a8..5e018df 100644 --- a/src/listeners/QueueEndEvent.ts +++ b/src/listeners/QueueEndEvent.ts @@ -15,7 +15,7 @@ export class QueueEndEvent extends BaseListener { ] }); } - manager.reset(); - void player.destroy(); + await manager.reset(); + await player.destroy(); } } diff --git a/src/listeners/TrackStartEvent.ts b/src/listeners/TrackStartEvent.ts index 30f271a..8ae553f 100644 --- a/src/listeners/TrackStartEvent.ts +++ b/src/listeners/TrackStartEvent.ts @@ -16,6 +16,5 @@ export class TrackStartEvent extends BaseListener { }); manager.oldMusicMessage = msg.id; } - manager.updateInterval = setInterval(() => manager.updatePlayerEmbed(), 10000); } } diff --git a/src/listeners/VoiceStateUpdateEvent.ts b/src/listeners/VoiceStateUpdateEvent.ts index d1dbf51..341c396 100644 --- a/src/listeners/VoiceStateUpdateEvent.ts +++ b/src/listeners/VoiceStateUpdateEvent.ts @@ -40,7 +40,7 @@ export class VoiceStateUpdateEvent extends BaseListener { if (msg.channelId === music.playerChannel) { setTimeout(() => msg.delete().catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)), 5000); } - newState.guild.music.reset(); + await newState.guild.music.reset(); }).catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)); } } catch (e) { diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index 3389177..2a63005 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -17,7 +17,6 @@ export class MusicHandler { public client: BotClient = this.guild.client; public skipVotes: User[] = []; public timeout?: NodeJS.Timeout; - public updateInterval?: NodeJS.Timer; public playerMessage!: Snowflake; public playerChannel!: Snowflake; private _lastMusicMessageID: Snowflake | null = null; @@ -72,12 +71,10 @@ export class MusicHandler { } } - public reset(): void { - void this.updatePlayerEmbed(); + public async reset(): Promise { + await this.updatePlayerEmbed(); if (this.timeout) clearTimeout(this.timeout); - if (this.updateInterval) clearInterval(this.updateInterval); this.timeout = undefined; - this.updateInterval = undefined; this.oldMusicMessage = null; this.oldExceptionMessage = null; this.oldVoiceStateUpdateMessage = null; From 50d9b6a879a67d032ed5e7fa859d85a96b1d18cf Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 11:09:57 +0700 Subject: [PATCH 17/34] feat(NodeRawEvent): shouldn't update every emitted --- src/listeners/NodeRawEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/listeners/NodeRawEvent.ts b/src/listeners/NodeRawEvent.ts index 4a65394..a0b0027 100644 --- a/src/listeners/NodeRawEvent.ts +++ b/src/listeners/NodeRawEvent.ts @@ -4,7 +4,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("nodeRaw", "erela") export class NodeRawEvent extends BaseListener { public async execute(payload: any): Promise { - if (payload?.op === "playerUpdate" && payload.state.connected) { + if (payload?.op === "playerUpdate" && payload.state.connected && this.client.config.enableProgressBar) { const manager = this.client._music.fetch(String(payload.guildId)); if (Boolean(manager)) { await manager.updatePlayerEmbed(); From efba904a38125ce89f46f763ba58480d2293e11a Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 11:12:25 +0700 Subject: [PATCH 18/34] feat(NodeRawEvent): debug when dev mode --- src/listeners/NodeRawEvent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/listeners/NodeRawEvent.ts b/src/listeners/NodeRawEvent.ts index a0b0027..907410b 100644 --- a/src/listeners/NodeRawEvent.ts +++ b/src/listeners/NodeRawEvent.ts @@ -4,6 +4,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("nodeRaw", "erela") export class NodeRawEvent extends BaseListener { public async execute(payload: any): Promise { + if (this.client.config.isDev) this.client.logger.debug(JSON.stringify(payload, null, 4)); if (payload?.op === "playerUpdate" && payload.state.connected && this.client.config.enableProgressBar) { const manager = this.client._music.fetch(String(payload.guildId)); if (Boolean(manager)) { From b3fb91fdb98281033c4c58a31f04af8498da7364 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 17:37:10 +0700 Subject: [PATCH 19/34] chore(config): remove `emojis` --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 51bb7d9..8e6c389 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,7 +32,7 @@ export const leftTimeout = 120000; export const defaultBanner = process.env.DEFAULT_BANNER!; export const databaseName = process.env.DATABASE_NAME; export const enableProgressBar = process.env.ENABLE_PROGRESS_BAR === "yes"; -export const emojis = ["โฏ", "โญ", "๐Ÿ”", "โน", "๐Ÿ”€"]; +// export const emojis = ["โฏ", "โญ", "๐Ÿ”", "โน", "๐Ÿ”€"]; if (typeof databaseName !== "string") throw new Error("config#databaseName must be a string."); if (typeof defaultBanner !== "string") throw new Error("config#defaultBanner must be a string."); From 97c03bf30db6e431ad750237b1a2e73bad6c1080 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 17:37:26 +0700 Subject: [PATCH 20/34] feat(Music): back to interaction button --- src/commands/administrator/SetupCommand.ts | 32 ++++- src/listeners/InteractionCreateEvent.ts | 84 ++++++++++- src/listeners/MessageReactionAddEvent.ts | 159 +++++++++++---------- src/utils/GuildSettingManager.ts | 8 +- 4 files changed, 197 insertions(+), 86 deletions(-) diff --git a/src/commands/administrator/SetupCommand.ts b/src/commands/administrator/SetupCommand.ts index adffef5..6e78942 100644 --- a/src/commands/administrator/SetupCommand.ts +++ b/src/commands/administrator/SetupCommand.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-base-to-string */ -import { GuildChannel, EmojiIdentifierResolvable } from "discord.js"; +import { GuildChannel, MessageActionRow, MessageButton } from "discord.js"; import { BaseCommand } from "../../structures/BaseCommand"; import { CommandContext } from "../../structures/CommandContext"; import { createEmbed } from "../../utils/createEmbed"; @@ -56,9 +56,33 @@ export class SetupCommand extends BaseCommand { .setImage(this.client.config.defaultBanner) .setDescription("Join a voice channel and queue songs by name or url in here.") .setFooter(`Prefix for this server is: ${data.prefix}`) + ], + components: [ + new MessageActionRow() + .addComponents( + new MessageButton() + .setCustomId(this.encode(`player_resumepause`)) + .setEmoji("โฏ") + .setStyle("SECONDARY"), + new MessageButton() + .setCustomId(this.encode(`player_skip`)) + .setEmoji("โญ") + .setStyle("SECONDARY"), + new MessageButton() + .setCustomId(this.encode(`player_loop`)) + .setEmoji("๐Ÿ”") + .setStyle("SECONDARY"), + new MessageButton() + .setCustomId(this.encode(`player_stop`)) + .setEmoji("โน") + .setStyle("DANGER"), + new MessageButton() + .setCustomId(this.encode(`player_shuffle`)) + .setEmoji("๐Ÿ”€") + .setStyle("SUCCESS") + ) ] }); - for (const emoji of this.client.config.emojis) { await msg.react(emoji as EmojiIdentifierResolvable); } ctx.guild!.music.playerMessage = msg.id; ctx.guild!.music.playerChannel = msg.channelId; data.requesterMessage = msg.id; @@ -68,4 +92,8 @@ export class SetupCommand extends BaseCommand { embeds: [createEmbed("info", `Set requester channel to: <#${data.requesterChannel!}>`)] }); } + + public encode(string: string): string { + return Buffer.from(string).toString("base64"); + } } diff --git a/src/listeners/InteractionCreateEvent.ts b/src/listeners/InteractionCreateEvent.ts index ad42f53..37c3cae 100644 --- a/src/listeners/InteractionCreateEvent.ts +++ b/src/listeners/InteractionCreateEvent.ts @@ -1,8 +1,9 @@ -import { Interaction } from "discord.js"; +import { Interaction, VoiceChannel } from "discord.js"; import { BaseListener } from "../structures/BaseListener"; import { CommandContext } from "../structures/CommandContext"; import { createEmbed } from "../utils/createEmbed"; import { DefineListener } from "../utils/decorators/DefineListener"; +import { LoopType } from "../utils/MusicHandler"; @DefineListener("interactionCreate") export class InteractionCreateEvent extends BaseListener { @@ -44,6 +45,87 @@ export class InteractionCreateEvent extends BaseListener { } } } + if (interaction.isButton()) { + const src = this.decode(interaction.customId || ""); + if (src.startsWith("player")) { + const action: "resumepause"|"stop"|"skip"|"loop"|"shuffle" = src.split("_")[1] as any; + const { music } = interaction.guild!; + if (!music.player) { + await interaction.deferReply({ ephemeral: true }); + const msg = await interaction.followUp({ + ephemeral: true, + embeds: [createEmbed("error", "I'm not playing anything right now", true)] + }); + setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); + return undefined; + } + const member = interaction.guild!.members.cache.get(interaction.user.id); + const vc = interaction.guild!.channels.cache.get(member!.voice.channelId!) as VoiceChannel|undefined; + if (!vc) { + await interaction.deferReply({ ephemeral: true }); + const msg = await interaction.followUp({ + ephemeral: true, + embeds: [createEmbed("error", "Please join a voice channel", true)] + }); + setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); + return undefined; + } + if (!vc.joinable) { + await interaction.deferReply({ ephemeral: true }); + const msg = await interaction.followUp({ + ephemeral: true, + embeds: [createEmbed("error", "I can't join your voice channel", true)] + }); + setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); + return undefined; + } + if (interaction.guild!.me!.voice.channelId && interaction.guild!.me!.voice.channelId !== member!.voice.channelId) { + await interaction.deferReply({ ephemeral: true }); + const msg = await interaction.followUp({ + ephemeral: true, + embeds: [createEmbed("error", `I'm already used on ${interaction.guild!.me!.voice.channel!.toString()}`, true)] + }); + setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); + return undefined; + } + const data = await this.client.databases.guilds.get(interaction.guild!.id); + if (data.dj_only && data.dj_role) { + const djRole = interaction.guild!.roles.resolve(data.dj_role); + if (djRole && !member?.roles.cache.has(djRole.id)) { + await interaction.deferReply({ ephemeral: true }); + const msg = await interaction.followUp({ + ephemeral: true, + embeds: [createEmbed("error", `Sorry, but my commands are restricted only for those who has ${djRole.name} role`, true)] + }); + setTimeout(() => this.client.util.convertToMessage(msg).delete().catch(() => null), 5000); + return undefined; + } + } + this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${interaction.user.tag} [${interaction.user.id}] executed "${action}" on ${interaction.guild!.name} [${interaction.guildId}]`); + if (action === "resumepause") { + await music.player.pause(!music.player.paused); + await interaction.deferUpdate(); + await music.updatePlayerEmbed(); + } else if (action === "loop") { + await interaction.deferReply({ ephemeral: true }); + const loopModes = { + [LoopType.ONE]: "track", + [LoopType.ALL]: "queue", + [LoopType.NONE]: "off" + }; + context.args = [loopModes[(music.loopType + 1) as 0|1|2] || loopModes[LoopType.NONE]]; + void this.client.commands.get("loop")!.execute(context); + } else if (action === "stop") { + await music.player.destroy(); + await music.reset(); + await interaction.deferUpdate(); + } else { + await interaction.deferReply({ ephemeral: true }); + const cmd = this.client.commands.find(x => x.meta.name === action); + if (cmd) void cmd.execute(context); + } + } + } } private decode(string: string): string { diff --git a/src/listeners/MessageReactionAddEvent.ts b/src/listeners/MessageReactionAddEvent.ts index 72038b5..4b16c34 100644 --- a/src/listeners/MessageReactionAddEvent.ts +++ b/src/listeners/MessageReactionAddEvent.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { MessageReaction, Message, User, VoiceChannel } from "discord.js"; import { BaseListener } from "../structures/BaseListener"; import { createEmbed } from "../utils/createEmbed"; @@ -8,84 +9,84 @@ import { CommandContext } from "../structures/CommandContext"; @DefineListener("messageReactionAdd") export class MessageReactionAddEvent extends BaseListener { public async execute(messageReaction: MessageReaction, user: User): Promise { - if (!messageReaction.message.guild || user.equals(this.client.user!)) return undefined; - const { message, message: { guild }, message: { guild: { music } } } = messageReaction; - const data = await this.client.databases.guilds.get(guild.id); - if (data.requesterMessage === message.id) { - const emojis = [ - { - emoji: "โฏ", - command: "resumepause" - }, - { - emoji: "โญ", - command: "skip" - }, - { - emoji: "๐Ÿ”", - command: "loop" - }, - { - emoji: "โน", - command: "stop" - }, - { - emoji: "๐Ÿ”€", - command: "shuffle" - } - ]; - await messageReaction.users.remove(user); - if (!music.player) { - const msg = await message.channel.send({ - embeds: [createEmbed("error", "I'm not playing anything right now", true)] - }); - setTimeout(() => msg.delete().catch(() => null), 5000); - return undefined; - } - const member = guild.members.cache.get(user.id)!; - const vc = guild.channels.cache.get(member.voice.channelId!) as VoiceChannel|null; - if (!vc) { - const msg = await message.channel.send({ - embeds: [createEmbed("error", "Please join a voice channel", true)] - }); - setTimeout(() => msg.delete().catch(() => null), 5000); - return undefined; - } - if (guild.me!.voice.channelId && guild.me!.voice.channelId !== member.voice.channelId) { - const msg = await message.channel.send({ - embeds: [createEmbed("error", `I'm already used on ${guild.me!.voice.channel!.toString()}`, true)] - }); - setTimeout(() => msg.delete().catch(() => null), 5000); - return undefined; - } - if (data.dj_only && data.dj_role) { - const djRole = guild.roles.resolve(data.dj_role); - if (djRole && !member.roles.cache.has(djRole.id)) { - const msg = await message.channel.send({ - embeds: [createEmbed("error", `Sorry, but my commands are restricted only for those who has ${djRole.name} role`, true)] - }); - setTimeout(() => msg.delete().catch(() => null), 5000); - return undefined; - } - } - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${user.tag} [${user.id}] executed "${messageReaction.emoji.name!.toString()}" on ${guild.name} [${guild.id}]`); - messageReaction.message.author = user; - const context = new CommandContext(messageReaction.message as Message); - const action = emojis.find(x => x.emoji === messageReaction.emoji.name!.toString()); - if (!action) return; - if (action.command === "resumepause") { - await music.player.pause(!music.player.paused); - return music.updatePlayerEmbed(); - } else if (action.command === "loop") { - const loopModes = { - [LoopType.ONE]: "track", - [LoopType.ALL]: "queue", - [LoopType.NONE]: "off" - }; - context.args = [loopModes[(music.loopType + 1) as 0|1|2] || loopModes[LoopType.NONE]]; - return this.client.commands.get("loop")!.execute(context); - } - return this.client.commands.get(action.command)!.execute(context); - } + // if (!messageReaction.message.guild || user.equals(this.client.user!)) return undefined; + // const { message, message: { guild }, message: { guild: { music } } } = messageReaction; + // const data = await this.client.databases.guilds.get(guild.id); + // if (data.requesterMessage === message.id) { + // const emojis = [ + // { + // emoji: "โฏ", + // command: "resumepause" + // }, + // { + // emoji: "โญ", + // command: "skip" + // }, + // { + // emoji: "๐Ÿ”", + // command: "loop" + // }, + // { + // emoji: "โน", + // command: "stop" + // }, + // { + // emoji: "๐Ÿ”€", + // command: "shuffle" + // } + // ]; + // await messageReaction.users.remove(user); + // if (!music.player) { + // const msg = await message.channel.send({ + // embeds: [createEmbed("error", "I'm not playing anything right now", true)] + // }); + // setTimeout(() => msg.delete().catch(() => null), 5000); + // return undefined; + // } + // const member = guild.members.cache.get(user.id)!; + // const vc = guild.channels.cache.get(member.voice.channelId!) as VoiceChannel|null; + // if (!vc) { + // const msg = await message.channel.send({ + // embeds: [createEmbed("error", "Please join a voice channel", true)] + // }); + // setTimeout(() => msg.delete().catch(() => null), 5000); + // return undefined; + // } + // if (guild.me!.voice.channelId && guild.me!.voice.channelId !== member.voice.channelId) { + // const msg = await message.channel.send({ + // embeds: [createEmbed("error", `I'm already used on ${guild.me!.voice.channel!.toString()}`, true)] + // }); + // setTimeout(() => msg.delete().catch(() => null), 5000); + // return undefined; + // } + // if (data.dj_only && data.dj_role) { + // const djRole = guild.roles.resolve(data.dj_role); + // if (djRole && !member.roles.cache.has(djRole.id)) { + // const msg = await message.channel.send({ + // embeds: [createEmbed("error", `Sorry, but my commands are restricted only for those who has ${djRole.name} role`, true)] + // }); + // setTimeout(() => msg.delete().catch(() => null), 5000); + // return undefined; + // } + // } + // this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${user.tag} [${user.id}] executed "${messageReaction.emoji.name!.toString()}" on ${guild.name} [${guild.id}]`); + // messageReaction.message.author = user; + // const context = new CommandContext(messageReaction.message as Message); + // const action = emojis.find(x => x.emoji === messageReaction.emoji.name!.toString()); + // if (!action) return; + // if (action.command === "resumepause") { + // await music.player.pause(!music.player.paused); + // return music.updatePlayerEmbed(); + // } else if (action.command === "loop") { + // const loopModes = { + // [LoopType.ONE]: "track", + // [LoopType.ALL]: "queue", + // [LoopType.NONE]: "off" + // }; + // context.args = [loopModes[(music.loopType + 1) as 0|1|2] || loopModes[LoopType.NONE]]; + // return this.client.commands.get("loop")!.execute(context); + // } + // return this.client.commands.get(action.command)!.execute(context); + // } } } diff --git a/src/utils/GuildSettingManager.ts b/src/utils/GuildSettingManager.ts index 7c064c9..ff79510 100644 --- a/src/utils/GuildSettingManager.ts +++ b/src/utils/GuildSettingManager.ts @@ -19,10 +19,10 @@ export class GuildSettingManager { const message = await channel.messages.fetch(data.requesterMessage!).catch(() => null); if (message) { this.client.logger.info(`Fetched ${(message.channel as TextChannel).name} [${message.channelId}] on ${message.guild!.name}`); - const cached = message.reactions.cache.filter(x => this.client.config.emojis.includes(x.emoji.name!)); - if (cached.size !== this.client.config.emojis.length) { - for (const e of this.client.config.emojis) await message.react(e); - } + // const cached = message.reactions.cache.filter(x => this.client.config.emojis.includes(x.emoji.name!)); + // if (cached.size !== this.client.config.emojis.length) { + // for (const e of this.client.config.emojis) await message.react(e); + // } guild.music.playerMessage = message.id; guild.music.playerChannel = message.channelId; } else { From 4b80eeda3af445002ae3ed935603ce93747ee5a5 Mon Sep 17 00:00:00 2001 From: Zen <45705890+ZenShibata@users.noreply.github.com> Date: Mon, 22 Nov 2021 20:17:54 +0700 Subject: [PATCH 21/34] feat(Logger): Use pino (#132) * chore(deps): add `pino` & `pino-pretty` * chore(scripts): change `start:dev` to `index.ts` * feat(Logger): Use pino --- package-lock.json | 571 ++++++++++++++++++++++++- package.json | 4 +- src/bot.ts | 27 +- src/commands/developers/EvalCommand.ts | 5 +- src/index.ts | 44 +- src/listeners/NodeRawEvent.ts | 2 +- src/listeners/VoiceStateUpdateEvent.ts | 7 +- src/structures/BotClient.ts | 5 +- src/utils/CommandManager.ts | 11 +- src/utils/CustomError.ts | 5 + src/utils/ListenerLoader.ts | 3 +- src/utils/Logger.ts | 70 ++- src/utils/MusicHandler.ts | 7 +- src/utils/NoStackError.ts | 6 - src/utils/Util.ts | 11 +- 15 files changed, 674 insertions(+), 104 deletions(-) create mode 100644 src/utils/CustomError.ts delete mode 100644 src/utils/NoStackError.ts diff --git a/package-lock.json b/package-lock.json index bb11a5d..040445a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "gorou", "version": "2.14.3", "license": "AGPL-3.0", "dependencies": { @@ -17,6 +16,8 @@ "erela.js-filter": "^0.2.3", "got": "^11.8.2", "mongodb": "^3.7.3", + "pino": "^7.3.0", + "pino-pretty": "^7.2.0", "reflect-metadata": "^0.1.13", "tslib": "^2.3.1", "typeorm": "^0.2.40", @@ -608,6 +609,84 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/args": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", + "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", + "dependencies": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/args/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/args/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/args/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/args/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/args/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -627,6 +706,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -769,6 +856,14 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "engines": { + "node": ">=6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -886,6 +981,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + }, "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -980,6 +1080,14 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -1144,6 +1252,30 @@ "node": ">=10" } }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1492,6 +1624,24 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-redact": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.2.tgz", + "integrity": "sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastify-warning": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz", + "integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -1868,6 +2018,14 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/joycon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", + "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", + "engines": { + "node": ">=10" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1909,6 +2067,14 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2101,6 +2267,14 @@ } } }, + "node_modules/mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2169,6 +2343,11 @@ "node": ">=0.10.0" } }, + "node_modules/on-exit-leak-free": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", + "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2327,6 +2506,84 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pino": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-7.3.0.tgz", + "integrity": "sha512-RHnBlpOc6qxGvwH5PO5THuQLOaq/CT7xWsdmMkd4tG3u6Z0Mp+DENz9eoLh85dBGzwXYoaSTjOYQ0S4aTn5THg==", + "dependencies": { + "fast-redact": "^3.0.0", + "fastify-warning": "^0.2.0", + "get-caller-file": "^2.0.5", + "on-exit-leak-free": "^0.2.0", + "pino-abstract-transport": "v0.5.0", + "pino-std-serializers": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.1.0", + "sonic-boom": "^2.2.1", + "thread-stream": "^0.13.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "dependencies": { + "duplexify": "^4.1.2", + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-7.2.0.tgz", + "integrity": "sha512-pkZhaF1JiyQt4BRqkLANYWuZTxavmuXh3OHsb8goeQasTFgNdzOECXkZWyPYrA0YMRa8zKoVsCzeYz9lI2NYwA==", + "dependencies": { + "args": "^5.0.1", + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-safe-stringify": "^2.0.7", + "joycon": "^3.0.0", + "pino-abstract-transport": "^0.5.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^2.2.0", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pino-std-serializers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", + "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" + }, + "node_modules/pino/node_modules/safe-stable-stringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.2.0.tgz", + "integrity": "sha512-C6AuMdYPuPV/P1leplHNu0lgc2LAElq/g3TdoksDCIVtBhr78o/CH03bt/9SKqugFbKU9CUjsNlCu0fjtQzQUw==", + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2388,6 +2645,11 @@ } ] }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -2418,6 +2680,14 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -2483,6 +2753,11 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2562,6 +2837,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/secure-json-parse": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", + "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -2627,6 +2907,14 @@ "node": ">=8" } }, + "node_modules/sonic-boom": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.3.1.tgz", + "integrity": "sha512-o0vJPsRiCW5Q0EmRKjNiiYGy2DqSXcxk4mY9vIBSPwmkH/e/vJ2Tq8EECd5NTiO77x8vlVN+ykDjRQJTqf7eKg==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2636,6 +2924,14 @@ "memory-pager": "^1.0.2" } }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/spotify-uri": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spotify-uri/-/spotify-uri-2.2.0.tgz", @@ -2665,6 +2961,11 @@ "node": "*" } }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2706,7 +3007,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -2755,6 +3055,14 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.0.tgz", + "integrity": "sha512-kTMZeX4Dzlb1zZ00/01aerGaTw2i8NE4sWF0TvF1uXewRhCiUjCvatQkvxIvFqauWG2ADFS2Wpd3qBeYL9i3dg==", + "dependencies": { + "real-require": "^0.1.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3644,6 +3952,68 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "args": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", + "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", + "requires": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3660,6 +4030,11 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3755,6 +4130,11 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3857,6 +4237,11 @@ "simple-swizzle": "^0.2.2" } }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -3931,6 +4316,11 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.25.0.tgz", "integrity": "sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w==" }, + "dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" + }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -4041,6 +4431,29 @@ "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", "dev": true }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4312,6 +4725,21 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-redact": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.2.tgz", + "integrity": "sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg==" + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fastify-warning": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz", + "integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -4584,6 +5012,11 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "joycon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", + "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==" + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4622,6 +5055,11 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4756,6 +5194,11 @@ "saslprep": "^1.0.0" } }, + "mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4807,6 +5250,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "on-exit-leak-free": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", + "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4927,6 +5375,76 @@ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "pino": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-7.3.0.tgz", + "integrity": "sha512-RHnBlpOc6qxGvwH5PO5THuQLOaq/CT7xWsdmMkd4tG3u6Z0Mp+DENz9eoLh85dBGzwXYoaSTjOYQ0S4aTn5THg==", + "requires": { + "fast-redact": "^3.0.0", + "fastify-warning": "^0.2.0", + "get-caller-file": "^2.0.5", + "on-exit-leak-free": "^0.2.0", + "pino-abstract-transport": "v0.5.0", + "pino-std-serializers": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.1.0", + "sonic-boom": "^2.2.1", + "thread-stream": "^0.13.0" + }, + "dependencies": { + "safe-stable-stringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.2.0.tgz", + "integrity": "sha512-C6AuMdYPuPV/P1leplHNu0lgc2LAElq/g3TdoksDCIVtBhr78o/CH03bt/9SKqugFbKU9CUjsNlCu0fjtQzQUw==" + } + } + }, + "pino-abstract-transport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "requires": { + "duplexify": "^4.1.2", + "split2": "^4.0.0" + } + }, + "pino-pretty": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-7.2.0.tgz", + "integrity": "sha512-pkZhaF1JiyQt4BRqkLANYWuZTxavmuXh3OHsb8goeQasTFgNdzOECXkZWyPYrA0YMRa8zKoVsCzeYz9lI2NYwA==", + "requires": { + "args": "^5.0.1", + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-safe-stringify": "^2.0.7", + "joycon": "^3.0.0", + "pino-abstract-transport": "^0.5.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^2.2.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "pino-std-serializers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", + "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4965,6 +5483,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, "quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -4991,6 +5514,11 @@ } } }, + "real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==" + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -5037,6 +5565,11 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -5079,6 +5612,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "secure-json-parse": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", + "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -5126,6 +5664,14 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "sonic-boom": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.3.1.tgz", + "integrity": "sha512-o0vJPsRiCW5Q0EmRKjNiiYGy2DqSXcxk4mY9vIBSPwmkH/e/vJ2Tq8EECd5NTiO77x8vlVN+ykDjRQJTqf7eKg==", + "requires": { + "atomic-sleep": "^1.0.0" + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -5135,6 +5681,11 @@ "memory-pager": "^1.0.2" } }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, "spotify-uri": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spotify-uri/-/spotify-uri-2.2.0.tgz", @@ -5155,6 +5706,11 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5191,8 +5747,7 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "supports-color": { "version": "7.2.0", @@ -5229,6 +5784,14 @@ "thenify": ">= 3.1.0 < 4" } }, + "thread-stream": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.13.0.tgz", + "integrity": "sha512-kTMZeX4Dzlb1zZ00/01aerGaTw2i8NE4sWF0TvF1uXewRhCiUjCvatQkvxIvFqauWG2ADFS2Wpd3qBeYL9i3dg==", + "requires": { + "real-require": "^0.1.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index e488557..09f9412 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "start": "node dist/index.js", - "start:dev": "ts-node -r dotenv/config src/bot.ts" + "start:dev": "ts-node -r dotenv/config src/index.ts" }, "main": "dist/index.js", "dependencies": { @@ -23,6 +23,8 @@ "erela.js-filter": "^0.2.3", "got": "^11.8.2", "mongodb": "^3.7.3", + "pino": "^7.3.0", + "pino-pretty": "^7.2.0", "reflect-metadata": "^0.1.13", "tslib": "^2.3.1", "typeorm": "^0.2.40", diff --git a/src/bot.ts b/src/bot.ts index 7f5b258..09bee61 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,25 +1,20 @@ import { BotClient } from "./structures/BotClient"; import { clientOptions } from "./config"; -import { NoStackError } from "./utils/NoStackError"; +import { CustomError } from "./utils/CustomError"; export const client = new BotClient(clientOptions); -process.on("exit", code => { - client.logger.info(`NodeJS process exited with code ${code}`); -}); -process.on("uncaughtException", err => { - // Temporary fix for winston crash - if (err.message === "self._addDefaultMeta is not a function") { - return undefined; +process.on("unhandledRejection", e => { + if (e instanceof Error) { + client.logger.error(e); + } else { + client.logger.error(CustomError("PromiseError", e as string)); } - client.logger.error("UNCAUGHT_EXCEPTION:", err); - client.logger.warn("Uncaught Exception detected. Restarting..."); - process.exit(1); }); -process.on("unhandledRejection", reason => { - client.logger.error("UNHANDLED_REJECTION:", (reason as Error).stack ? reason : new NoStackError(reason as string)); +process.on("uncaughtException", e => { + client.logger.fatal(e); + process.exit(1); }); -process.on("warning", client.logger.warn); -client.build(process.env.DISCORD_TOKEN!) - .catch(e => client.logger.error("PROMISE_ERR:", e)); +client.build(process.env.SECRET_DISCORD_TOKEN!) + .catch(e => client.logger.error(e)); diff --git a/src/commands/developers/EvalCommand.ts b/src/commands/developers/EvalCommand.ts index 15c8f1a..56a6615 100644 --- a/src/commands/developers/EvalCommand.ts +++ b/src/commands/developers/EvalCommand.ts @@ -4,6 +4,7 @@ import { MessageEmbed } from "discord.js"; import { request } from "https"; import { DefineCommand } from "../../utils/decorators/DefineCommand"; import { CommandContext } from "../../structures/CommandContext"; +import { CustomError } from "../../utils/CustomError"; @DefineCommand({ aliases: ["ev", "js-exec", "e", "evaluate"], @@ -57,14 +58,14 @@ export class EvalCommand extends BaseCommand { const hastebin = await this.hastebin(output); embed.addField("Output", `${hastebin}.js`); } else { embed.addField("Output", `\`\`\`js\n${output}\`\`\``); } - ctx.send({ embeds: [embed] }).catch(e => this.client.logger.error("PROMISE_ERR:", e)); + ctx.send({ embeds: [embed] }).catch(e => this.client.logger.error(CustomError("PROMISE_ERR:", String(e)))); } catch (e) { const error = this.clean(String(e)); if (error.length > 1024) { const hastebin = await this.hastebin(error); embed.addField("Error", `${hastebin}.js`); } else { embed.setColor("#FF0000").addField("Error", `\`\`\`js\n${error}\`\`\``); } - ctx.send({ embeds: [embed] }).catch(e => this.client.logger.error("PROMISE_ERR:", e)); + ctx.send({ embeds: [embed] }).catch(e => this.client.logger.error(CustomError("PROMISE_ERR:", String(e)))); } } diff --git a/src/index.ts b/src/index.ts index 2cc8174..ef2f9c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,24 +1,34 @@ import "dotenv/config"; -import { ShardingManager } from "discord.js"; import { resolve } from "path"; -import { isProd, shardsCount } from "./config"; +import { ShardingManager } from "discord.js"; import { createLogger } from "./utils/Logger"; +import { shardsCount as totalShards, isDev } from "./config"; +const log = createLogger(`shardingmanager`, "en-US", "manager", undefined, isDev); -const log = createLogger(`shardingmanager`, isProd); - -const manager = new ShardingManager(resolve(__dirname, "bot.js"), { - totalShards: shardsCount, - respawn: true, - token: process.env.DISCORD_TOKEN, - mode: "process" +process.on("uncaughtException", e => { + log.fatal(e); + process.exit(1); }); -manager.on("shardCreate", shard => { - log.info(`[ShardManager] Shard #${shard.id} Spawned.`); - shard.on("disconnect", () => { - log.warn("SHARD_DISCONNECTED: ", { stack: `[ShardManager] Shard #${shard.id} Disconnected` }); - }).on("reconnecting", () => { - log.info(`[ShardManager] Shard #${shard.id} Reconnected.`); +// @ts-expect-error Ignore next line +if (process[Symbol.for("ts-node.register.instance")]) { + log.warn("ts-node detected, sharding is disabled. Please only use ts-node for development purposes."); + require("./bot"); +} else { + const manager = new ShardingManager(resolve(__dirname, "bot.js"), { + totalShards, + mode: "worker", + respawn: true, + token: process.env.SECRET_DISCORD_TOKEN }); - if (manager.shards.size === manager.totalShards) log.info("[ShardManager] All shards spawned successfully."); -}).spawn().catch(e => log.error("SHARD_SPAWN_ERR: ", e)); + + manager.on("shardCreate", shard => { + log.info(`[ShardManager] Shard #${shard.id} Spawned.`); + shard.on("disconnect", () => { + log.warn("SHARD_DISCONNECTED: ", { stack: `[ShardManager] Shard #${shard.id} Disconnected` }); + }).on("reconnecting", () => { + log.info(`[ShardManager] Shard #${shard.id} Reconnected.`); + }); + if (manager.shards.size === manager.totalShards) log.info("[ShardManager] All shards spawned successfully."); + }).spawn().catch(e => log.error(e.status ? `Error while fetching recommended shards: ${e.status}, ${e.statusText}` : e)); +} diff --git a/src/listeners/NodeRawEvent.ts b/src/listeners/NodeRawEvent.ts index 907410b..acf1b3d 100644 --- a/src/listeners/NodeRawEvent.ts +++ b/src/listeners/NodeRawEvent.ts @@ -4,7 +4,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("nodeRaw", "erela") export class NodeRawEvent extends BaseListener { public async execute(payload: any): Promise { - if (this.client.config.isDev) this.client.logger.debug(JSON.stringify(payload, null, 4)); + if (this.client.config.isDev) this.client.logger.debug(payload, `[WS => Lavalink] [${payload.op}]:`); if (payload?.op === "playerUpdate" && payload.state.connected && this.client.config.enableProgressBar) { const manager = this.client._music.fetch(String(payload.guildId)); if (Boolean(manager)) { diff --git a/src/listeners/VoiceStateUpdateEvent.ts b/src/listeners/VoiceStateUpdateEvent.ts index 341c396..93b698c 100644 --- a/src/listeners/VoiceStateUpdateEvent.ts +++ b/src/listeners/VoiceStateUpdateEvent.ts @@ -1,6 +1,7 @@ import { VoiceChannel, VoiceState } from "discord.js"; import { BaseListener } from "../structures/BaseListener"; import { createEmbed } from "../utils/createEmbed"; +import { CustomError } from "../utils/CustomError"; import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("voiceStateUpdate") @@ -38,13 +39,13 @@ export class VoiceStateUpdateEvent extends BaseListener { } music.oldMusicMessage = null; music.oldVoiceStateUpdateMessage = null; if (msg.channelId === music.playerChannel) { - setTimeout(() => msg.delete().catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)), 5000); + setTimeout(() => msg.delete().catch(e => this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e)))), 5000); } await newState.guild.music.reset(); - }).catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)); + }).catch(e => this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e)))); } } catch (e) { - this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e); + this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e))); } } diff --git a/src/structures/BotClient.ts b/src/structures/BotClient.ts index d4e3f24..253ebe4 100644 --- a/src/structures/BotClient.ts +++ b/src/structures/BotClient.ts @@ -17,10 +17,11 @@ import { GuildSettingManager } from "../utils/GuildSettingManager"; import { Connection, createConnection } from "typeorm"; import { GuildSetting } from "../entities/Guild"; import { VoicePacket } from "erela.js"; +import { CustomError } from "../utils/CustomError"; export class BotClient extends Client { public readonly config = config; - public readonly logger = createLogger("bot", this.config.isProd); + public readonly logger = createLogger("main", "en-US", "shard", this.shard?.ids[0], this.config.isDev); public readonly request = got; public readonly music = new Manager({ nodes: this.config.nodes, @@ -60,7 +61,7 @@ export class BotClient extends Client { url: process.env.DATABASE!, useUnifiedTopology: true }).catch(e => { - this.logger.error("MONGODB_CONN_ERR:", e); + this.logger.error(CustomError("MONGODB_CONN_ERR:", String(e))); this.logger.warn("Couldn't connect to database. Exiting..."); return process.exit(1); }).then(async c => { diff --git a/src/utils/CommandManager.ts b/src/utils/CommandManager.ts index dd77e32..c2f5ca8 100644 --- a/src/utils/CommandManager.ts +++ b/src/utils/CommandManager.ts @@ -4,6 +4,7 @@ import { Collection, Snowflake, Message, ApplicationCommandData } from "discord. import { BotClient } from "../structures/BotClient"; import { ICommandComponent, ICategoryMeta } from "../typings"; import { CommandContext } from "../structures/CommandContext"; +import { CustomError } from "./CustomError"; export class CommandManager extends Collection { public isReady = false; @@ -81,11 +82,11 @@ export class CommandManager extends Collection { this.client.logger.info(`Done loading ${data.files.length} commands in ${category} category.`); if (data.disabledCount !== 0) this.client.logger.info(`${data.disabledCount} out of ${data.files.length} commands in ${category} category is disabled.`); }) - .catch(err => this.client.logger.error("CMD_LOADER_ERR:", err)) + .catch(err => this.client.logger.error(CustomError("CMD_LOADER_ERR:", String(err)))) .finally(() => this.client.logger.info(`Done registering ${category} category.`)); } }) - .catch(err => this.client.logger.error("CMD_LOADER_ERR:", err)) + .catch(err => this.client.logger.error(CustomError("CMD_LOADER_ERR:", String(err)))) .finally(() => { this.client.logger.info("All categories has been registered."); this.isReady = true; @@ -107,8 +108,8 @@ export class CommandManager extends Collection { if (now < expirationTime) { const timeLeft = (expirationTime - now) / 1000; message.channel.send(`**${message.author.username}**, please wait **${timeLeft.toFixed(1)}** cooldown time.`).then(msg => { - void msg.delete().then(m => setTimeout(() => m.delete().catch(e => this.client.logger.error("PROMISE_ERR:", e)), 3500)); - }).catch(e => this.client.logger.error("PROMISE_ERR:", e)); + void msg.delete().then(m => setTimeout(() => m.delete().catch(e => this.client.logger.error(CustomError("PROMISE_ERR:", String(e)))), 3500)); + }).catch(e => this.client.logger.error(CustomError("PROMISE_ERR:", String(e)))); return undefined; } @@ -122,7 +123,7 @@ export class CommandManager extends Collection { if (command.meta.devOnly && !this.client.config.devs.includes(message.author.id)) return undefined; return command.execute(new CommandContext(message, args)); } catch (e) { - this.client.logger.error("COMMAND_HANDLER_ERR:", e); + this.client.logger.error(CustomError("COMMAND_HANDLER_ERR:", String(e))); } finally { // eslint-disable-next-line no-unsafe-finally if (command.meta.devOnly && !this.client.config.devs.includes(message.author.id)) return undefined; diff --git a/src/utils/CustomError.ts b/src/utils/CustomError.ts new file mode 100644 index 0000000..8ec4761 --- /dev/null +++ b/src/utils/CustomError.ts @@ -0,0 +1,5 @@ +export function CustomError(name: string, message?: string | undefined): Error { + const error = new Error(message); + error.name = name; + return error; +} diff --git a/src/utils/ListenerLoader.ts b/src/utils/ListenerLoader.ts index 6af011d..633532b 100644 --- a/src/utils/ListenerLoader.ts +++ b/src/utils/ListenerLoader.ts @@ -3,6 +3,7 @@ import { promises as fs } from "fs"; import { parse, resolve } from "path"; import { IListener } from "../typings"; import { BotClient } from "../structures/BotClient"; +import { CustomError } from "./CustomError"; export class ListenerLoader { public constructor(public client: BotClient, public path: string) {} @@ -18,7 +19,7 @@ export class ListenerLoader { if (event.emitter === "erela") this.client.music.addListener(event.name, (...args) => event.execute(...args)); } }) - .catch(err => this.client.logger.error("LISTENER_LOADER_ERR:", err)) + .catch(err => this.client.logger.error(CustomError("LISTENER_LOADER_ERR:", err))) .finally(() => this.client.logger.info("Done loading listeners.")); } diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 474a526..015d1fc 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -1,44 +1,38 @@ -import winston from "winston"; -import { format } from "date-fns"; +/* eslint-disable sort-keys */ +import pino from "pino"; +import { resolve } from "path"; -export function createLogger(serviceName: string, prod = false): winston.Logger { - const logger = winston.createLogger({ - defaultMeta: { - serviceName - }, - format: winston.format.combine( - winston.format.printf(info => { - const { level, message, stack } = info; - const prefix = `[${format(Date.now(), "yyyy-MM-dd HH:mm:ss (x)")}] [${level}]`; - if (["error", "crit"].includes(level)) return `${prefix}: ${stack}`; - return `${prefix}: ${message}`; +export function createLogger(name: string, lang: string, type: "manager" | "shard", shardID?: number, debug = false): pino.Logger { + const dateFormat = Intl.DateTimeFormat(lang, { + year: "numeric", + month: "numeric", + day: "numeric", + hour12: false + }); + const date = formatDate(dateFormat); + const logger = pino({ + name, + timestamp: true, + level: debug ? "debug" : "info", + formatters: { + bindings: () => ({ + pid: type === "shard" ? shardID === undefined ? null : `Shard #${shardID}` : `ShardManager` }) - ), - level: prod ? "info" : "debug", - levels: { - alert: 1, - debug: 5, - error: 0, - info: 4, - notice: 3, - warn: 2 }, - transports: [ - new winston.transports.File({ filename: `logs/${serviceName}/error-${format(Date.now(), "yyyy-MM-dd-HH-mm-ss")}.log`, level: "error" }), - new winston.transports.File({ filename: `logs/${serviceName}/logs-${format(Date.now(), "yyyy-MM-dd-HH-mm-ss")}.log` }) - ] + transport: { + targets: [ + { target: "pino/file", level: "info", options: { destination: resolve(process.cwd(), "logs", `${name}-${date}.log`) } }, + { target: "pino-pretty", level: debug ? "debug" : "info", options: { translateTime: "SYS:yyyy-mm-dd HH:MM:ss.l o" } } + ] + } }); - logger.add(new winston.transports.Console({ - format: winston.format.combine( - winston.format.printf(info => { - const { level, message, stack } = info; - const prefix = `[${format(Date.now(), "yyyy-MM-dd HH:mm:ss (x)")}] [${level}]`; - if (["error", "alert"].includes(level) && !prod) return `${prefix}: ${stack}`; - return `${prefix}: ${message}`; - }), - winston.format.align(), - prod ? winston.format.colorize({ all: false }) : winston.format.colorize({ all: true }) - ) - })); return logger; } + +function formatDate(dateFormat: Intl.DateTimeFormat, date: number | Date = new Date()): string { + const data = dateFormat.formatToParts(date); + return `--` + .replace(//g, data.find(d => d.type === "year")!.value) + .replace(//g, data.find(d => d.type === "month")!.value) + .replace(//g, data.find(d => d.type === "day")!.value); +} diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index 2a63005..419ecf9 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -4,6 +4,7 @@ import { readableTime } from "./readableTime"; import { Node, Player, TrackUtils } from "erela.js"; import { createEmbed } from "./createEmbed"; import { chunk } from "./chunk"; +import { CustomError } from "./CustomError"; export enum LoopType { NONE, @@ -159,7 +160,7 @@ export class MusicHandler { if (textChannel?.isText()) { textChannel.messages.fetch(this._lastExceptionMessageID, { cache: false }) .then(m => m.delete()) - .catch(e => this.client.logger.error("DELETE_OLD_EXCEPTION_MESSAGE_ERR:", e)); + .catch(e => this.client.logger.error(CustomError("DELETE_OLD_EXCEPTION_MESSAGE_ERR:", String(e)))); } } this._lastExceptionMessageID = id; @@ -175,7 +176,7 @@ export class MusicHandler { if (textChannel?.isText()) { textChannel.messages.fetch(this._lastMusicMessageID, { cache: false }) .then(m => m.delete()) - .catch(e => this.client.logger.error("DELETE_OLD_MUSIC_MESSAGE_ERR:", e)); + .catch(e => this.client.logger.error(CustomError("DELETE_OLD_MUSIC_MESSAGE_ERR:", String(e)))); } } this._lastMusicMessageID = id; @@ -191,7 +192,7 @@ export class MusicHandler { if (textChannel?.isText()) { textChannel.messages.fetch(this._lastVoiceStateUpdateMessageID, { cache: false }) .then(m => m.delete()) - .catch(e => this.client.logger.error("DELETE_OLD_VOICE_STATE_UPDATE_MESSAGE_ERR:", e)); + .catch(e => this.client.logger.error(CustomError("DELETE_OLD_VOICE_STATE_UPDATE_MESSAGE_ERR:", String(e)))); } } this._lastVoiceStateUpdateMessageID = id; diff --git a/src/utils/NoStackError.ts b/src/utils/NoStackError.ts deleted file mode 100644 index c5bdf82..0000000 --- a/src/utils/NoStackError.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class NoStackError extends Error { - public constructor(message?: string | undefined) { - super(message); - this.stack = this.message; - } -} diff --git a/src/utils/Util.ts b/src/utils/Util.ts index e7cde7a..68ec5df 100644 --- a/src/utils/Util.ts +++ b/src/utils/Util.ts @@ -5,6 +5,7 @@ import { formatMS } from "./formatMS"; import { MusicHandler } from "./MusicHandler"; import { APIMessage } from "discord-api-types/v9"; import { resolve } from "path/posix"; +import { CustomError } from "./CustomError"; export class Util { public constructor(public client: BotClient) {} @@ -108,7 +109,7 @@ export class Util { createEmbed("error", `**${duration}** have passed and there is no one who joins my voice channel, the queue was deleted.`) .setTitle("โน Queue deleted.") ] - }).catch(e => { this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e); return null; }) + }).catch(e => { this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e))); return null; }) .then(async msg => { if (msg?.channelId === music.playerChannel) { const ch = music.guild.channels.cache.get(music.playerChannel); @@ -116,7 +117,7 @@ export class Util { const old = await ch.messages.fetch(music.oldVoiceStateUpdateMessage!, { cache: false }).catch(() => null); if (old) old.delete().catch(() => null); } - setTimeout(() => msg.delete().catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)), 5000); + setTimeout(() => msg.delete().catch(e => this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e)))), 5000); } await music.reset(); music.oldVoiceStateUpdateMessage = null; @@ -131,9 +132,9 @@ export class Util { `If there's no one who joins my voice channel in the next **${duration}**, the queue will be deleted.`) .setTitle("โธ Queue paused.") ] - }).then(msg => music.oldVoiceStateUpdateMessage = msg.id).catch(e => this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e)); + }).then(msg => music.oldVoiceStateUpdateMessage = msg.id).catch(e => this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e)))); } - } catch (e) { this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e); } + } catch (e) { this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e))); } } public resumeTimeout(vcMembers: GuildMember[], music: MusicHandler): any { @@ -157,7 +158,7 @@ export class Util { music.oldVoiceStateUpdateMessage = null; } music.player?.pause(false); - } catch (e) { this.client.logger.error("VOICE_STATE_UPDATE_EVENT_ERR:", e); } + } catch (e) { this.client.logger.error(CustomError("VOICE_STATE_UPDATE_EVENT_ERR:", String(e))); } } } From 44bf3087506e56a4c3c9ea5119769ef0bfd830bb Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 20:21:12 +0700 Subject: [PATCH 22/34] feat(Logger): remove shard thingy from old logger --- src/listeners/InteractionCreateEvent.ts | 2 +- src/listeners/TrackEndEvent.ts | 2 +- src/listeners/TrackStartEvent.ts | 2 +- src/listeners/VoiceStateUpdateEvent.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/listeners/InteractionCreateEvent.ts b/src/listeners/InteractionCreateEvent.ts index 37c3cae..a8ae7e0 100644 --- a/src/listeners/InteractionCreateEvent.ts +++ b/src/listeners/InteractionCreateEvent.ts @@ -101,7 +101,7 @@ export class InteractionCreateEvent extends BaseListener { return undefined; } } - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} ${interaction.user.tag} [${interaction.user.id}] executed "${action}" on ${interaction.guild!.name} [${interaction.guildId}]`); + this.client.logger.info(`${interaction.user.tag} [${interaction.user.id}] executed "${action}" on ${interaction.guild!.name} [${interaction.guildId}]`); if (action === "resumepause") { await music.player.pause(!music.player.paused); await interaction.deferUpdate(); diff --git a/src/listeners/TrackEndEvent.ts b/src/listeners/TrackEndEvent.ts index 3ce0fa2..da85ab8 100644 --- a/src/listeners/TrackEndEvent.ts +++ b/src/listeners/TrackEndEvent.ts @@ -10,6 +10,6 @@ export class TrackEndEvent extends BaseListener { manager.oldMusicMessage = null; } await manager.updatePlayerEmbed(); - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} Track: "${track.title}" on ${manager.guild.name} ended`); + this.client.logger.info(`Track: "${track.title}" on ${manager.guild.name} ended`); } } diff --git a/src/listeners/TrackStartEvent.ts b/src/listeners/TrackStartEvent.ts index 8ae553f..52a93c9 100644 --- a/src/listeners/TrackStartEvent.ts +++ b/src/listeners/TrackStartEvent.ts @@ -8,7 +8,7 @@ export class TrackStartEvent extends BaseListener { public async execute(player: Player, track: Track): Promise { const manager = this.client._music.fetch(player.guild); const channel = this.client.channels.cache.get(player.textChannel!); - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} Track: "${track.title}" on ${manager.guild.name} started`); + this.client.logger.info(`Track: "${track.title}" on ${manager.guild.name} started`); await manager.updatePlayerEmbed(); if (channel?.isText() && !manager.playerMessage) { const msg = await channel.send({ diff --git a/src/listeners/VoiceStateUpdateEvent.ts b/src/listeners/VoiceStateUpdateEvent.ts index 93b698c..47a74e0 100644 --- a/src/listeners/VoiceStateUpdateEvent.ts +++ b/src/listeners/VoiceStateUpdateEvent.ts @@ -23,7 +23,7 @@ export class VoiceStateUpdateEvent extends BaseListener { if (oldMember?.id === botID && oldState.channelId === queueVC.id && !newState.channelId) { try { newState.guild.music.player?.destroy(); - this.client.logger.info(`${this.client.shard ? `[Shard #${this.client.shard.ids[0]}]` : ""} Disconnected from the voice channel at ${newState.guild.name}, the queue was deleted.`); + this.client.logger.info(`Disconnected from the voice channel at ${newState.guild.name}, the queue was deleted.`); if (textChannel?.isText()) { textChannel.send({ embeds: [ From 2249b48f14a95b54b52f17537415fae6647fadb3 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 20:23:24 +0700 Subject: [PATCH 23/34] refactor(CommandContext): remove useless space --- src/structures/CommandContext.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/structures/CommandContext.ts b/src/structures/CommandContext.ts index 63782cf..1551b97 100644 --- a/src/structures/CommandContext.ts +++ b/src/structures/CommandContext.ts @@ -3,7 +3,6 @@ import { ButtonInteraction, Collection, CommandInteraction, CommandInteractionOp import { MessageInteractionAction } from "../typings"; import { InteractionTypes, MessageComponentTypes } from "../typings/enum"; - export class CommandContext { public additionalArgs: Collection = new Collection(); public channel: TextBasedChannels|null = this.context.channel; From bf16a422b9ad42d4d41b511c62b97717fb6b70f9 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:06:26 +0700 Subject: [PATCH 24/34] feat(Global): extend string prototype --- src/extension/Global.ts | 9 +++++++++ src/typings/index.d.ts | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 src/extension/Global.ts diff --git a/src/extension/Global.ts b/src/extension/Global.ts new file mode 100644 index 0000000..fadbdb7 --- /dev/null +++ b/src/extension/Global.ts @@ -0,0 +1,9 @@ +import { Util } from "discord.js"; + +globalThis.String.prototype.toProperCase = function toProperCase() { + return this.replace(/([^\W_]+[^\s-]*) */g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); +}; + +globalThis.String.prototype.escapeMarkdown = function escapeMarkdown() { + return Util.escapeMarkdown(this.toString()); +}; diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index d1cf1f9..73a005d 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -59,3 +59,10 @@ declare module "discord.js" { music: MusicHandler; } } + +declare global { + interface String { + toProperCase(): string; + escapeMarkdown(): string; + } +} From c0c803fd563682be099712b6d0d51c01cf85a421 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:09:44 +0700 Subject: [PATCH 25/34] feat(Music): escape the title --- src/commands/music/PlayCommand.ts | 6 +++--- src/commands/music/QueueCommand.ts | 6 +++--- src/commands/music/RemoveCommand.ts | 2 +- src/commands/music/SearchCommand.ts | 2 +- src/commands/music/SkipCommand.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/commands/music/PlayCommand.ts b/src/commands/music/PlayCommand.ts index 065c178..62d666c 100644 --- a/src/commands/music/PlayCommand.ts +++ b/src/commands/music/PlayCommand.ts @@ -141,7 +141,7 @@ export class PlayCommand extends BaseCommand { if (duplicateSong && duplicated) { const duplicateMessage = await ctx.send({ embeds: [ - createEmbed("warn", `Track **[${duplicated.title}](${duplicated.uri!})** is already queued, and this server configuration disallow duplicated tracks in queue`) + createEmbed("warn", `Track **[${duplicated.title.escapeMarkdown()}](${duplicated.uri!})** is already queued, and this server configuration disallow duplicated tracks in queue`) ] }); if (fromRequester) { @@ -154,11 +154,11 @@ export class PlayCommand extends BaseCommand { if (!ctx.additionalArgs.get("values") && (!ctx.additionalArgs.get("fromRequesterChannel"))) { if (fromRequester && ctx.isInteraction()) { await ctx.send({ - embeds: [createEmbed("info", `โœ… Track **[${response.tracks[0].title}](${response.tracks[0].uri})** has been added to the queue`).setThumbnail(response.tracks[0].thumbnail!)] + embeds: [createEmbed("info", `โœ… Track **[${response.tracks[0].title.escapeMarkdown()}](${response.tracks[0].uri})** has been added to the queue`).setThumbnail(response.tracks[0].thumbnail!)] }); } else if (!fromRequester) { await ctx.send({ - embeds: [createEmbed("info", `โœ… Track **[${response.tracks[0].title}](${response.tracks[0].uri})** has been added to the queue`).setThumbnail(response.tracks[0].thumbnail!)] + embeds: [createEmbed("info", `โœ… Track **[${response.tracks[0].title.escapeMarkdown()}](${response.tracks[0].uri})** has been added to the queue`).setThumbnail(response.tracks[0].thumbnail!)] }); } } diff --git a/src/commands/music/QueueCommand.ts b/src/commands/music/QueueCommand.ts index c347c06..71b8569 100644 --- a/src/commands/music/QueueCommand.ts +++ b/src/commands/music/QueueCommand.ts @@ -21,18 +21,18 @@ export class QueueCommand extends BaseCommand { if (ctx.isInteraction() && !ctx.deferred) await ctx.deferReply(); const { music } = ctx.guild!; const queue = music.player?.queue ?? []; - const pages = chunk(queue.map((x, i) => `**${++i}.** **[${x.title}](${x.uri!})** <@${String(x.requester)}>`), 10).map(x => x.join("\n")); + const pages = chunk(queue.map((x, i) => `**${++i}.** **[${x.title.escapeMarkdown()}](${x.uri!})** <@${String(x.requester)}>`), 10).map(x => x.join("\n")); const embed = createEmbed("info", pages[0] || "Empty, add some by using `play` command", false) .setAuthor(`${ctx.guild!.name} Queue`, ctx.guild!.iconURL({ dynamic: true, size: 4096 })!); const msg = await ctx.send({ embeds: [embed], - content: music.player?.queue.current ? `โ–ถ **Now playing: __${music.player.queue.current.title}__**` : null + content: music.player?.queue.current ? `โ–ถ **Now playing: __${music.player.queue.current.title.escapeMarkdown()}__**` : null }); if (pages.length) embed.setFooter(`Page 1 of ${pages.length}.`); if (pages.length > 1) { const pagination = new ButtonPagination(msg, { author: ctx.author.id, - content: music.player?.queue.current ? `โ–ถ **Now playing: __${music.player.queue.current.title}__**` : "", + content: music.player?.queue.current ? `โ–ถ **Now playing: __${music.player.queue.current.title.escapeMarkdown()}__**` : "", edit: (i, emb, page): MessageEmbed => emb.setDescription(page).setFooter(`Page ${i + 1} of ${pages.length}`), embed, pages }); diff --git a/src/commands/music/RemoveCommand.ts b/src/commands/music/RemoveCommand.ts index 126167b..8c50f83 100644 --- a/src/commands/music/RemoveCommand.ts +++ b/src/commands/music/RemoveCommand.ts @@ -37,7 +37,7 @@ export class RemoveCommand extends BaseCommand { }); } const removed = music.player!.queue.splice(position - 1, 1)[0]; - const m = await ctx.send({ embeds: [createEmbed("info", `Removed **[${removed.title}](${removed.uri!})** from the queue!`)] }); + const m = await ctx.send({ embeds: [createEmbed("info", `Removed **[${removed.title.escapeMarkdown()}](${removed.uri!})** from the queue!`)] }); if (ctx.channel!.id === ctx.guild!.music.playerChannel) { setTimeout(() => m.delete().catch(() => null), 5000); } diff --git a/src/commands/music/SearchCommand.ts b/src/commands/music/SearchCommand.ts index acfb3be..335c783 100644 --- a/src/commands/music/SearchCommand.ts +++ b/src/commands/music/SearchCommand.ts @@ -156,7 +156,7 @@ export class SearchCommand extends BaseCommand { const emojis = ["1๏ธโƒฃ", "2๏ธโƒฃ", "3๏ธโƒฃ", "4๏ธโƒฃ", "5๏ธโƒฃ", "6๏ธโƒฃ", "7๏ธโƒฃ", "8๏ธโƒฃ", "9๏ธโƒฃ", "๐Ÿ”Ÿ"]; return tracks.slice(0, 10).map((x, i) => ( { - label: x.title.length > 98 ? `${x.title.substr(0, 97)}...` : x.title, + label: x.title.escapeMarkdown().length > 98 ? `${x.title.escapeMarkdown().substr(0, 97)}...` : x.title.escapeMarkdown(), emoji: emojis[i], description: `${x.author} ยท ${readableTime(x.duration)}`, value: x.uri diff --git a/src/commands/music/SkipCommand.ts b/src/commands/music/SkipCommand.ts index 60cff2c..460760f 100644 --- a/src/commands/music/SkipCommand.ts +++ b/src/commands/music/SkipCommand.ts @@ -52,7 +52,7 @@ export class SkipCommand extends BaseCommand { } const msg = await ctx.send({ embeds: [ - createEmbed("info", `Skipped **[${music.player!.queue.current!.title}](${music.player!.queue.current!.uri!})**`, true) + createEmbed("info", `Skipped **[${music.player!.queue.current!.title.escapeMarkdown()}](${music.player!.queue.current!.uri!})**`, true) ] }); if (music.playerChannel === ctx.context.channelId) { From 93587378a5f46f5a081ef3f58232f25fde4caa53 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:09:57 +0700 Subject: [PATCH 26/34] feat: import global extension --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index ef2f9c1..8af4e04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import "dotenv/config"; +import "./extension/Global"; import { resolve } from "path"; import { ShardingManager } from "discord.js"; import { createLogger } from "./utils/Logger"; From f55a470506b85508a4e2ccdde8607a08877f3040 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:10:21 +0700 Subject: [PATCH 27/34] feat(extension): use `BotClient#queue` --- src/extension/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/index.ts b/src/extension/index.ts index b9d0cdd..086d8fc 100644 --- a/src/extension/index.ts +++ b/src/extension/index.ts @@ -4,6 +4,6 @@ import { Guild } from "discord.js"; Reflect.defineProperty(Guild.prototype, "music", { get() { // @ts-expect-error-next-line - return this.client._music.fetch(this.id); + return this.client.queue.fetch(this.id); } }); From ee7b7d3d9b13e025a3532effbf6c4a60af37753d Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:11:32 +0700 Subject: [PATCH 28/34] feat(*): Use `BotClient#queue` instead --- src/listeners/MessageCreateEvent.ts | 4 +-- src/listeners/NodeRawEvent.ts | 2 +- src/listeners/QueueEndEvent.ts | 2 +- src/listeners/TrackEndEvent.ts | 2 +- src/listeners/TrackErrorEvent.ts | 2 +- src/listeners/TrackStartEvent.ts | 4 +-- src/structures/BotClient.ts | 14 +++++----- src/utils/GuildSettingManager.ts | 4 --- src/utils/MusicHandler.ts | 5 ++-- src/utils/Util.ts | 4 +-- src/utils/decorators/MusicHelpers.ts | 38 ++++++++++++++-------------- 11 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/listeners/MessageCreateEvent.ts b/src/listeners/MessageCreateEvent.ts index 67ff738..d25faf7 100644 --- a/src/listeners/MessageCreateEvent.ts +++ b/src/listeners/MessageCreateEvent.ts @@ -8,7 +8,7 @@ export class MessageCreateEvent extends BaseListener { public async execute(message: Message): Promise { if (message.channel.type === "DM" || !this.client.commands.isReady) return message; const data = await this.client.databases.guilds.get(message.guild!.id); - if (message.channelId === message.guild!.music.playerChannel) { + if (message.channelId === data.requesterChannel) { if (message.deletable && message.author.id !== this.client.user!.id) await message.delete().catch(() => null); if ((!message.content.startsWith(data.prefix) && !message.content.startsWith(this.client.config.prefix)) && !message.author.bot) { this.client.logger.info(`${message.author.tag} [${message.author.id}] is using play command on ${message.guild!.name}`); @@ -18,7 +18,7 @@ export class MessageCreateEvent extends BaseListener { if (message.author.bot) return; if (message.content.startsWith(data.prefix) || message.content.startsWith(this.client.config.prefix)) void this.client.commands.handle(message, message.content.startsWith(this.client.config.prefix) ? this.client.config.prefix : data.prefix); - if ((await this.getUserFromMention(message.content))?.id === this.client.user?.id && message.channelId !== message.guild!.music.playerChannel) { + if ((await this.getUserFromMention(message.content))?.id === this.client.user?.id && message.channelId !== data.requesterChannel) { message.channel.send({ embeds: [ new MessageEmbed() diff --git a/src/listeners/NodeRawEvent.ts b/src/listeners/NodeRawEvent.ts index acf1b3d..a1f60b4 100644 --- a/src/listeners/NodeRawEvent.ts +++ b/src/listeners/NodeRawEvent.ts @@ -6,7 +6,7 @@ export class NodeRawEvent extends BaseListener { public async execute(payload: any): Promise { if (this.client.config.isDev) this.client.logger.debug(payload, `[WS => Lavalink] [${payload.op}]:`); if (payload?.op === "playerUpdate" && payload.state.connected && this.client.config.enableProgressBar) { - const manager = this.client._music.fetch(String(payload.guildId)); + const manager = this.client.queue.fetch(String(payload.guildId)); if (Boolean(manager)) { await manager.updatePlayerEmbed(); } diff --git a/src/listeners/QueueEndEvent.ts b/src/listeners/QueueEndEvent.ts index 5e018df..7424aea 100644 --- a/src/listeners/QueueEndEvent.ts +++ b/src/listeners/QueueEndEvent.ts @@ -6,7 +6,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("queueEnd", "erela") export class QueueEndEvent extends BaseListener { public async execute(player: Player): Promise { - const manager = this.client._music.fetch(player.guild); + const manager = this.client.queue.fetch(player.guild); const channel = this.client.channels.cache.get(player.textChannel!); if (channel?.isText() && !manager.playerMessage) { await channel.send({ diff --git a/src/listeners/TrackEndEvent.ts b/src/listeners/TrackEndEvent.ts index da85ab8..00c74d8 100644 --- a/src/listeners/TrackEndEvent.ts +++ b/src/listeners/TrackEndEvent.ts @@ -5,7 +5,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("trackEnd", "erela") export class TrackEndEvent extends BaseListener { public async execute(player: Player, track: Track): Promise { - const manager = this.client._music.fetch(player.guild); + const manager = this.client.queue.fetch(player.guild); if (manager.oldMusicMessage) { manager.oldMusicMessage = null; } diff --git a/src/listeners/TrackErrorEvent.ts b/src/listeners/TrackErrorEvent.ts index b27d250..52446d8 100644 --- a/src/listeners/TrackErrorEvent.ts +++ b/src/listeners/TrackErrorEvent.ts @@ -6,7 +6,7 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("trackError", "erela") export class TrackErrorEvent extends BaseListener { public async execute(player: Player, track: Track, payload: TrackExceptionEvent): Promise { - const manager = this.client._music.fetch(player.guild); + const manager = this.client.queue.fetch(player.guild); if (manager.oldExceptionMessage) { manager.oldExceptionMessage = null; } diff --git a/src/listeners/TrackStartEvent.ts b/src/listeners/TrackStartEvent.ts index 52a93c9..12ea217 100644 --- a/src/listeners/TrackStartEvent.ts +++ b/src/listeners/TrackStartEvent.ts @@ -6,13 +6,13 @@ import { DefineListener } from "../utils/decorators/DefineListener"; @DefineListener("trackStart", "erela") export class TrackStartEvent extends BaseListener { public async execute(player: Player, track: Track): Promise { - const manager = this.client._music.fetch(player.guild); + const manager = this.client.queue.fetch(player.guild); const channel = this.client.channels.cache.get(player.textChannel!); this.client.logger.info(`Track: "${track.title}" on ${manager.guild.name} started`); await manager.updatePlayerEmbed(); if (channel?.isText() && !manager.playerMessage) { const msg = await channel.send({ - embeds: [createEmbed("info", `โ–ถ Start playing: **[${track.title}](${track.uri})**`).setThumbnail(track.thumbnail!)] + embeds: [createEmbed("info", `โ–ถ Start playing: **[${track.title.escapeMarkdown()}](${track.uri})**`).setThumbnail(track.thumbnail!)] }); manager.oldMusicMessage = msg.id; } diff --git a/src/structures/BotClient.ts b/src/structures/BotClient.ts index 253ebe4..e0ef838 100644 --- a/src/structures/BotClient.ts +++ b/src/structures/BotClient.ts @@ -1,28 +1,29 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ +import * as config from "../config"; import { Client, ClientOptions } from "discord.js"; -import got from "got"; import { resolve } from "path"; -import * as config from "../config"; import { CommandManager } from "../utils/CommandManager"; import { createLogger } from "../utils/Logger"; import { formatMS } from "../utils/formatMS"; import { ListenerLoader } from "../utils/ListenerLoader"; -import { MusicManager } from "../utils/MusicManager"; -import "../extension"; import { Manager } from "./Manager"; -import Spotify from "better-erela.js-spotify"; -import Filters from "erela.js-filter"; import { Util } from "../utils/Util"; import { GuildSettingManager } from "../utils/GuildSettingManager"; import { Connection, createConnection } from "typeorm"; import { GuildSetting } from "../entities/Guild"; import { VoicePacket } from "erela.js"; import { CustomError } from "../utils/CustomError"; +import { MusicManager } from "../utils/MusicManager"; +import got from "got"; +import Spotify from "better-erela.js-spotify"; +import Filters from "erela.js-filter"; +import "../extension"; export class BotClient extends Client { public readonly config = config; public readonly logger = createLogger("main", "en-US", "shard", this.shard?.ids[0], this.config.isDev); public readonly request = got; + public readonly queue = new MusicManager(this); public readonly music = new Manager({ nodes: this.config.nodes, send: (id: string, payload: any) => { @@ -38,7 +39,6 @@ export class BotClient extends Client { public readonly commands = new CommandManager(this, resolve(__dirname, "..", "commands")); // @ts-expect-error override public readonly listeners = new ListenerLoader(this, resolve(__dirname, "..", "listeners")); - public readonly _music = new MusicManager(this); public readonly databases = { guilds: new GuildSettingManager(this) }; diff --git a/src/utils/GuildSettingManager.ts b/src/utils/GuildSettingManager.ts index ff79510..2e0517e 100644 --- a/src/utils/GuildSettingManager.ts +++ b/src/utils/GuildSettingManager.ts @@ -19,10 +19,6 @@ export class GuildSettingManager { const message = await channel.messages.fetch(data.requesterMessage!).catch(() => null); if (message) { this.client.logger.info(`Fetched ${(message.channel as TextChannel).name} [${message.channelId}] on ${message.guild!.name}`); - // const cached = message.reactions.cache.filter(x => this.client.config.emojis.includes(x.emoji.name!)); - // if (cached.size !== this.client.config.emojis.length) { - // for (const e of this.client.config.emojis) await message.react(e); - // } guild.music.playerMessage = message.id; guild.music.playerChannel = message.channelId; } else { diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index 419ecf9..d545cbb 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -33,13 +33,13 @@ export class MusicHandler { }; if (this.playerMessage) { if (this.player?.queue && this.player.queue.totalSize) { - const list = chunk(this.player.queue.map((x, i) => `${++i}. ${x.author!} - ${x.title} [${readableTime(x.duration!)}] ~ <@${x.requester as any}>`), 10); + const list = chunk(this.player.queue.map((x, i) => `${++i}. ${x.author!} - ${x.title.escapeMarkdown()} [${readableTime(x.duration!)}] ~ <@${x.requester as any}>`), 10); const currentSong = this.player.queue.current!; // @ts-expect-error-next-line const display = TrackUtils.isUnresolvedTrack(currentSong) ? currentSong.thumbnail ?? this.client.config.defaultBanner : currentSong.displayThumbnail("maxresdefault") || this.client.config.defaultBanner; const image = await this.client.request.get(display).then(() => display).catch(() => this.client.config.defaultBanner); const embed = createEmbed("info") - .setTitle(`**${this.player.queue.current!.title}**`) + .setTitle(`**${this.player.queue.current!.title.escapeMarkdown()}**`) .setURL(this.player.queue.current!.uri!) .setDescription(`Requested by: <@${String(this.player.queue.current!.requester)}>`) .setImage(image) @@ -80,6 +80,7 @@ export class MusicHandler { this.oldExceptionMessage = null; this.oldVoiceStateUpdateMessage = null; this.skipVotes = []; + this.client.queue.cache.delete(this.guild.id); return undefined; } diff --git a/src/utils/Util.ts b/src/utils/Util.ts index 68ec5df..527004b 100644 --- a/src/utils/Util.ts +++ b/src/utils/Util.ts @@ -32,7 +32,7 @@ export class Util { users: (client: BotClient) => client.users.cache, channels: (client: BotClient) => client.channels.cache, guilds: (client: BotClient) => client.guilds.cache, - queues: (client: BotClient) => client._music.cache.mapValues(v => v) + queues: (client: BotClient) => client.queue.cache.mapValues(v => v) }; /* @@ -146,7 +146,7 @@ export class Util { music.timeout = undefined; const song = music.player!.queue.current; if (textChannel?.isText() && textChannel.id !== music.playerChannel) { - const embed = createEmbed("info", `Someone joins the voice channel. Enjoy the music ๐ŸŽถ\nNow Playing: **[${song!.title}](${(song as any).url})**`) + const embed = createEmbed("info", `Someone joins the voice channel. Enjoy the music ๐ŸŽถ\nNow Playing: **[${song!.title.escapeMarkdown()}](${(song as any).url})**`) .setTitle("โ–ถ Queue resumed"); // @ts-expect-error-next-line const thumbnail = song?.displayThumbnail("maxresdefault"); diff --git a/src/utils/decorators/MusicHelpers.ts b/src/utils/decorators/MusicHelpers.ts index 892a6aa..99f90b7 100644 --- a/src/utils/decorators/MusicHelpers.ts +++ b/src/utils/decorators/MusicHelpers.ts @@ -2,32 +2,32 @@ import { inhibit } from "./Inhibit"; import { VoiceChannel } from "discord.js"; export function isMusicPlaying(): any { - return inhibit(msg => { - if (!msg.guild!.music.player?.queue.current) return "I'm not playing anything right now"; + return inhibit(ctx => { + if (!ctx.guild!.music.player?.queue.current) return "I'm not playing anything right now"; }); } export function isSameVoiceChannel(): any { - return inhibit(msg => { - if (msg.guild!.me!.voice.channelId && msg.guild!.me!.voice.channelId !== msg.member!.voice.channelId) { - return `I'm already used on ${msg.guild!.me!.voice.channel!.toString()}`; + return inhibit(ctx => { + if (ctx.guild!.me!.voice.channelId && ctx.guild!.me!.voice.channelId !== ctx.member!.voice.channelId) { + return `I'm already used on ${ctx.guild!.me!.voice.channel!.toString()}`; } }); } export function isMemberInVoiceChannel(): any { - return inhibit(msg => { - if (!msg.member!.voice.channelId) { + return inhibit(ctx => { + if (!ctx.member!.voice.channelId) { return "Please join a voice channel"; } }); } export function isMemberVoiceChannelJoinable(ignoreWhenSame = true): any { - return inhibit(msg => { - const vc = msg.guild?.channels.cache.get(msg.member!.voice.channelId!) as VoiceChannel|null; - if (ignoreWhenSame && msg.guild!.me!.voice.channelId && msg.guild!.me!.voice.channelId === msg.member!.voice.channelId) return undefined; - if (!vc?.permissionsFor(msg.guild!.me!)!.has(["CONNECT", "SPEAK"])) { + return inhibit(ctx => { + const vc = ctx.guild?.channels.cache.get(ctx.member!.voice.channelId!) as VoiceChannel|null; + if (ignoreWhenSame && ctx.guild!.me!.voice.channelId && ctx.guild!.me!.voice.channelId === ctx.member!.voice.channelId) return undefined; + if (!vc?.permissionsFor(ctx.guild!.me!)!.has(["CONNECT", "SPEAK"])) { return "I'm missing `CONNECT` or `SPEAK` permission in your voice!"; } if (!vc.joinable) return "I can't join your voice channel"; @@ -35,27 +35,27 @@ export function isMemberVoiceChannelJoinable(ignoreWhenSame = true): any { } export function isInStream(): any { - return inhibit(msg => { - if (msg.guild!.music.player?.queue.current?.isStream) { + return inhibit(ctx => { + if (ctx.guild!.music.player?.queue.current?.isStream) { return "Try to stop the stream first"; } }); } export function isHasQueue(): any { - return inhibit(msg => { - if (!msg.guild!.music.player!.queue.length) { + return inhibit(ctx => { + if (!ctx.guild!.music.player!.queue.length) { return "This guild has no queue"; } }); } export function isMemberDJ(): any { - return inhibit(async msg => { - const data = await msg.client.databases.guilds.get(msg.guild!.id); + return inhibit(async ctx => { + const data = await ctx.client.databases.guilds.get(ctx.guild!.id); if (data.dj_only && data.dj_role) { - const djRole = msg.guild!.roles.resolve(data.dj_role); - if (djRole && !msg.member!.roles.cache.has(djRole.id)) { + const djRole = ctx.guild!.roles.resolve(data.dj_role); + if (djRole && !ctx.member!.roles.cache.has(djRole.id)) { return `Sorry, but my commands are restricted only for those who has ${djRole.name} role`; } } From 2cf26e490b1a1fe83b17c94487934a48a439b017 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:32:21 +0700 Subject: [PATCH 29/34] fix(Global): import should be in `bot.ts` --- src/bot.ts | 1 + src/index.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot.ts b/src/bot.ts index 09bee61..5e2a30a 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,3 +1,4 @@ +import "./extension/Global"; import { BotClient } from "./structures/BotClient"; import { clientOptions } from "./config"; import { CustomError } from "./utils/CustomError"; diff --git a/src/index.ts b/src/index.ts index 8af4e04..ef2f9c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ import "dotenv/config"; -import "./extension/Global"; import { resolve } from "path"; import { ShardingManager } from "discord.js"; import { createLogger } from "./utils/Logger"; From 100146d5b23ecc75536e96dac4275a3b1841410e Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:37:37 +0700 Subject: [PATCH 30/34] chore(Shard): use `DISCORD_TOKEN` from .env --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ef2f9c1..a4bbcb2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ if (process[Symbol.for("ts-node.register.instance")]) { totalShards, mode: "worker", respawn: true, - token: process.env.SECRET_DISCORD_TOKEN + token: process.env.DISCORD_TOKEN }); manager.on("shardCreate", shard => { From 64ae03ea4a30d975dcc2bc3d76704a26d2a01bd3 Mon Sep 17 00:00:00 2001 From: KurokuTetsuya Date: Mon, 22 Nov 2021 21:38:16 +0700 Subject: [PATCH 31/34] chore(bot): access token from `DISCORD_TOKEN` --- src/bot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot.ts b/src/bot.ts index 5e2a30a..d0d5fb3 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -17,5 +17,5 @@ process.on("uncaughtException", e => { process.exit(1); }); -client.build(process.env.SECRET_DISCORD_TOKEN!) +client.build(process.env.DISCORD_TOKEN!) .catch(e => client.logger.error(e)); From ff689260026eee0f6783dc131a99754ef239985d Mon Sep 17 00:00:00 2001 From: Zen <45705890+ZenShibata@users.noreply.github.com> Date: Tue, 23 Nov 2021 08:04:04 +0700 Subject: [PATCH 32/34] fix(MusicHandler): don't delete queue --- src/utils/MusicHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/MusicHandler.ts b/src/utils/MusicHandler.ts index d545cbb..77746b7 100644 --- a/src/utils/MusicHandler.ts +++ b/src/utils/MusicHandler.ts @@ -80,7 +80,6 @@ export class MusicHandler { this.oldExceptionMessage = null; this.oldVoiceStateUpdateMessage = null; this.skipVotes = []; - this.client.queue.cache.delete(this.guild.id); return undefined; } From d265777acea8b722ffba4c2e38ed2254c5cbf580 Mon Sep 17 00:00:00 2001 From: Zen <45705890+ZenShibata@users.noreply.github.com> Date: Sun, 28 Nov 2021 09:45:47 +0700 Subject: [PATCH 33/34] chore(RemoveCommand): change usage --- src/commands/music/RemoveCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/music/RemoveCommand.ts b/src/commands/music/RemoveCommand.ts index 8c50f83..4b0abcc 100644 --- a/src/commands/music/RemoveCommand.ts +++ b/src/commands/music/RemoveCommand.ts @@ -19,7 +19,7 @@ import { isMemberDJ, isMemberInVoiceChannel, isMemberVoiceChannelJoinable, isMus } ] }, - usage: "{prefix}volume [new volume]" + usage: "{prefix}remove [track position]" }) export class RemoveCommand extends BaseCommand { @isMusicPlaying() From 2f0f66f98f42199820d4e56c742e3323678e2312 Mon Sep 17 00:00:00 2001 From: Zen <45705890+ZenShibata@users.noreply.github.com> Date: Sun, 28 Nov 2021 16:58:01 +0700 Subject: [PATCH 34/34] feat(EightDCommand): defer interaction --- src/commands/filters/EightDCommand.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/filters/EightDCommand.ts b/src/commands/filters/EightDCommand.ts index c94d195..dc22fbe 100644 --- a/src/commands/filters/EightDCommand.ts +++ b/src/commands/filters/EightDCommand.ts @@ -20,6 +20,7 @@ export class EightDCommand extends BaseCommand { @isMemberVoiceChannelJoinable() @isSameVoiceChannel() public async execute(ctx: CommandContext): Promise { + if (ctx.isInteraction() && !ctx.deferred) await ctx.deferReply(); await ctx.guild!.music.player!.setEightD(!ctx.guild!.music.player!.filters.eightD); const msg = await ctx.send({ embeds: [