discord-bot-shared 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,12 @@ interface Command {
8
8
  command: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder;
9
9
  run: CommandRunFn;
10
10
  }
11
- interface CommandHookResult {
12
- success: boolean;
11
+ type CommandHookResult = {
12
+ success: true;
13
+ } | {
14
+ success: false;
13
15
  message?: string;
14
- }
16
+ };
15
17
  type CommandHook = (interaction: ChatInputCommandInteraction<"cached">) => CommandHookResult | Promise<CommandHookResult>;
16
18
  declare class CommandManager {
17
19
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"command-manager.d.ts","names":[],"sources":["../src/command-manager.ts"],"sourcesContent":[],"mappings":";;;;KAcY,YAAA,iBAA6B,iDAAiD;UAEzE,OAAA;EAFL,aAAA,CAAA,EAAY,MAAA,EAAA;EAEP,OAAA,EAEN,mBAFa,GAES,8BAFT;EAEb,GAAA,EACJ,YADI;;UAID,iBAAA,CAHH;EAAY,OAAA,EAAA,OAAA;EAGT,OAAA,CAAA,EAAA,MAAA;AAIV;AACe,KADH,WAAA,GACG,CAAA,WAAA,EAAA,2BAAA,CAAA,QAAA,CAAA,EAAA,GACV,iBADU,GACU,OADV,CACkB,iBADlB,CAAA;AACV,cAEQ,cAAA,CAFR;EAA4B,CAAA,OAAA;EAAR,WAAA,CAAA,OAAA,EAOK,cAPL;EAAO,QAAA,iBAAA;EAEnB,QAAA,OAAA;EAKiB,GAAA,CAAA,OAAA,EAgBR,OAhBQ,CAAA,EAAA,IAAA;EAgBR,oBAAA,CAAA,WAAA,EAIqB,WAJrB,CAAA,EAAA,IAAA;EAIqB,QAAA,CAAA,CAAA,EAIhB,OAJgB,CAAA,IAAA,CAAA;EAIhB,UAAA,CAAA,CAAA,EAWE,OAXF,CAAA,IAAA,CAAA;EAWE,aAAA,CAAA,CAAA,EAWD,OAXC,CAAA,IAAA,CAAA;EAWD,eAAA,CAAA,CAAA,EAkCM,OAlCN,CAAA,IAAA,CAAA;EAkCM,OAAA,CAAA,CAAA,EAAA,IAAA;EAAO,eAAA,UAAA"}
1
+ {"version":3,"file":"command-manager.d.ts","names":[],"sources":["../src/command-manager.ts"],"sourcesContent":[],"mappings":";;;;KAcY,YAAA,iBAA6B,iDAAiD;UAEzE,OAAA;EAFL,aAAA,CAAA,EAAY,MAAA,EAAA;EAEP,OAAA,EAEN,mBAFa,GAES,8BAFT;EAEb,GAAA,EACJ,YADI;;KAIN,iBAAA,GAHE;EAAY,OAAA,EAAA,IAAA;AAClB,CAAA,GAEI;EASO,OAAA,EAAA,KAAW;EACR,OAAA,CAAA,EAAA,MAAA;CACV;AAA4B,KAFrB,WAAA,GAEqB,CAAA,WAAA,EADlB,2BACkB,CAAA,QAAA,CAAA,EAAA,GAA5B,iBAA4B,GAAR,OAAQ,CAAA,iBAAA,CAAA;AAAR,cAEZ,cAAA,CAFY;EAAO,CAAA,OAAA;EAEnB,WAAA,CAAA,OAAc,EAKG,cALH;EAKG,QAAA,iBAAA;EAgBR,QAAA,OAAA;EAIqB,GAAA,CAAA,OAAA,EAJrB,OAIqB,CAAA,EAAA,IAAA;EAIhB,oBAAA,CAAA,WAAA,EAJgB,WAIhB,CAAA,EAAA,IAAA;EAWE,QAAA,CAAA,CAAA,EAXF,OAWE,CAAA,IAAA,CAAA;EAWD,UAAA,CAAA,CAAA,EAXC,OAWD,CAAA,IAAA,CAAA;EAkCM,aAAA,CAAA,CAAA,EAlCN,OAkCM,CAAA,IAAA,CAAA;EAAO,eAAA,CAAA,CAAA,EAAP,OAAO,CAAA,IAAA,CAAA"}
@@ -91,8 +91,7 @@ var CommandManager = class CommandManager {
91
91
  if (!hasRequiredRole) return await CommandManager.interactionErrorReply(interaction, "You do not have one of the required roles to run this command.", "warn");
92
92
  const globalCommandHookResult = await attempt(() => this.#globalCommandHook ? this.#globalCommandHook(interaction) : { success: true });
93
93
  if (isErr(globalCommandHookResult)) return await CommandManager.interactionErrorReply(interaction, err("failed to run global command hook", globalCommandHookResult));
94
- const { success: shouldContinue, message: globalCommandHookMessage = "The global command hook did not succeed." } = globalCommandHookResult;
95
- if (!shouldContinue) return await CommandManager.interactionErrorReply(interaction, globalCommandHookMessage, "warn");
94
+ if (!globalCommandHookResult.success) return await CommandManager.interactionErrorReply(interaction, globalCommandHookResult.message ?? "The global command hook did not succeed.", "warn");
96
95
  const commandRunResult = await attempt(() => command.run(interaction));
97
96
  if (isErr(commandRunResult)) return await CommandManager.interactionErrorReply(interaction, err(`failed to run command \`${command.command.name}\``, commandRunResult));
98
97
  };
@@ -1 +1 @@
1
- {"version":3,"file":"command-manager.js","names":["#commands","#discord","#globalCommandHook"],"sources":["../src/command-manager.ts"],"sourcesContent":["import type {\n ChatInputCommandInteraction,\n Interaction,\n SlashCommandBuilder,\n SlashCommandOptionsOnlyBuilder,\n} from \"discord.js\"\nimport { Collection, Events, MessageFlags, Routes } from \"discord.js\"\nimport type { Result } from \"ts-explicit-errors\"\nimport { attempt, CtxError, err, filterMap, isErr } from \"ts-explicit-errors\"\n\nimport type { DiscordContext } from \"~/bot.ts\"\nimport { components } from \"~/components.ts\"\nimport { handleCallback } from \"~/util.ts\"\n\nexport type CommandRunFn = (interaction: ChatInputCommandInteraction<\"cached\">) => void | Promise<void>\n\nexport interface Command {\n requiredRoles?: string[]\n command: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder\n run: CommandRunFn\n}\n\ninterface CommandHookResult {\n success: boolean\n message?: string\n}\nexport type CommandHook = (\n interaction: ChatInputCommandInteraction<\"cached\">,\n) => CommandHookResult | Promise<CommandHookResult>\n\nexport class CommandManager {\n readonly #commands = new Collection<string, Command>()\n #globalCommandHook?: CommandHook\n readonly #discord: DiscordContext\n\n public constructor(discord: DiscordContext) {\n this.#discord = discord\n }\n\n private getCommandPayload() {\n return this.#commands.map((c) => c.command.toJSON())\n }\n\n private async onReady(fn: () => Result | Promise<Result>) {\n if (this.#discord.client.isReady()) await handleCallback(fn)\n else this.#discord.client.once(Events.ClientReady, async () => await handleCallback(fn))\n }\n\n /*\n * Add a command\n */\n public add(command: Command): void {\n this.#commands.set(command.command.name, command)\n }\n\n public setGlobalCommandHook(commandHook: CommandHook): void {\n this.#globalCommandHook = commandHook\n }\n\n public async register(): Promise<void> {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationCommands(this.#discord.applicationId)\n await this.#discord.rest.put(route, { body: this.getCommandPayload() })\n\n console.log(`Globally registered ${this.#commands.size.toString()} (/) commands.`)\n })\n\n if (isErr(registerResult)) console.error(`failed to register global commands: ${registerResult.messageChain}`)\n }\n\n public async unregister(): Promise<void> {\n const unregisterResult = await attempt(async () => {\n const route = Routes.applicationCommands(this.#discord.applicationId)\n await this.#discord.rest.put(route, { body: [] })\n\n console.log(\"Unregistered global commands.\")\n })\n\n if (isErr(unregisterResult)) console.error(`failed to unregister global commands: ${unregisterResult.messageChain}`)\n }\n\n public async guildRegister() {\n const registerGuildCommands = async (): Promise<Result> => {\n const guilds = await attempt(() => this.#discord.client.guilds.fetch())\n if (isErr(guilds)) return err(\"failed to fetch guilds\", guilds)\n\n const commandPayload = this.getCommandPayload()\n\n const { errors } = await filterMap(guilds.values(), async (guild) => {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationGuildCommands(this.#discord.applicationId, guild.id)\n await this.#discord.rest.put(route, { body: commandPayload })\n console.log(`Registered ${this.#commands.size.toString()} (/) commands in guild '${guild.name}'`)\n })\n\n if (isErr(registerResult))\n return err(`failed to register guild commands in guild '${guild.name}'`, registerResult)\n\n return\n })\n\n if (errors)\n return err(\n `failed to register guild commands in all guilds:\\n${errors.map((error) => error.message).join(\"\\n\")}`,\n undefined,\n )\n }\n\n await this.onReady(async (): Promise<Result> => {\n const registerGuildCommandsResult = await registerGuildCommands()\n if (isErr(registerGuildCommandsResult))\n return err(\"failed to register guild commands\", registerGuildCommandsResult)\n })\n }\n\n public async guildUnregister(): Promise<void> {\n const unregisterGuildCommands = async (): Promise<Result> => {\n const guilds = await attempt(() => this.#discord.client.guilds.fetch())\n if (isErr(guilds)) return err(\"failed to fetch guilds\", guilds)\n\n const { errors } = await filterMap(guilds.values(), async (guild) => {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationGuildCommands(this.#discord.applicationId, guild.id)\n await this.#discord.rest.put(route, { body: [] })\n console.log(`Unregistered commands in guild '${guild.name}'`)\n })\n\n if (isErr(registerResult))\n return err(`failed to unregister guild commands in guild '${guild.name}'`, registerResult)\n\n return\n })\n\n if (errors)\n return err(\n `failed to unregister guild commands in all guilds:\\n${errors.map((error) => error.message).join(\"\\n\")}`,\n undefined,\n )\n }\n\n await this.onReady(async (): Promise<Result> => {\n const unregisterGuildCommandsResult = await unregisterGuildCommands()\n if (isErr(unregisterGuildCommandsResult))\n return err(\"failed to unregister guild commands\", unregisterGuildCommandsResult)\n })\n }\n\n public _listen(): void {\n const handleInteraction = async (interaction: Interaction): Promise<Result> => {\n if (!interaction.isChatInputCommand()) return\n if (!interaction.guildId) return\n\n const guild = await interaction.client.guilds.fetch(interaction.guildId)\n if (isErr(guild))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to fetch guild with id '${interaction.guildId}'`, guild),\n )\n\n if (!interaction.inCachedGuild())\n return await CommandManager.interactionErrorReply(interaction, \"Guild is not cached. Try again.\")\n\n const command = this.#commands.get(interaction.commandName)\n if (!command)\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to get command with name '${interaction.commandName}'`, undefined),\n )\n\n const hasRequiredRole = await CommandManager.checkRoles(command, interaction)\n if (isErr(hasRequiredRole))\n return await CommandManager.interactionErrorReply(interaction, err(\"failed to check roles\", hasRequiredRole))\n\n if (!hasRequiredRole)\n return await CommandManager.interactionErrorReply(\n interaction,\n \"You do not have one of the required roles to run this command.\",\n \"warn\",\n )\n\n const globalCommandHookResult = await attempt(() =>\n this.#globalCommandHook ? this.#globalCommandHook(interaction) : { success: true },\n )\n if (isErr(globalCommandHookResult))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(\"failed to run global command hook\", globalCommandHookResult),\n )\n\n const {\n success: shouldContinue,\n message: globalCommandHookMessage = \"The global command hook did not succeed.\",\n } = globalCommandHookResult\n if (!shouldContinue)\n return await CommandManager.interactionErrorReply(interaction, globalCommandHookMessage, \"warn\")\n\n const commandRunResult = await attempt(() => command.run(interaction))\n if (isErr(commandRunResult))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to run command \\`${command.command.name}\\``, commandRunResult),\n )\n }\n\n this.#discord.client.on(\n Events.InteractionCreate,\n async (interaction) => await handleCallback(() => handleInteraction(interaction)),\n )\n\n console.log(\"Listening for commands.\")\n }\n\n private static async checkRoles({ requiredRoles }: Command, interaction: ChatInputCommandInteraction<\"cached\">) {\n if (!requiredRoles) return true\n\n if (requiredRoles.length > 0) {\n const member = await attempt(() => interaction.guild.members.fetch(interaction.user))\n if (isErr(member)) return err(`failed to fetch member '${interaction.user.id}'`, member)\n\n return member.roles.cache.some((role) => requiredRoles.includes(role.name))\n }\n\n return false\n }\n\n private static async interactionErrorReply(\n interaction: ChatInputCommandInteraction,\n error: string | CtxError,\n type: keyof typeof components = \"error\",\n ): Promise<Result> {\n const errorMessage = error instanceof CtxError ? error.messageChain : error\n const errorComponent = components[type](errorMessage)\n\n const reply = await attempt(async () =>\n interaction.deferred\n ? interaction.editReply(errorComponent)\n : interaction.reply({\n components: errorComponent.components,\n flags: [...errorComponent.flags, MessageFlags.Ephemeral],\n }),\n )\n\n if (isErr(reply)) return err(`failed to reply to interaction from command '${interaction.commandName}'`, reply)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"command-manager.js","names":["#commands","#discord","#globalCommandHook"],"sources":["../src/command-manager.ts"],"sourcesContent":["import type {\n ChatInputCommandInteraction,\n Interaction,\n SlashCommandBuilder,\n SlashCommandOptionsOnlyBuilder,\n} from \"discord.js\"\nimport { Collection, Events, MessageFlags, Routes } from \"discord.js\"\nimport type { Result } from \"ts-explicit-errors\"\nimport { attempt, CtxError, err, filterMap, isErr } from \"ts-explicit-errors\"\n\nimport type { DiscordContext } from \"~/bot.ts\"\nimport { components } from \"~/components.ts\"\nimport { handleCallback } from \"~/util.ts\"\n\nexport type CommandRunFn = (interaction: ChatInputCommandInteraction<\"cached\">) => void | Promise<void>\n\nexport interface Command {\n requiredRoles?: string[]\n command: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder\n run: CommandRunFn\n}\n\ntype CommandHookResult =\n | {\n success: true\n }\n | {\n success: false\n message?: string\n }\n\nexport type CommandHook = (\n interaction: ChatInputCommandInteraction<\"cached\">,\n) => CommandHookResult | Promise<CommandHookResult>\n\nexport class CommandManager {\n readonly #commands = new Collection<string, Command>()\n #globalCommandHook?: CommandHook\n readonly #discord: DiscordContext\n\n public constructor(discord: DiscordContext) {\n this.#discord = discord\n }\n\n private getCommandPayload() {\n return this.#commands.map((c) => c.command.toJSON())\n }\n\n private async onReady(fn: () => Result | Promise<Result>) {\n if (this.#discord.client.isReady()) await handleCallback(fn)\n else this.#discord.client.once(Events.ClientReady, async () => await handleCallback(fn))\n }\n\n /*\n * Add a command\n */\n public add(command: Command): void {\n this.#commands.set(command.command.name, command)\n }\n\n public setGlobalCommandHook(commandHook: CommandHook): void {\n this.#globalCommandHook = commandHook\n }\n\n public async register(): Promise<void> {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationCommands(this.#discord.applicationId)\n await this.#discord.rest.put(route, { body: this.getCommandPayload() })\n\n console.log(`Globally registered ${this.#commands.size.toString()} (/) commands.`)\n })\n\n if (isErr(registerResult)) console.error(`failed to register global commands: ${registerResult.messageChain}`)\n }\n\n public async unregister(): Promise<void> {\n const unregisterResult = await attempt(async () => {\n const route = Routes.applicationCommands(this.#discord.applicationId)\n await this.#discord.rest.put(route, { body: [] })\n\n console.log(\"Unregistered global commands.\")\n })\n\n if (isErr(unregisterResult)) console.error(`failed to unregister global commands: ${unregisterResult.messageChain}`)\n }\n\n public async guildRegister() {\n const registerGuildCommands = async (): Promise<Result> => {\n const guilds = await attempt(() => this.#discord.client.guilds.fetch())\n if (isErr(guilds)) return err(\"failed to fetch guilds\", guilds)\n\n const commandPayload = this.getCommandPayload()\n\n const { errors } = await filterMap(guilds.values(), async (guild) => {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationGuildCommands(this.#discord.applicationId, guild.id)\n await this.#discord.rest.put(route, { body: commandPayload })\n console.log(`Registered ${this.#commands.size.toString()} (/) commands in guild '${guild.name}'`)\n })\n\n if (isErr(registerResult))\n return err(`failed to register guild commands in guild '${guild.name}'`, registerResult)\n\n return\n })\n\n if (errors)\n return err(\n `failed to register guild commands in all guilds:\\n${errors.map((error) => error.message).join(\"\\n\")}`,\n undefined,\n )\n }\n\n await this.onReady(async (): Promise<Result> => {\n const registerGuildCommandsResult = await registerGuildCommands()\n if (isErr(registerGuildCommandsResult))\n return err(\"failed to register guild commands\", registerGuildCommandsResult)\n })\n }\n\n public async guildUnregister(): Promise<void> {\n const unregisterGuildCommands = async (): Promise<Result> => {\n const guilds = await attempt(() => this.#discord.client.guilds.fetch())\n if (isErr(guilds)) return err(\"failed to fetch guilds\", guilds)\n\n const { errors } = await filterMap(guilds.values(), async (guild) => {\n const registerResult = await attempt(async () => {\n const route = Routes.applicationGuildCommands(this.#discord.applicationId, guild.id)\n await this.#discord.rest.put(route, { body: [] })\n console.log(`Unregistered commands in guild '${guild.name}'`)\n })\n\n if (isErr(registerResult))\n return err(`failed to unregister guild commands in guild '${guild.name}'`, registerResult)\n\n return\n })\n\n if (errors)\n return err(\n `failed to unregister guild commands in all guilds:\\n${errors.map((error) => error.message).join(\"\\n\")}`,\n undefined,\n )\n }\n\n await this.onReady(async (): Promise<Result> => {\n const unregisterGuildCommandsResult = await unregisterGuildCommands()\n if (isErr(unregisterGuildCommandsResult))\n return err(\"failed to unregister guild commands\", unregisterGuildCommandsResult)\n })\n }\n\n public _listen(): void {\n const handleInteraction = async (interaction: Interaction): Promise<Result> => {\n if (!interaction.isChatInputCommand()) return\n if (!interaction.guildId) return\n\n const guild = await interaction.client.guilds.fetch(interaction.guildId)\n if (isErr(guild))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to fetch guild with id '${interaction.guildId}'`, guild),\n )\n\n if (!interaction.inCachedGuild())\n return await CommandManager.interactionErrorReply(interaction, \"Guild is not cached. Try again.\")\n\n const command = this.#commands.get(interaction.commandName)\n if (!command)\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to get command with name '${interaction.commandName}'`, undefined),\n )\n\n const hasRequiredRole = await CommandManager.checkRoles(command, interaction)\n if (isErr(hasRequiredRole))\n return await CommandManager.interactionErrorReply(interaction, err(\"failed to check roles\", hasRequiredRole))\n\n if (!hasRequiredRole)\n return await CommandManager.interactionErrorReply(\n interaction,\n \"You do not have one of the required roles to run this command.\",\n \"warn\",\n )\n\n const globalCommandHookResult = await attempt(() =>\n this.#globalCommandHook ? this.#globalCommandHook(interaction) : ({ success: true } as const),\n )\n if (isErr(globalCommandHookResult))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(\"failed to run global command hook\", globalCommandHookResult),\n )\n\n if (!globalCommandHookResult.success)\n return await CommandManager.interactionErrorReply(\n interaction,\n globalCommandHookResult.message ?? \"The global command hook did not succeed.\",\n \"warn\",\n )\n\n const commandRunResult = await attempt(() => command.run(interaction))\n if (isErr(commandRunResult))\n return await CommandManager.interactionErrorReply(\n interaction,\n err(`failed to run command \\`${command.command.name}\\``, commandRunResult),\n )\n }\n\n this.#discord.client.on(\n Events.InteractionCreate,\n async (interaction) => await handleCallback(() => handleInteraction(interaction)),\n )\n\n console.log(\"Listening for commands.\")\n }\n\n private static async checkRoles({ requiredRoles }: Command, interaction: ChatInputCommandInteraction<\"cached\">) {\n if (!requiredRoles) return true\n\n if (requiredRoles.length > 0) {\n const member = await attempt(() => interaction.guild.members.fetch(interaction.user))\n if (isErr(member)) return err(`failed to fetch member '${interaction.user.id}'`, member)\n\n return member.roles.cache.some((role) => requiredRoles.includes(role.name))\n }\n\n return false\n }\n\n private static async interactionErrorReply(\n interaction: ChatInputCommandInteraction,\n error: string | CtxError,\n type: keyof typeof components = \"error\",\n ): Promise<Result> {\n const errorMessage = error instanceof CtxError ? error.messageChain : error\n const errorComponent = components[type](errorMessage)\n\n const reply = await attempt(async () =>\n interaction.deferred\n ? interaction.editReply(errorComponent)\n : interaction.reply({\n components: errorComponent.components,\n flags: [...errorComponent.flags, MessageFlags.Ephemeral],\n }),\n )\n\n if (isErr(reply)) return err(`failed to reply to interaction from command '${interaction.commandName}'`, reply)\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discord-bot-shared",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "type": "module",
5
5
  "description": "Modules for creating discord bots.",
6
6
  "repository": {
@@ -36,13 +36,13 @@
36
36
  "devDependencies": {
37
37
  "@adamhl8/configs": "^0.18.0",
38
38
  "@arethetypeswrong/core": "^0.18.2",
39
- "@biomejs/biome": "^2.3.8",
40
- "@types/bun": "^1.3.4",
39
+ "@biomejs/biome": "^2.3.10",
40
+ "@types/bun": "^1.3.5",
41
41
  "discord.js": "^14.25.1",
42
- "knip": "^5.73.4",
42
+ "knip": "^5.76.1",
43
43
  "markdown-toc": "^1.2.0",
44
44
  "publint": "^0.3.16",
45
- "tsdown": "^0.17.3",
45
+ "tsdown": "^0.18.2",
46
46
  "typescript": "^5.9.3"
47
47
  },
48
48
  "dependencies": {