discordthing 0.1.2 → 0.2.0

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.
Files changed (49) hide show
  1. package/build/bot.d.ts +15 -19
  2. package/build/bot.js +28 -139
  3. package/build/commands/command.d.ts +26 -7
  4. package/build/commands/command.js +71 -37
  5. package/build/commands/context.d.ts +23 -0
  6. package/build/commands/context.js +75 -0
  7. package/build/commands/diff.js +0 -1
  8. package/build/commands/index.d.ts +3 -1
  9. package/build/commands/index.js +0 -1
  10. package/build/commands/manager.d.ts +64 -0
  11. package/build/commands/manager.js +138 -0
  12. package/build/commands/registration.js +0 -1
  13. package/build/context.d.ts +1 -1
  14. package/build/context.js +2 -3
  15. package/build/events/index.d.ts +1 -0
  16. package/build/events/index.js +0 -1
  17. package/build/events/listener.js +0 -1
  18. package/build/events/manager.d.ts +40 -0
  19. package/build/events/manager.js +86 -0
  20. package/build/index.js +0 -1
  21. package/build/logger.js +0 -1
  22. package/build/options/index.js +0 -1
  23. package/build/options/option.d.ts +12 -30
  24. package/build/options/option.js +7 -15
  25. package/build/options/options.d.ts +39 -30
  26. package/build/options/options.js +59 -36
  27. package/build/permissions.d.ts +4 -4
  28. package/build/permissions.js +2 -3
  29. package/build/plugin.js +0 -1
  30. package/build/utility-types.js +0 -1
  31. package/build/utils.js +0 -1
  32. package/package.json +6 -3
  33. package/build/bot.js.map +0 -1
  34. package/build/commands/command.js.map +0 -1
  35. package/build/commands/diff.js.map +0 -1
  36. package/build/commands/index.js.map +0 -1
  37. package/build/commands/registration.js.map +0 -1
  38. package/build/context.js.map +0 -1
  39. package/build/events/index.js.map +0 -1
  40. package/build/events/listener.js.map +0 -1
  41. package/build/index.js.map +0 -1
  42. package/build/logger.js.map +0 -1
  43. package/build/options/index.js.map +0 -1
  44. package/build/options/option.js.map +0 -1
  45. package/build/options/options.js.map +0 -1
  46. package/build/permissions.js.map +0 -1
  47. package/build/plugin.js.map +0 -1
  48. package/build/utility-types.js.map +0 -1
  49. package/build/utils.js.map +0 -1
package/build/bot.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { type ReturnResult } from "@l3dev/result";
2
- import { Client, REST, SlashCommandBuilder, type ClientOptions, type Guild, type RestOrArray } from "discord.js";
3
- import type { Command, CommandFilter } from "./commands/command.js";
1
+ import { Client, REST, SlashCommandBuilder, type ClientOptions } from "discord.js";
2
+ import type { Command } from "./commands/command.js";
3
+ import { CommandManager } from "./commands/manager.js";
4
4
  import type { Listener } from "./events/listener.js";
5
+ import { EventsManager } from "./events/manager.js";
5
6
  import type { ILogger } from "./logger.js";
6
7
  import type { Plugin } from "./plugin.js";
7
8
  export type BotOptions = {
@@ -15,23 +16,18 @@ export declare class Bot {
15
16
  readonly client: Client<boolean>;
16
17
  private _rest;
17
18
  get rest(): REST;
19
+ readonly logger: ILogger;
20
+ readonly commands: CommandManager;
21
+ readonly events: EventsManager;
18
22
  private readonly plugins;
19
- private readonly commands;
20
- private readonly commandFilters;
21
- private readonly readyListeners;
22
- private readonly logger;
23
23
  constructor(options: BotOptions, clientOptions: ClientOptions);
24
- addCommands(...commands: RestOrArray<Command<string, SlashCommandBuilder>>): void;
25
- addCommand(command: Command<string, SlashCommandBuilder>): void;
26
- addCommandFilter(filter: CommandFilter): void;
27
- addListeners(...listeners: RestOrArray<Listener<any>>): void;
28
- addListener(listener: Listener<any>): void;
29
- private createListenerFn;
30
- start(token: string): Promise<void>;
31
- getMe(guild: Guild): Promise<ReturnResult<import("discord.js").GuildMember, import("@l3dev/result").ResultErrorDefinition<"GET_ME", object & {
32
- error: Error;
33
- }>>>;
34
- private registerCommands;
24
+ /**
25
+ * Starts the bot and establishes a WebSocket connection to Discord.
26
+ * @param token Token of the account to log in with
27
+ * @returns Token of the account used
28
+ * @example
29
+ * bot.start('my token');
30
+ */
31
+ start(token: string): Promise<string>;
35
32
  private onReady;
36
- private onInteraction;
37
33
  }
package/build/bot.js CHANGED
@@ -1,19 +1,17 @@
1
- import { err, ok, Result } from "@l3dev/result";
2
- import { Client, Events, InteractionContextType, REST, SlashCommandBuilder } from "discord.js";
1
+ import { Client, Events, REST, SlashCommandBuilder } from "discord.js";
3
2
  import { Logger } from "tslog";
4
- import { CommandRegistration } from "./commands/registration.js";
5
- import { createContext } from "./context.js";
3
+ import { CommandManager } from "./commands/manager.js";
4
+ import { EventsManager } from "./events/manager.js";
6
5
  export class Bot {
7
6
  client;
8
7
  _rest;
9
8
  get rest() {
10
9
  return this._rest;
11
10
  }
12
- plugins;
13
- commands;
14
- commandFilters = [];
15
- readyListeners = [];
16
11
  logger;
12
+ commands;
13
+ events;
14
+ plugins;
17
15
  constructor(options, clientOptions) {
18
16
  this._rest = new REST({ version: "10" });
19
17
  this.client = new Client(clientOptions);
@@ -26,146 +24,37 @@ export class Bot {
26
24
  for (const plugin of this.plugins) {
27
25
  plugin.resolved(this);
28
26
  }
29
- this.commands = [
30
- ...(options.commands ?? []),
31
- ...this.plugins.flatMap((plugin) => plugin.commands)
32
- ].reduce((acc, command) => ({
33
- ...acc,
34
- [command.name]: command
35
- }), {});
27
+ this.commands = new CommandManager(this, {
28
+ commands: [...(options.commands ?? []), ...this.plugins.flatMap((plugin) => plugin.commands)]
29
+ });
30
+ this.events = new EventsManager(this, {
31
+ listeners: [
32
+ ...(options.listeners ?? []),
33
+ ...this.plugins.flatMap((plugin) => plugin.listeners)
34
+ ]
35
+ });
36
+ this.client.on(Events.Debug, (message) => {
37
+ this.logger.debug(message);
38
+ });
36
39
  this.client.on(Events.ClientReady, this.onReady.bind(this));
37
- this.client.on(Events.InteractionCreate, this.onInteraction.bind(this));
38
- const allListeners = [
39
- ...(options.listeners ?? []),
40
- ...this.plugins.flatMap((plugin) => plugin.listeners)
41
- ];
42
- for (const listener of allListeners) {
43
- if (listener.event === Events.ClientReady) {
44
- this.readyListeners.push(listener);
45
- continue;
46
- }
47
- this.client.on(listener.event, this.createListenerFn(listener));
48
- }
49
- }
50
- addCommands(...commands) {
51
- for (const command of commands.flat()) {
52
- this.addCommand(command);
53
- }
54
- }
55
- addCommand(command) {
56
- if (this.client.isReady()) {
57
- throw new Error("Cannot add command after bot has started");
58
- }
59
- this.commands[command.name] = command;
60
- }
61
- addCommandFilter(filter) {
62
- this.commandFilters.push(filter);
63
- }
64
- addListeners(...listeners) {
65
- for (const listener of listeners.flat()) {
66
- this.addListener(listener);
67
- }
68
- }
69
- addListener(listener) {
70
- if (this.client.isReady()) {
71
- throw new Error("Cannot add listener after bot has started");
72
- }
73
- if (listener.event === Events.ClientReady) {
74
- this.readyListeners.push(listener);
75
- return;
76
- }
77
- (listener.once ? this.client.once : this.client.on)(listener.event, this.createListenerFn(listener));
78
- }
79
- createListenerFn(listener) {
80
- return async (...args) => {
81
- const ctx = createContext(this, this.logger);
82
- try {
83
- await listener.handler(ctx, ...args);
84
- }
85
- catch (error) {
86
- this.logger.error(`Error in listener ${listener.event}:`, error);
87
- }
88
- };
89
40
  }
41
+ /**
42
+ * Starts the bot and establishes a WebSocket connection to Discord.
43
+ * @param token Token of the account to log in with
44
+ * @returns Token of the account used
45
+ * @example
46
+ * bot.start('my token');
47
+ */
90
48
  async start(token) {
91
49
  this._rest = this._rest.setToken(token);
92
- await this.client.login(token);
93
- }
94
- async getMe(guild) {
95
- if (guild.members.me) {
96
- return ok(guild.members.me);
97
- }
98
- return await Result.fromPromise(guild.members.fetchMe(), {
99
- onError: { type: "GET_ME" }
100
- });
101
- }
102
- async registerCommands(client) {
103
- const commandRegistration = new CommandRegistration(this._rest, client);
104
- const globalCommands = Object.values(this.commands).filter((command) => {
105
- const contexts = command.getContexts();
106
- return (contexts.includes(InteractionContextType.BotDM) ||
107
- contexts.includes(InteractionContextType.PrivateChannel));
108
- });
109
- const guildCommands = Object.values(this.commands).filter((command) => {
110
- const contexts = command.getContexts();
111
- return contexts.includes(InteractionContextType.Guild);
112
- });
113
- this.logger.debug(`Registering global commands...`);
114
- await commandRegistration.registerGlobalCommands(globalCommands);
115
- const guilds = await client.guilds.fetch();
116
- for (const guild of guilds.values()) {
117
- this.logger.debug(`Registering guild commands for guild ${guild.id}...`);
118
- await commandRegistration.registerGuildCommands(guild.id, guildCommands);
119
- }
50
+ return await this.client.login(token);
120
51
  }
121
52
  async onReady(client) {
122
53
  const startMs = performance.now();
123
- await this.registerCommands(client);
124
- // Make a copy so we can remove _once_ listeners
125
- const listeners = [...this.readyListeners];
126
- for (const listener of listeners) {
127
- const handler = this.createListenerFn(listener);
128
- handler(client);
129
- if (listener.once) {
130
- const index = this.readyListeners.indexOf(listener);
131
- if (index === -1) {
132
- return;
133
- }
134
- this.readyListeners.splice(index, 1);
135
- }
136
- }
54
+ await this.commands.register(client);
55
+ await this.events.runReadyListeners(client);
137
56
  const endMs = performance.now();
138
57
  const elapsedMs = endMs - startMs;
139
58
  this.logger.info(`Ready! (${elapsedMs > 1000 ? `${(elapsedMs / 1000).toFixed(2)}s` : `${elapsedMs}ms`})`);
140
59
  }
141
- async onInteraction(interaction) {
142
- if (interaction.user.bot || !interaction.isChatInputCommand())
143
- return;
144
- const command = this.commands[interaction.commandName];
145
- if (!command) {
146
- this.logger.error(`Unknown command '${interaction.commandName}'`);
147
- return;
148
- }
149
- if (!this.commandFilters.some((filter) => filter(interaction, command))) {
150
- return;
151
- }
152
- const ctx = createContext(this, this.logger);
153
- ctx.interaction = interaction;
154
- let result;
155
- try {
156
- result = await command.execute(ctx);
157
- }
158
- catch (error) {
159
- result = err("COMMAND_FAILED", {
160
- command: interaction.commandName,
161
- interactionId: interaction.id,
162
- error
163
- });
164
- }
165
- const unwrapped = Result.unwrap(result);
166
- if (!unwrapped.ok) {
167
- this.logger.error(`Error while executing command '${interaction.commandName}':`, result);
168
- }
169
- }
170
60
  }
171
- //# sourceMappingURL=bot.js.map
@@ -1,11 +1,9 @@
1
- import { type ReturnResult } from "@l3dev/result";
2
- import { InteractionContextType, SlashCommandBuilder, SlashCommandSubcommandBuilder, type ChatInputCommandInteraction, type SharedNameAndDescription, type SharedSlashCommandOptions } from "discord.js";
3
- import type { GenericCtx } from "../context.js";
1
+ import { type Err, type ReturnResult } from "@l3dev/result";
2
+ import { InteractionContextType, SlashCommandBuilder, SlashCommandSubcommandBuilder, type ChatInputCommandInteraction, type CommandInteractionOption, type SharedNameAndDescription, type SharedSlashCommandOptions } from "discord.js";
3
+ import type * as z from "zod";
4
4
  import type { Args, OptionDefinitions } from "../options/index.js";
5
- import type { Expand, MaybePromise } from "../utility-types.js";
6
- export type CommandCtx = Expand<GenericCtx & {
7
- interaction: ChatInputCommandInteraction;
8
- }>;
5
+ import type { MaybePromise } from "../utility-types.js";
6
+ import type { CommandCtx } from "./context.js";
9
7
  export type ArgsArrayForOptions<Options extends OptionDefinitions> = [Options] extends [
10
8
  OptionDefinitions
11
9
  ] ? [args: Args<Options>] : [];
@@ -109,3 +107,24 @@ export declare function subcommand<Name extends string, Options extends OptionDe
109
107
  handler: (ctx: CommandCtx, ...args: ArgsArrayForOptions<Options>) => MaybePromise<ReturnResult<any, any>>;
110
108
  }): Command<Name, SlashCommandSubcommandBuilder>;
111
109
  export type CommandFilter = (interaction: ChatInputCommandInteraction, command: Command<string, SlashCommandBuilder>) => boolean;
110
+ type KnownCommandError = Err<"OPTION_ERROR", {
111
+ error: Error;
112
+ }> | Err<"INVALID_OPTION_TYPE", {
113
+ name: string;
114
+ option: CommandInteractionOption;
115
+ }> | Err<"MISSING_OPTION", {
116
+ name: string;
117
+ }> | Err<"INVALID_OPTION_VALUE", {
118
+ zodError: z.ZodError;
119
+ name: string;
120
+ value: any;
121
+ }> | Err<"UNKNOWN_COMMAND", {
122
+ command: string;
123
+ }> | Err<"UNKNOWN_SUBCOMMAND", {
124
+ subcommand: string;
125
+ }> | Err<"COMMAND_FAILED", {
126
+ command: string;
127
+ error: Error;
128
+ }>;
129
+ export type ErrorHandler = (ctx: CommandCtx, error: KnownCommandError | Err<string & {}, any>) => void | Promise<void>;
130
+ export {};
@@ -1,6 +1,6 @@
1
- import { err, NONE } from "@l3dev/result";
2
- import { ApplicationCommandOptionType, InteractionContextType, SlashCommandBuilder, SlashCommandSubcommandBuilder } from "discord.js";
3
- import { AttachmentOption, BooleanOption, BuilderOption, ChannelOption, IntegerOption, MentionableOption, NumberOption, RoleOption, StringOption, UserOption } from "../options/options.js";
1
+ import { err, NONE, ok, Result } from "@l3dev/result";
2
+ import { ApplicationCommandOptionType, DiscordjsErrorCodes, DiscordjsTypeError, InteractionContextType, SlashCommandBuilder, SlashCommandSubcommandBuilder } from "discord.js";
3
+ import { AttachmentOption, BooleanOption, ChannelOption, IntegerOption, MentionableOption, NumberOption, RoleOption, StringOption, UserOption } from "../options/options.js";
4
4
  export class Command {
5
5
  builder;
6
6
  name;
@@ -34,12 +34,16 @@ export class Command {
34
34
  const subcommand = this.subcommands[subcommandName];
35
35
  if (!subcommand) {
36
36
  return err("UNKNOWN_SUBCOMMAND", {
37
- name: subcommandName
37
+ subcommand: subcommandName
38
38
  });
39
39
  }
40
40
  return await subcommand.execute(ctx);
41
41
  }
42
- const args = this.parseOptions(ctx.interaction.options);
42
+ const parseResult = this.parseOptions(ctx.interaction.options);
43
+ if (!parseResult.ok) {
44
+ return parseResult;
45
+ }
46
+ const args = parseResult.value;
43
47
  return await this.handler(ctx, args);
44
48
  }
45
49
  /** @internal */
@@ -47,40 +51,73 @@ export class Command {
47
51
  const args = {};
48
52
  for (const [name, option] of Object.entries(this.options ?? {})) {
49
53
  const required = option.isOptional === "required";
50
- let value = null;
51
- if (options.getAttachment && option instanceof AttachmentOption) {
52
- value = options.getAttachment(name, required);
53
- }
54
- else if (options.getBoolean && option instanceof BooleanOption) {
55
- value = options.getBoolean(name, required);
56
- }
57
- else if (options.getChannel && option instanceof ChannelOption) {
58
- value = options.getChannel(name, required, option.getBuilder().channel_types);
59
- }
60
- else if (options.getInteger && option instanceof IntegerOption) {
61
- value = options.getInteger(name, required);
62
- }
63
- else if (options.getMentionable && option instanceof MentionableOption) {
64
- value = options.getMentionable(name, required);
65
- }
66
- else if (options.getNumber && option instanceof NumberOption) {
67
- value = options.getNumber(name, required);
68
- }
69
- else if (options.getRole && option instanceof RoleOption) {
70
- value = options.getRole(name, required);
71
- }
72
- else if (options.getString && option instanceof StringOption) {
73
- value = options.getString(name, required);
74
- }
75
- else if (options.getUser && option instanceof UserOption) {
76
- value = options.getUser(name, required);
54
+ const result = Result.from(() => {
55
+ if (option instanceof AttachmentOption) {
56
+ return options.getAttachment(name, required);
57
+ }
58
+ else if (option instanceof BooleanOption) {
59
+ return options.getBoolean(name, required);
60
+ }
61
+ else if (option instanceof ChannelOption) {
62
+ return options.getChannel(name, required, option.getBuilder().channel_types);
63
+ }
64
+ else if (option instanceof IntegerOption) {
65
+ return options.getInteger(name, required);
66
+ }
67
+ else if (option instanceof MentionableOption) {
68
+ return options.getMentionable(name, required);
69
+ }
70
+ else if (option instanceof NumberOption) {
71
+ return options.getNumber(name, required);
72
+ }
73
+ else if (option instanceof RoleOption) {
74
+ return options.getRole(name, required);
75
+ }
76
+ else if (option instanceof StringOption) {
77
+ return options.getString(name, required);
78
+ }
79
+ else if (option instanceof UserOption) {
80
+ return options.getUser(name, required);
81
+ }
82
+ return null;
83
+ }, {
84
+ onError: { type: "OPTION_ERROR" }
85
+ });
86
+ if (!result.ok) {
87
+ const error = result.context.error;
88
+ if (error instanceof DiscordjsTypeError) {
89
+ switch (error.code) {
90
+ case DiscordjsErrorCodes.CommandInteractionOptionType:
91
+ return err("INVALID_OPTION_TYPE", {
92
+ name,
93
+ option: options.get(name)
94
+ });
95
+ case DiscordjsErrorCodes.CommandInteractionOptionEmpty:
96
+ return err("MISSING_OPTION", {
97
+ name
98
+ });
99
+ }
100
+ }
101
+ return result;
77
102
  }
103
+ let value = result.value;
78
104
  if (value === null || value === undefined) {
79
105
  continue;
80
106
  }
107
+ if (option.validator) {
108
+ const validatorResult = option.validator.safeParse(value);
109
+ if (!validatorResult.success) {
110
+ return err("INVALID_OPTION_VALUE", {
111
+ zodError: validatorResult.error,
112
+ name,
113
+ value
114
+ });
115
+ }
116
+ value = validatorResult.data;
117
+ }
81
118
  args[name] = value;
82
119
  }
83
- return args;
120
+ return ok(args);
84
121
  }
85
122
  /** @internal */
86
123
  getContexts() {
@@ -97,9 +134,7 @@ export class Command {
97
134
  setOptions(options) {
98
135
  this.options = options;
99
136
  for (const [name, option] of Object.entries(options)) {
100
- if (option instanceof BuilderOption) {
101
- this.addOption(name, option);
102
- }
137
+ this.addOption(name, option);
103
138
  }
104
139
  }
105
140
  /** @internal */
@@ -173,4 +208,3 @@ export function subcommand(command) {
173
208
  subcommand.setHandler(command.handler);
174
209
  return subcommand;
175
210
  }
176
- //# sourceMappingURL=command.js.map
@@ -0,0 +1,23 @@
1
+ import { type Err, type Ok } from "@l3dev/result";
2
+ import type { ChatInputCommandInteraction, InteractionEditReplyOptions, InteractionReplyOptions, Message, MessagePayload, MessageResolvable } from "discord.js";
3
+ import type { Bot } from "../bot.js";
4
+ import { type GenericCtx } from "../context.js";
5
+ import type { Expand } from "../utility-types.js";
6
+ type ReplyPosition = number | "@original" | "@last";
7
+ export type CommandCtx = Expand<GenericCtx & {
8
+ interaction: ChatInputCommandInteraction;
9
+ replies: readonly Message[];
10
+ reply(options: string | MessagePayload | InteractionReplyOptions): Promise<Ok<Message> | Err<"COMMAND_REPLY", {
11
+ error: Error;
12
+ }>>;
13
+ editReply(options: string | MessagePayload | InteractionEditReplyOptions): Promise<Ok<Message> | Err<"COMMAND_NOT_REPLIED", null> | Err<"COMMAND_EDIT_REPLY", {
14
+ error: Error;
15
+ }>>;
16
+ deleteReply(message: MessageResolvable | ReplyPosition): Promise<Ok<void> | Err<"COMMAND_NOT_REPLIED", null> | Err<"COMMAND_REPLY_NOT_FOUND", {
17
+ message: MessageResolvable | ReplyPosition;
18
+ }> | Err<"COMMAND_DELETE_REPLY", {
19
+ error: Error;
20
+ }>>;
21
+ }>;
22
+ export declare function createCommandContext(bot: Bot, interaction: ChatInputCommandInteraction): CommandCtx;
23
+ export {};
@@ -0,0 +1,75 @@
1
+ import { err, Result } from "@l3dev/result";
2
+ import { createGenericContext } from "../context.js";
3
+ export function createCommandContext(bot, interaction) {
4
+ const replies = [];
5
+ return {
6
+ ...createGenericContext(bot),
7
+ interaction,
8
+ replies: replies,
9
+ async reply(options) {
10
+ let promise;
11
+ if (interaction.replied || interaction.deferred) {
12
+ promise = interaction.followUp(options);
13
+ }
14
+ else {
15
+ promise = interaction.reply(options).then((response) => response.fetch());
16
+ }
17
+ const result = await Result.fromPromise(promise, {
18
+ onError: { type: "COMMAND_REPLY" }
19
+ });
20
+ if (!result.ok) {
21
+ return result;
22
+ }
23
+ replies.push(result.value);
24
+ return result;
25
+ },
26
+ async editReply(options) {
27
+ if (!interaction.deferred && !interaction.replied) {
28
+ return err("COMMAND_NOT_REPLIED");
29
+ }
30
+ return await Result.fromPromise(interaction.editReply(options), {
31
+ onError: { type: "COMMAND_EDIT_REPLY" }
32
+ });
33
+ },
34
+ async deleteReply(message = "@original") {
35
+ if (!interaction.deferred && !interaction.replied) {
36
+ return err("COMMAND_NOT_REPLIED");
37
+ }
38
+ let position = -1;
39
+ if (typeof message === "number") {
40
+ const reply = replies[message];
41
+ if (reply) {
42
+ position = message;
43
+ message = reply;
44
+ }
45
+ }
46
+ else if (message === "@last") {
47
+ const lastPosition = replies.length - 1;
48
+ const reply = replies[lastPosition];
49
+ if (reply) {
50
+ position = lastPosition;
51
+ message = reply;
52
+ }
53
+ }
54
+ else if (message === "@original") {
55
+ position = 0;
56
+ }
57
+ else {
58
+ const resolvable = message;
59
+ position = replies.findIndex((reply) => reply.id === (typeof resolvable === "string" ? resolvable : resolvable.id));
60
+ }
61
+ if (position === -1) {
62
+ return err("COMMAND_REPLY_NOT_FOUND", {
63
+ message
64
+ });
65
+ }
66
+ const result = await Result.fromPromise(interaction.deleteReply(message), {
67
+ onError: { type: "COMMAND_DELETE_REPLY" }
68
+ });
69
+ if (result.ok) {
70
+ replies.splice(position, 1);
71
+ }
72
+ return result;
73
+ }
74
+ };
75
+ }
@@ -39,4 +39,3 @@ export function diffCheck({ prev, next }) {
39
39
  }
40
40
  return false;
41
41
  }
42
- //# sourceMappingURL=diff.js.map
@@ -1,2 +1,4 @@
1
1
  export { command, subcommand } from "./command.js";
2
- export type { CommandCtx, CommandMeta, SubcommandMeta, Command, CommandFilter } from "./command.js";
2
+ export type { CommandMeta, SubcommandMeta, Command, CommandFilter } from "./command.js";
3
+ export type { CommandCtx } from "./context.js";
4
+ export type { CommandManager, CommandManagerOptions } from "./manager.js";
@@ -1,2 +1 @@
1
1
  export { command, subcommand } from "./command.js";
2
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,64 @@
1
+ import { type RestOrArray, type SlashCommandBuilder } from "discord.js";
2
+ import type { Command, CommandFilter, ErrorHandler } from "./command.js";
3
+ import type { Bot } from "../bot.js";
4
+ export type CommandManagerOptions = {
5
+ commands?: Command<string, SlashCommandBuilder>[];
6
+ };
7
+ export declare class CommandManager {
8
+ private readonly bot;
9
+ private readonly commands;
10
+ private readonly commandFilters;
11
+ private errorHandler;
12
+ constructor(bot: Bot, options: CommandManagerOptions);
13
+ /**
14
+ * Adds commands to the bot.
15
+ * @param commands The commands to add
16
+ * @returns A result indicating whether the commands were added successfully
17
+ * @example
18
+ * bot.commands.addMany(myCommand1, myCommand2);
19
+ * bot.commands.addMany([myCommand1, myCommand2]);
20
+ */
21
+ addMany(...commands: RestOrArray<Command<string, SlashCommandBuilder>>): import("@l3dev/result").Err<"BOT_ALREADY_STARTED", {
22
+ message: string;
23
+ }> | import("@l3dev/result").Ok<void[]>;
24
+ /**
25
+ * Adds a command to the bot.
26
+ * @param command The command to add
27
+ * @returns A result indicating whether the command was added successfully
28
+ * @example
29
+ * const myCommand = command({
30
+ * meta: {
31
+ * name: 'my-command',
32
+ * },
33
+ * handler: async (ctx) => {
34
+ * // your code here
35
+ * }
36
+ * });
37
+ *
38
+ * bot.commands.add(myCommand);
39
+ */
40
+ add(command: Command<string, SlashCommandBuilder>): import("@l3dev/result").None | import("@l3dev/result").Err<"BOT_ALREADY_STARTED", {
41
+ message: string;
42
+ }>;
43
+ /**
44
+ * Adds a command filter that determines whether a command should be executed.
45
+ * @param filter The filter to add to the bot
46
+ * @example
47
+ * bot.commands.addFilter(async (interaction, command) => {
48
+ * if (interaction.guild.id !== process.env.GUILD_ID) return false;
49
+ * return true;
50
+ * });
51
+ */
52
+ addFilter(filter: CommandFilter): void;
53
+ /**
54
+ * Sets the command error handler for the bot.
55
+ * @param handler The error handler to use
56
+ * @example
57
+ * bot.commands.setErrorHandler(async (ctx, err) => {
58
+ * await ctx.interaction.reply("An error occurred!");
59
+ * });
60
+ */
61
+ setErrorHandler(handler: ErrorHandler): void;
62
+ private onInteraction;
63
+ private executeCommand;
64
+ }