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.
- package/build/bot.d.ts +15 -19
- package/build/bot.js +28 -139
- package/build/commands/command.d.ts +26 -7
- package/build/commands/command.js +71 -37
- package/build/commands/context.d.ts +23 -0
- package/build/commands/context.js +75 -0
- package/build/commands/diff.js +0 -1
- package/build/commands/index.d.ts +3 -1
- package/build/commands/index.js +0 -1
- package/build/commands/manager.d.ts +64 -0
- package/build/commands/manager.js +138 -0
- package/build/commands/registration.js +0 -1
- package/build/context.d.ts +1 -1
- package/build/context.js +2 -3
- package/build/events/index.d.ts +1 -0
- package/build/events/index.js +0 -1
- package/build/events/listener.js +0 -1
- package/build/events/manager.d.ts +40 -0
- package/build/events/manager.js +86 -0
- package/build/index.js +0 -1
- package/build/logger.js +0 -1
- package/build/options/index.js +0 -1
- package/build/options/option.d.ts +12 -30
- package/build/options/option.js +7 -15
- package/build/options/options.d.ts +39 -30
- package/build/options/options.js +59 -36
- package/build/permissions.d.ts +4 -4
- package/build/permissions.js +2 -3
- package/build/plugin.js +0 -1
- package/build/utility-types.js +0 -1
- package/build/utils.js +0 -1
- package/package.json +6 -3
- package/build/bot.js.map +0 -1
- package/build/commands/command.js.map +0 -1
- package/build/commands/diff.js.map +0 -1
- package/build/commands/index.js.map +0 -1
- package/build/commands/registration.js.map +0 -1
- package/build/context.js.map +0 -1
- package/build/events/index.js.map +0 -1
- package/build/events/listener.js.map +0 -1
- package/build/index.js.map +0 -1
- package/build/logger.js.map +0 -1
- package/build/options/index.js.map +0 -1
- package/build/options/option.js.map +0 -1
- package/build/options/options.js.map +0 -1
- package/build/permissions.js.map +0 -1
- package/build/plugin.js.map +0 -1
- package/build/utility-types.js.map +0 -1
- package/build/utils.js.map +0 -1
package/build/bot.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import
|
|
3
|
-
import
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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 {
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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.
|
|
124
|
-
|
|
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
|
|
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 {
|
|
6
|
-
|
|
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,
|
|
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
|
-
|
|
37
|
+
subcommand: subcommandName
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
return await subcommand.execute(ctx);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/build/commands/diff.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { command, subcommand } from "./command.js";
|
|
2
|
-
export type {
|
|
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";
|
package/build/commands/index.js
CHANGED
|
@@ -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
|
+
}
|