@guardbot/framework 1.0.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/README.md +106 -0
- package/build/index.d.mts +284 -0
- package/build/index.d.ts +284 -0
- package/build/index.js +699 -0
- package/build/index.mjs +665 -0
- package/package.json +46 -0
package/build/index.mjs
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/utils/errors.ts
|
|
9
|
+
import { inspect } from "util";
|
|
10
|
+
var messages = {
|
|
11
|
+
NoOptions: () => `No options object was provided.`,
|
|
12
|
+
InvalidOption: (name) => `No "${name}" option was provided.`,
|
|
13
|
+
InvalidType: (name, expected, actual) => `Expected "${name}" to be of type "${expected}", but got "${actual}".`,
|
|
14
|
+
InvalidValue: (name, expected) => `Expected "${name}" to be any one of the listed values: ${expected.map((v) => `"${v}"`).join(" | ")}`,
|
|
15
|
+
InvalidValues: (name, expected) => `Expected "${name}" to contain only the listed values: ${expected.map((v) => `"${v}"`).join(" | ")}`,
|
|
16
|
+
UnknownComponent: (type, id) => `Encountered an error as there is no "${type}" loaded with the id "${id}".`,
|
|
17
|
+
ComponentLoadError: (type, error) => `Encountered an error while loading the "${type}":
|
|
18
|
+
${inspect(error)}`,
|
|
19
|
+
ComponentAlreadyLoaded: (type, id) => `Encountered an error as a "${type}" with the id "${id}" is already loaded.`,
|
|
20
|
+
AppCommandRegister: (error, guildId) => `Encountered an error while registering commands ${guildId ? `to guild "${guildId}"` : ""}:
|
|
21
|
+
${inspect(error)}`
|
|
22
|
+
};
|
|
23
|
+
var FrameworkError = class extends Error {
|
|
24
|
+
constructor(id, ...values) {
|
|
25
|
+
const message = messages[id](...values);
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = `Error [ ${id} ]`;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var FrameworkTypeError = class extends TypeError {
|
|
31
|
+
constructor(id, ...values) {
|
|
32
|
+
const message = messages[id](...values);
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = `TypeError [ ${id} ]`;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/autocomplete/Autocompleter.ts
|
|
39
|
+
function Autocompleter(options) {
|
|
40
|
+
if (!options) throw new FrameworkError("NoOptions");
|
|
41
|
+
if (typeof options !== "object") throw new FrameworkTypeError("InvalidType", "options", "object", typeof options);
|
|
42
|
+
if (!options.name || !options.name?.length) throw new FrameworkError("InvalidOption", "name");
|
|
43
|
+
if (typeof options.name !== "string") throw new FrameworkTypeError("InvalidType", "name", "string", typeof options.name);
|
|
44
|
+
if (!options.execute) throw new FrameworkError("InvalidOption", "execute");
|
|
45
|
+
if (typeof options.execute !== "function") throw new FrameworkTypeError("InvalidType", "execute", "function", typeof options.execute);
|
|
46
|
+
if (options.disabled !== void 0 && typeof options.disabled !== "boolean") throw new FrameworkTypeError("InvalidType", "disabled", "boolean", typeof options.disabled);
|
|
47
|
+
return {
|
|
48
|
+
id: options.name,
|
|
49
|
+
name: options.name,
|
|
50
|
+
disabled: options.disabled ?? false,
|
|
51
|
+
execute: options.execute
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/commands/ContextCommand.ts
|
|
56
|
+
import { ApplicationIntegrationType as ApplicationIntegrationType2, InteractionContextType as InteractionContextType2 } from "discord.js";
|
|
57
|
+
|
|
58
|
+
// src/utils/utils.ts
|
|
59
|
+
import { ApplicationCommandType, ApplicationIntegrationType, InteractionContextType, PermissionsBitField } from "discord.js";
|
|
60
|
+
import path from "path";
|
|
61
|
+
import fs from "fs";
|
|
62
|
+
async function listFiles(dir) {
|
|
63
|
+
if (!fs.existsSync(dir)) return [];
|
|
64
|
+
const files = [];
|
|
65
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
const fullPath = path.join(dir, item.name);
|
|
68
|
+
if (item.isDirectory()) files.push(...await listFiles(fullPath));
|
|
69
|
+
else if (item.name.endsWith(".js") || item.name.endsWith(".ts")) files.push(fullPath);
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
function isPermissionResolvable(option) {
|
|
74
|
+
try {
|
|
75
|
+
PermissionsBitField.resolve(option);
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function unixTimestamp(date, type = "f") {
|
|
82
|
+
return `<t:${Math.floor(new Date(date).valueOf() / 1e3)}:${type}>`;
|
|
83
|
+
}
|
|
84
|
+
function resolveCommandType(type) {
|
|
85
|
+
return type === "Slash" ? ApplicationCommandType.ChatInput : type === "ContextUser" ? ApplicationCommandType.User : ApplicationCommandType.Message;
|
|
86
|
+
}
|
|
87
|
+
function resolveCommandContexts(contexts) {
|
|
88
|
+
return contexts?.length ? contexts.map((c) => typeof c === "string" ? InteractionContextType[c] : c) : [InteractionContextType.Guild];
|
|
89
|
+
}
|
|
90
|
+
function resolveIntegrationTypes(types) {
|
|
91
|
+
return types?.length ? types.map((c) => typeof c === "string" ? ApplicationIntegrationType[c] : c) : [ApplicationIntegrationType.GuildInstall];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/commands/Command.ts
|
|
95
|
+
var CommandTypes = ["Slash", "Message", "ContextMessage", "ContextUser"];
|
|
96
|
+
function Command(options) {
|
|
97
|
+
if (!options) throw new FrameworkError("NoOptions");
|
|
98
|
+
if (typeof options !== "object") throw new FrameworkTypeError("InvalidType", "options", "object", typeof options);
|
|
99
|
+
if (!options.name || !options.name?.length) throw new FrameworkError("InvalidOption", "name");
|
|
100
|
+
if (typeof options.name !== "string") throw new FrameworkTypeError("InvalidType", "name", "string", typeof options.name);
|
|
101
|
+
if (!options.description || !options.description?.length) throw new FrameworkError("InvalidOption", "description");
|
|
102
|
+
if (typeof options.description !== "string") throw new FrameworkTypeError("InvalidType", "description", "string", typeof options.name);
|
|
103
|
+
if (!options.commandType) throw new FrameworkError("InvalidOption", "commandType");
|
|
104
|
+
if (!CommandTypes.includes(options.commandType)) throw new FrameworkError("InvalidValue", "commandType", CommandTypes);
|
|
105
|
+
if (options.memberPermissions !== void 0 && !isPermissionResolvable(options.memberPermissions)) throw new FrameworkTypeError("InvalidType", "memberPermissions", "PermissionResolvable", typeof options.memberPermissions);
|
|
106
|
+
if (options.clientPermissions !== void 0 && !isPermissionResolvable(options.clientPermissions)) throw new FrameworkTypeError("InvalidType", "clientPermissions", "PermissionResolvable", typeof options.clientPermissions);
|
|
107
|
+
if (options.cooldown && typeof options.cooldown !== "number") throw new FrameworkTypeError("InvalidType", "cooldown", "number", typeof options.cooldown);
|
|
108
|
+
if (options.disabled !== void 0 && typeof options.disabled !== "boolean") throw new FrameworkTypeError("InvalidType", "disabled", "boolean", typeof options.disabled);
|
|
109
|
+
return {
|
|
110
|
+
...options,
|
|
111
|
+
id: `${options.commandType}:${options.name}`,
|
|
112
|
+
name: options.name,
|
|
113
|
+
description: options.description,
|
|
114
|
+
commandType: options.commandType,
|
|
115
|
+
memberPermissions: options.memberPermissions ?? void 0,
|
|
116
|
+
clientPermissions: options.clientPermissions ?? void 0,
|
|
117
|
+
cooldown: options.cooldown ?? void 0,
|
|
118
|
+
disabled: options.disabled ?? false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/commands/ContextCommand.ts
|
|
123
|
+
var CommandScope = ["default", "guild", "global"];
|
|
124
|
+
var CommandTypes2 = ["ContextUser", "ContextMessage"];
|
|
125
|
+
var CommandContexts = ["Guild", "BotDM", "PrivateChannel", 0, 1, 2];
|
|
126
|
+
var IntegrationTypes = ["GuildInstall", "UserInstall", 0, 1];
|
|
127
|
+
function ContextCommand(options) {
|
|
128
|
+
if (!options.commandType) throw new FrameworkError("InvalidOption", "commandType");
|
|
129
|
+
if (typeof options.commandScope !== "string" || !CommandTypes2.includes(options.commandType)) throw new FrameworkError("InvalidValue", "commandType", CommandTypes2);
|
|
130
|
+
if (!options.commandScope) throw new FrameworkError("InvalidOption", "commandScope");
|
|
131
|
+
if (typeof options.commandScope !== "string" || !CommandScope.includes(options.commandScope)) throw new FrameworkError("InvalidValue", "commandScope", CommandScope);
|
|
132
|
+
if (options.commandContexts && !Array.isArray(options.commandContexts)) throw new FrameworkTypeError("InvalidType", "commandContexts", "Array", typeof options.commandContexts);
|
|
133
|
+
if (options.commandContexts && options.commandContexts.some((c) => !CommandContexts.includes(c))) throw new FrameworkError("InvalidValues", "commandContexts", CommandContexts);
|
|
134
|
+
if (options.integrationTypes && !Array.isArray(options.integrationTypes)) throw new FrameworkTypeError("InvalidType", "integrationTypes", "Array", typeof options.integrationTypes);
|
|
135
|
+
if (options.integrationTypes && options.integrationTypes.some((c) => !IntegrationTypes.includes(c))) throw new FrameworkError("InvalidValues", "integrationTypes", IntegrationTypes);
|
|
136
|
+
if (!options.execute) throw new FrameworkError("InvalidOption", "execute");
|
|
137
|
+
if (typeof options.execute !== "function") throw new FrameworkTypeError("InvalidType", "execute", "function", typeof options.execute);
|
|
138
|
+
if (!options.commandContexts) options.commandContexts = [InteractionContextType2.Guild];
|
|
139
|
+
if (!options.integrationTypes) options.integrationTypes = [ApplicationIntegrationType2.GuildInstall];
|
|
140
|
+
return Command({
|
|
141
|
+
...options,
|
|
142
|
+
commandType: options.commandType
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/commands/MessageCommand.ts
|
|
147
|
+
var Contexts = ["BotDM", "Guild"];
|
|
148
|
+
function MessageCommand(options) {
|
|
149
|
+
if (options.aliases && !Array.isArray(options.aliases)) throw new FrameworkTypeError("InvalidType", "aliases", "array", typeof options.aliases);
|
|
150
|
+
if (options.usage && typeof options.usage !== "string") throw new FrameworkTypeError("InvalidType", "usage", "string", typeof options.usage);
|
|
151
|
+
if (options.contexts && !Array.isArray(options.contexts)) throw new FrameworkTypeError("InvalidType", "contexts", "array", typeof options.contexts);
|
|
152
|
+
if (options.contexts && options.contexts.some((c) => !Contexts.includes(c))) throw new FrameworkError("InvalidValues", "contexts", Contexts);
|
|
153
|
+
if (!options.execute) throw new FrameworkError("InvalidOption", "execute");
|
|
154
|
+
if (typeof options.execute !== "function") throw new FrameworkTypeError("InvalidType", "execute", "function", typeof options.execute);
|
|
155
|
+
if (options.devOnly !== void 0 && typeof options.devOnly !== "boolean") throw new FrameworkTypeError("InvalidType", "devOnly", "boolean", typeof options.devOnly);
|
|
156
|
+
if (!options.contexts) options.contexts = ["Guild"];
|
|
157
|
+
if (options.devOnly === void 0) options.devOnly = false;
|
|
158
|
+
return Command({
|
|
159
|
+
...options,
|
|
160
|
+
commandType: "Message"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/commands/SlashCommand.ts
|
|
165
|
+
import { ApplicationIntegrationType as ApplicationIntegrationType3, InteractionContextType as InteractionContextType3 } from "discord.js";
|
|
166
|
+
var CommandScope2 = ["default", "guild", "global"];
|
|
167
|
+
var CommandContexts2 = ["Guild", "BotDM", "PrivateChannel", 0, 1, 2];
|
|
168
|
+
var IntegrationTypes2 = ["GuildInstall", "UserInstall", 0, 1];
|
|
169
|
+
function SlashCommand(options) {
|
|
170
|
+
if (!options.commandScope) throw new FrameworkError("InvalidOption", "commandScope");
|
|
171
|
+
if (typeof options.commandScope !== "string" || !CommandScope2.includes(options.commandScope)) throw new FrameworkError("InvalidValue", "commandScope", CommandScope2);
|
|
172
|
+
if (options.commandContexts && !Array.isArray(options.commandContexts)) throw new FrameworkTypeError("InvalidType", "commandContexts", "Array", typeof options.commandContexts);
|
|
173
|
+
if (options.commandContexts && options.commandContexts.some((c) => !CommandContexts2.includes(c))) throw new FrameworkError("InvalidValues", "commandContexts", CommandContexts2);
|
|
174
|
+
if (options.integrationTypes && !Array.isArray(options.integrationTypes)) throw new FrameworkTypeError("InvalidType", "integrationTypes", "Array", typeof options.integrationTypes);
|
|
175
|
+
if (options.integrationTypes && options.integrationTypes.some((c) => !IntegrationTypes2.includes(c))) throw new FrameworkError("InvalidType", "integrationTypes", IntegrationTypes2);
|
|
176
|
+
if (!options.execute) throw new FrameworkError("InvalidOption", "execute");
|
|
177
|
+
if (typeof options.execute !== "function") throw new FrameworkTypeError("InvalidType", "execute", "function", typeof options.execute);
|
|
178
|
+
if (!options.commandContexts) options.commandContexts = [InteractionContextType3.Guild];
|
|
179
|
+
if (!options.integrationTypes) options.integrationTypes = [ApplicationIntegrationType3.GuildInstall];
|
|
180
|
+
return Command({
|
|
181
|
+
...options,
|
|
182
|
+
commandType: "Slash"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/listeners/Listener.ts
|
|
187
|
+
function Listener(options) {
|
|
188
|
+
if (!options) throw new FrameworkError("NoOptions");
|
|
189
|
+
if (typeof options !== "object") throw new FrameworkTypeError("InvalidType", "options", "object", typeof options);
|
|
190
|
+
if (!options.name || !options.name?.length) throw new FrameworkError("InvalidOption", "name");
|
|
191
|
+
if (typeof options.name !== "string") throw new FrameworkTypeError("InvalidType", "name", "string", typeof options.name);
|
|
192
|
+
if (!options.execute) throw new FrameworkError("InvalidOption", "execute");
|
|
193
|
+
if (typeof options.execute !== "function") throw new FrameworkTypeError("InvalidType", "execute", "function", typeof options.execute);
|
|
194
|
+
if (options.once !== void 0 && typeof options.once !== "boolean") throw new FrameworkTypeError("InvalidType", "once", "boolean", typeof options.once);
|
|
195
|
+
if (options.disabled !== void 0 && typeof options.disabled !== "boolean") throw new FrameworkTypeError("InvalidType", "disabled", "boolean", typeof options.disabled);
|
|
196
|
+
return {
|
|
197
|
+
id: `Client:${options.name}`,
|
|
198
|
+
name: options.name,
|
|
199
|
+
once: options.once ?? false,
|
|
200
|
+
disabled: options.disabled ?? false,
|
|
201
|
+
execute: options.execute
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/FrameworkClient.ts
|
|
206
|
+
import { Collection as Collection2, Client as DiscordClient } from "discord.js";
|
|
207
|
+
|
|
208
|
+
// src/autocomplete/AutocompleteModule.ts
|
|
209
|
+
import { pathToFileURL } from "url";
|
|
210
|
+
import EventEmitter from "events";
|
|
211
|
+
import path2 from "path";
|
|
212
|
+
var AutocompleteModule = class extends EventEmitter {
|
|
213
|
+
client;
|
|
214
|
+
constructor(client) {
|
|
215
|
+
super();
|
|
216
|
+
this.client = client;
|
|
217
|
+
this.client.on("interactionCreate", (interaction) => this._handleInteraction(interaction));
|
|
218
|
+
}
|
|
219
|
+
async load(filepath, reload = false) {
|
|
220
|
+
const completerModule = await import(pathToFileURL(filepath).href);
|
|
221
|
+
const completer = completerModule.autocomplete ?? completerModule.default ?? completerModule;
|
|
222
|
+
if (typeof completer !== "object" || !completer.name || completer.disabled) return false;
|
|
223
|
+
if (!reload && this.client.autocomplete.has(completer.id)) throw new FrameworkError("ComponentAlreadyLoaded", "autocomplete", completer.id);
|
|
224
|
+
completer.filepath = filepath;
|
|
225
|
+
this.client.autocomplete.set(completer.id, completer);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
async loadAll() {
|
|
229
|
+
const listenerDirs = path2.resolve(this.client.rootDir, "autocomplete");
|
|
230
|
+
const files = await listFiles(listenerDirs);
|
|
231
|
+
for (const file of files) {
|
|
232
|
+
try {
|
|
233
|
+
await this.load(path2.resolve(file));
|
|
234
|
+
} catch (error) {
|
|
235
|
+
this.client.emit("error", new FrameworkError("ComponentLoadError", "autocomplete", error));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
reload(id) {
|
|
240
|
+
const completer = this.client.autocomplete.get(id);
|
|
241
|
+
if (!completer) throw new FrameworkError("UnknownComponent", "autocomplete", id);
|
|
242
|
+
this.unload(id, true);
|
|
243
|
+
this.load(completer.filepath, true);
|
|
244
|
+
}
|
|
245
|
+
unload(id, reload = false) {
|
|
246
|
+
if (!this.client.autocomplete.has(id)) throw new FrameworkError("UnknownComponent", "autocomplete", id);
|
|
247
|
+
const completer = this.client.autocomplete.get(id);
|
|
248
|
+
delete __require.cache[completer.filepath];
|
|
249
|
+
if (!reload) this.client.autocomplete.delete(id);
|
|
250
|
+
}
|
|
251
|
+
async _handleInteraction(interaction) {
|
|
252
|
+
if (!interaction.isAutocomplete()) return;
|
|
253
|
+
const option = interaction.options.getFocused(true);
|
|
254
|
+
const command = this.client.commands.get(`Slash:${interaction.commandName}`);
|
|
255
|
+
const completer = this.client.autocomplete.get(option.name);
|
|
256
|
+
if (!option || !command || command.commandType !== "Slash") {
|
|
257
|
+
this.emit("unknown", interaction);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (completer && !completer.disabled) {
|
|
261
|
+
try {
|
|
262
|
+
this.emit("execute", { interaction, command, completer });
|
|
263
|
+
await completer.execute(this.client, interaction, command, option.value);
|
|
264
|
+
this.emit("success", { interaction, command, completer });
|
|
265
|
+
} catch (error) {
|
|
266
|
+
this.emit("success", { interaction, command, completer, error });
|
|
267
|
+
}
|
|
268
|
+
} else if (typeof command.autocomplete === "function" && !command.disabled) {
|
|
269
|
+
try {
|
|
270
|
+
this.emit("execute", { interaction, command, completer });
|
|
271
|
+
await command.autocomplete(this.client, interaction);
|
|
272
|
+
this.emit("success", { interaction, command, completer });
|
|
273
|
+
} catch (error) {
|
|
274
|
+
this.emit("success", { interaction, command, completer, error });
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
this.emit("unknown", interaction);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/listeners/ListenersModule.ts
|
|
283
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
284
|
+
import path3 from "path";
|
|
285
|
+
var ListenerModule = class {
|
|
286
|
+
client;
|
|
287
|
+
constructor(client) {
|
|
288
|
+
this.client = client;
|
|
289
|
+
}
|
|
290
|
+
async load(filepath, reload = false) {
|
|
291
|
+
const listenerModule = await import(pathToFileURL2(filepath).href);
|
|
292
|
+
const listener = listenerModule.listener ?? listenerModule.default ?? listenerModule;
|
|
293
|
+
if (typeof listener !== "object" || !listener.name || listener.disabled) return false;
|
|
294
|
+
if (!reload && this.client.events.has(listener.id)) throw new FrameworkError("ComponentAlreadyLoaded", "listener", listener.id);
|
|
295
|
+
listener.filepath = filepath;
|
|
296
|
+
listener._execute = (...args) => listener.execute(...args);
|
|
297
|
+
this.client[listener.once ? "once" : "on"](listener.name, listener._execute);
|
|
298
|
+
this.client.events.set(listener.id, listener);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
async loadAll() {
|
|
302
|
+
const listenerDirs = path3.resolve(this.client.rootDir, "listeners");
|
|
303
|
+
const files = await listFiles(listenerDirs);
|
|
304
|
+
for (const file of files) {
|
|
305
|
+
try {
|
|
306
|
+
await this.load(path3.resolve(file));
|
|
307
|
+
} catch (error) {
|
|
308
|
+
this.client.emit("error", new FrameworkError("ComponentLoadError", "listener", error));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
reload(id) {
|
|
313
|
+
if (!this.client.events.has(id)) throw new FrameworkError("UnknownComponent", "listener", id);
|
|
314
|
+
const listener = this.client.events.get(id);
|
|
315
|
+
this.unload(id, true);
|
|
316
|
+
this.load(listener.filepath, true);
|
|
317
|
+
}
|
|
318
|
+
unload(id, reload = false) {
|
|
319
|
+
if (!this.client.events.has(id)) throw new FrameworkError("UnknownComponent", "listener", id);
|
|
320
|
+
const listener = this.client.events.get(id);
|
|
321
|
+
if (listener._execute) this.client.off(listener.name, listener._execute);
|
|
322
|
+
delete __require.cache[listener.filepath];
|
|
323
|
+
if (!reload) this.client.events.delete(id);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/commands/CommandsModule.ts
|
|
328
|
+
import { Collection, MessageFlags } from "discord.js";
|
|
329
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
330
|
+
import EventEmitter2 from "events";
|
|
331
|
+
import path4 from "path";
|
|
332
|
+
var CommandsModule = class extends EventEmitter2 {
|
|
333
|
+
client;
|
|
334
|
+
handler = {};
|
|
335
|
+
constructor(client) {
|
|
336
|
+
super();
|
|
337
|
+
this.client = client;
|
|
338
|
+
this.client.on("messageCreate", (message) => this._handleMessage(message));
|
|
339
|
+
this.client.on("interactionCreate", (interaction) => this._handleInteraction(interaction));
|
|
340
|
+
}
|
|
341
|
+
setHandler(key, callback) {
|
|
342
|
+
return this.handler[key] = callback;
|
|
343
|
+
}
|
|
344
|
+
setMessageInterceptor(callback) {
|
|
345
|
+
return this.handler.MessageCommandInterceptor = callback;
|
|
346
|
+
}
|
|
347
|
+
setInteractionInterceptor(callback) {
|
|
348
|
+
return this.handler.InteractionCommandInterceptor = callback;
|
|
349
|
+
}
|
|
350
|
+
async load(filepath, reload = false) {
|
|
351
|
+
const commandModule = await import(pathToFileURL3(filepath).href);
|
|
352
|
+
const command = commandModule?.command ?? commandModule?.default?.default ?? commandModule?.default ?? commandModule;
|
|
353
|
+
if (typeof command !== "object" || !command.name || command.disabled) return false;
|
|
354
|
+
if (!reload && this.client.commands.has(command.id)) throw new FrameworkError("ComponentAlreadyLoaded", "command", command.id);
|
|
355
|
+
command.filepath = filepath;
|
|
356
|
+
this.client.commands.set(command.id, command);
|
|
357
|
+
if (command.commandType === "Message" && Array.isArray(command.aliases)) command.aliases.forEach((alias) => {
|
|
358
|
+
if (typeof alias === "string" && !this.client.aliases.get(alias)) this.client.aliases.set(alias, command.id);
|
|
359
|
+
});
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
async loadAll() {
|
|
363
|
+
const commandsDir = path4.resolve(this.client.rootDir, "commands");
|
|
364
|
+
const files = await listFiles(commandsDir);
|
|
365
|
+
for (const file of files) {
|
|
366
|
+
try {
|
|
367
|
+
await this.load(path4.resolve(file));
|
|
368
|
+
} catch (error) {
|
|
369
|
+
this.client.emit("error", new FrameworkError("ComponentLoadError", "commands", error));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
reload(id) {
|
|
374
|
+
if (!this.client.commands.has(id)) throw new FrameworkError("UnknownComponent", "commands", id);
|
|
375
|
+
const command = this.client.commands.get(id);
|
|
376
|
+
this.unload(id, true);
|
|
377
|
+
this.load(command.filepath, true);
|
|
378
|
+
}
|
|
379
|
+
unload(id, reload = false) {
|
|
380
|
+
if (!this.client.commands.has(id)) throw new FrameworkError("UnknownComponent", "commands", id);
|
|
381
|
+
const command = this.client.commands.get(id);
|
|
382
|
+
delete __require.cache[command.filepath];
|
|
383
|
+
if (!reload) this.client.commands.delete(id);
|
|
384
|
+
}
|
|
385
|
+
async registerOnStart(guildIds, commands) {
|
|
386
|
+
for (const guildId of guildIds) {
|
|
387
|
+
try {
|
|
388
|
+
await this.publishGuild(guildId, commands);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
this.client.emit("error", new FrameworkError("AppCommandRegister", error, guildId));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async publishGlobal(commands) {
|
|
395
|
+
if (!this.client.application) return false;
|
|
396
|
+
const commandsToSet = (commands ?? this.client.commands).filter((c) => c.commandType !== "Message" && c.commandScope !== "guild" && !c.disabled).map((c) => this._getCommandData(c));
|
|
397
|
+
return this.client.application.commands.set(commandsToSet);
|
|
398
|
+
}
|
|
399
|
+
async publishGuild(guildId, commands) {
|
|
400
|
+
const commandsToSet = (commands ?? this.client.commands).filter((c) => c.commandType !== "Message" && c.commandScope !== "global" && !c.disabled).map((c) => this._getCommandData(c));
|
|
401
|
+
const guild = await this.client.guilds.fetch({ guild: guildId, force: false });
|
|
402
|
+
if (guild) guild.commands.set(commandsToSet);
|
|
403
|
+
}
|
|
404
|
+
async _handleMessage(message) {
|
|
405
|
+
if (!message.content?.length || message.author.bot) return;
|
|
406
|
+
if (this.handler.MessageCommandInterceptor) {
|
|
407
|
+
const shouldContinue = await this.handler.MessageCommandInterceptor(message);
|
|
408
|
+
if (!shouldContinue) return;
|
|
409
|
+
}
|
|
410
|
+
const prefixRegex = new RegExp(`^(<@!?${message.client.user.id}>${this.client.prefix ? `|${this.client.prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}` : ""})\\s*`);
|
|
411
|
+
if (!prefixRegex.test(message.content.toLowerCase())) return;
|
|
412
|
+
const matchedPrefix = message.content.toLowerCase().match(prefixRegex)?.[1];
|
|
413
|
+
if (!matchedPrefix) return;
|
|
414
|
+
const args = (message.content || "").slice(matchedPrefix.length).trim().split(/ +/);
|
|
415
|
+
const commandName = args.shift()?.toLowerCase();
|
|
416
|
+
if (!commandName) return;
|
|
417
|
+
const commandId = `Message:${commandName}`;
|
|
418
|
+
const command = this.client.commands.get(commandId) || this.client.commands.get(this.client.aliases.get(commandId) || "");
|
|
419
|
+
if (!command || command.commandType !== "Message" || command.disabled || command.devOnly && !this.client.developers.includes(message.author.id)) return;
|
|
420
|
+
if (!message.inGuild() && !command.contexts.includes("BotDM") || message.inGuild() && !command.contexts.includes("Guild")) return;
|
|
421
|
+
if (message.inGuild() && command.memberPermissions) {
|
|
422
|
+
const executor = await message.guild.members.fetch({ user: message.author.id, force: false });
|
|
423
|
+
const memberPerms = executor.permissionsIn(message.channel) ?? executor.permissions;
|
|
424
|
+
if (!memberPerms.has(command.memberPermissions)) {
|
|
425
|
+
const missingPermsArray = memberPerms.missing(command.memberPermissions);
|
|
426
|
+
if (typeof this.handler.onMemberPermissions === "function") return this.handler.onMemberPermissions(message, command, missingPermsArray);
|
|
427
|
+
else return await message.reply({ content: "\u274C You don't have the required permission(s) to use this command." }).then((m) => {
|
|
428
|
+
setTimeout(() => m.delete().catch((e) => null), 1e4);
|
|
429
|
+
}).catch(() => null);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (message.inGuild() && command.clientPermissions) {
|
|
433
|
+
const clientMember = await message.guild.members.fetchMe({ force: false });
|
|
434
|
+
const clientPerms = clientMember.permissionsIn(message.channel) ?? clientMember.permissions;
|
|
435
|
+
if (!clientMember.permissions.has(command.clientPermissions)) {
|
|
436
|
+
const missingPermsArray = clientPerms.missing(command.clientPermissions);
|
|
437
|
+
if (typeof this.handler.onClientPermissions === "function") return this.handler.onClientPermissions(message, command, missingPermsArray);
|
|
438
|
+
else return await message.reply({ content: `\u274C ${clientMember.displayName} requires ${missingPermsArray.map((p) => `\` ${p.replace(/([A-Z])/g, (_, l, i) => i === 0 ? l : ` ${l}`)} \``).join(" ")} permission(s) to run this command.` }).then((m) => {
|
|
439
|
+
setTimeout(() => m.delete().catch((e) => null), 1e4);
|
|
440
|
+
}).catch(() => null);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (command.cooldown && command.cooldown > 1e3) {
|
|
444
|
+
if (!this.client.cooldowns.has(commandId)) this.client.cooldowns.set(commandId, new Collection());
|
|
445
|
+
const commandCooldowns = this.client.cooldowns.get(commandId);
|
|
446
|
+
if (commandCooldowns) {
|
|
447
|
+
if (commandCooldowns.has(message.author.id)) {
|
|
448
|
+
const expirationDate = new Date((commandCooldowns.get(message.author.id) || 0) + command.cooldown);
|
|
449
|
+
if (expirationDate.valueOf() - Date.now() > 1e3) {
|
|
450
|
+
if (typeof this.handler.onCooldown === "function") return this.handler.onCooldown(message, command, expirationDate);
|
|
451
|
+
else return await message.reply({ content: `\u274C Slow down and try the Command Again **${unixTimestamp(new Date(expirationDate), "R")}**.` }).then((m) => {
|
|
452
|
+
setTimeout(() => m.delete().catch((e) => null), expirationDate.valueOf() - Date.now());
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
commandCooldowns.set(message.author.id, (/* @__PURE__ */ new Date()).valueOf());
|
|
457
|
+
setTimeout(() => commandCooldowns.delete(message.author.id), command.cooldown);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
this.emit("execute", { context: message, command });
|
|
462
|
+
await command.execute(this.client, message, args);
|
|
463
|
+
this.emit("success", { context: message, command });
|
|
464
|
+
} catch (error) {
|
|
465
|
+
this.emit("error", { context: message, command, error });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async _handleInteraction(interaction) {
|
|
469
|
+
if (!interaction.isChatInputCommand() && !interaction.isContextMenuCommand()) return;
|
|
470
|
+
if (this.handler.InteractionCommandInterceptor) {
|
|
471
|
+
const shouldContinue = await this.handler.InteractionCommandInterceptor(interaction);
|
|
472
|
+
if (!shouldContinue) return;
|
|
473
|
+
}
|
|
474
|
+
const commandId = `${this._getCommandId(interaction)}:${interaction.commandName}`;
|
|
475
|
+
const command = this.client.commands.get(commandId);
|
|
476
|
+
if (!command || command.disabled || command.commandType === "Message") {
|
|
477
|
+
this.emit("unknown", interaction);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (interaction.guild && interaction.channel && !interaction.channel.isDMBased() && command.clientPermissions) {
|
|
481
|
+
const clientMember = await interaction.guild.members.fetchMe({ force: false });
|
|
482
|
+
const clientPerms = interaction.channel.isSendable() ? clientMember.permissionsIn(interaction.channel) : clientMember.permissions;
|
|
483
|
+
if (!clientPerms.has(command.clientPermissions)) {
|
|
484
|
+
const missingPermsArray = clientPerms.missing(command.clientPermissions);
|
|
485
|
+
if (typeof this.handler.onClientPermissions === "function") return this.handler.onClientPermissions(interaction, command, missingPermsArray);
|
|
486
|
+
else return await interaction.reply({ content: `\u274C ${clientMember.displayName} requires ${missingPermsArray.map((p) => `\` ${p.replace(/([A-Z])/g, (_, l, i) => i === 0 ? l : ` ${l}`)} \``).join(" ")} permission(s) to run this command.`, flags: MessageFlags.Ephemeral });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (command.cooldown && command.cooldown > 1e3) {
|
|
490
|
+
if (!this.client.cooldowns.has(commandId)) this.client.cooldowns.set(commandId, new Collection());
|
|
491
|
+
const commandCooldowns = this.client.cooldowns.get(commandId);
|
|
492
|
+
if (commandCooldowns) {
|
|
493
|
+
if (commandCooldowns.has(interaction.user.id)) {
|
|
494
|
+
const expirationDate = new Date((commandCooldowns.get(interaction.user.id) || 0) + command.cooldown);
|
|
495
|
+
if (expirationDate.valueOf() - Date.now() > 1e3) {
|
|
496
|
+
if (typeof this.handler.onCooldown === "function") return this.handler.onCooldown(interaction, command, expirationDate);
|
|
497
|
+
else return await interaction.reply({ content: `\u274C Slow down and try the Command Again **${unixTimestamp(new Date(expirationDate), "R")}**.`, flags: MessageFlags.Ephemeral });
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
commandCooldowns.set(interaction.user.id, (/* @__PURE__ */ new Date()).valueOf());
|
|
501
|
+
setTimeout(() => commandCooldowns.delete(interaction.user.id), command.cooldown);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
this.emit("execute", { context: interaction, command });
|
|
506
|
+
await command.execute(this.client, interaction);
|
|
507
|
+
this.emit("success", { context: interaction, command });
|
|
508
|
+
} catch (error) {
|
|
509
|
+
this.emit("error", { context: interaction, command, error });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
_getCommandId(interaction) {
|
|
513
|
+
if (interaction.isChatInputCommand()) return "Slash";
|
|
514
|
+
else if (interaction.isUserContextMenuCommand()) return "ContextUser";
|
|
515
|
+
else if (interaction.isMessageContextMenuCommand()) return "ContextMessage";
|
|
516
|
+
return "unknown";
|
|
517
|
+
}
|
|
518
|
+
_getCommandData(command) {
|
|
519
|
+
return {
|
|
520
|
+
name: command.name,
|
|
521
|
+
description: command.description,
|
|
522
|
+
type: resolveCommandType(command.commandType),
|
|
523
|
+
defaultMemberPermissions: command.memberPermissions ?? null,
|
|
524
|
+
contexts: resolveCommandContexts(command.commandContexts),
|
|
525
|
+
integrationTypes: resolveIntegrationTypes(command.integrationTypes),
|
|
526
|
+
nameLocalizations: command.nameLocalizations,
|
|
527
|
+
descriptionLocalizations: command.descriptionLocalizations,
|
|
528
|
+
...command.commandType === "Slash" ? { options: command.options ?? [] } : {}
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/FrameworkClient.ts
|
|
534
|
+
import fs2 from "fs";
|
|
535
|
+
var FrameworkClient = class extends DiscordClient {
|
|
536
|
+
rootDir;
|
|
537
|
+
prefix;
|
|
538
|
+
developers;
|
|
539
|
+
constructor(frameworkOptions) {
|
|
540
|
+
super(frameworkOptions.clientOptions);
|
|
541
|
+
this.prefix = frameworkOptions.prefix ?? "!";
|
|
542
|
+
this.rootDir = frameworkOptions.rootDir ?? (fs2.existsSync("./src") ? "./src" : "./");
|
|
543
|
+
this.developers = frameworkOptions.developers || [];
|
|
544
|
+
this.autocomplete = new Collection2();
|
|
545
|
+
this.events = new Collection2();
|
|
546
|
+
this.aliases = new Collection2();
|
|
547
|
+
this.commands = new Collection2();
|
|
548
|
+
this.cooldowns = new Collection2();
|
|
549
|
+
this.autocompleteModule = new AutocompleteModule(this);
|
|
550
|
+
this.listenerModule = new ListenerModule(this);
|
|
551
|
+
this.commandsModule = new CommandsModule(this);
|
|
552
|
+
this.on("interactionCreate", (interaction) => {
|
|
553
|
+
if (interaction.isAnySelectMenu()) return this.emit("selectMenuInteraction", interaction);
|
|
554
|
+
else if (interaction.isModalSubmit()) return this.emit("modalSubmitInteraction", interaction);
|
|
555
|
+
else if (interaction.isButton()) return this.emit("buttonInteraction", interaction);
|
|
556
|
+
});
|
|
557
|
+
this.once("clientReady", async () => {
|
|
558
|
+
if (frameworkOptions.registerOnStart && frameworkOptions.guildsToRegister?.length) {
|
|
559
|
+
await this.commandsModule.registerOnStart(frameworkOptions.guildsToRegister);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
async start(token) {
|
|
564
|
+
await this.autocompleteModule.loadAll();
|
|
565
|
+
await this.commandsModule.loadAll();
|
|
566
|
+
await this.listenerModule.loadAll();
|
|
567
|
+
await this.login(token);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// src/extensions/Message.ts
|
|
572
|
+
import { Message as Message2, MessageCollector, InteractionCollector, InteractionType } from "discord.js";
|
|
573
|
+
function prefix() {
|
|
574
|
+
return this.client.prefix;
|
|
575
|
+
}
|
|
576
|
+
function isDeveloper() {
|
|
577
|
+
return this.client.developers.includes(this.author.id);
|
|
578
|
+
}
|
|
579
|
+
function awaitMemberResponse(options = {}) {
|
|
580
|
+
return new Promise((resolve, reject) => {
|
|
581
|
+
if (!this.channel) {
|
|
582
|
+
return reject(new Error("Channel not available for this message."));
|
|
583
|
+
}
|
|
584
|
+
const collector = new MessageCollector(this.channel, {
|
|
585
|
+
time: 6e4,
|
|
586
|
+
filter: (m) => m.author.id === this.author.id && (m.content || "").length > 0,
|
|
587
|
+
...options,
|
|
588
|
+
max: 1
|
|
589
|
+
});
|
|
590
|
+
collector.on("end", (messages2, reason) => {
|
|
591
|
+
const message = messages2.first();
|
|
592
|
+
if (message) {
|
|
593
|
+
if (options.deleteMessage) message.delete().catch(() => null);
|
|
594
|
+
resolve(message);
|
|
595
|
+
} else {
|
|
596
|
+
reject(new Error(reason));
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function createModalSubmitCollector(options = {}) {
|
|
602
|
+
return new InteractionCollector(this.client, {
|
|
603
|
+
...options,
|
|
604
|
+
interactionType: InteractionType.ModalSubmit,
|
|
605
|
+
message: this
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
function awaitModalSubmitComponent(options = {}) {
|
|
609
|
+
return new Promise((resolve, reject) => {
|
|
610
|
+
const collector = new InteractionCollector(this.client, {
|
|
611
|
+
time: 6e4,
|
|
612
|
+
filter: (i) => i.user.id === this.author.id,
|
|
613
|
+
...options,
|
|
614
|
+
interactionType: InteractionType.ModalSubmit,
|
|
615
|
+
message: this,
|
|
616
|
+
max: 1
|
|
617
|
+
});
|
|
618
|
+
collector.on("end", (interactions, reason) => {
|
|
619
|
+
const interaction = interactions.first();
|
|
620
|
+
if (interaction) resolve(interaction);
|
|
621
|
+
else reject(new Error(reason));
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
Object.defineProperties(Message2.prototype, {
|
|
626
|
+
prefix: { get: prefix, configurable: true },
|
|
627
|
+
isDeveloper: { value: isDeveloper, writable: true, configurable: true },
|
|
628
|
+
awaitMemberResponse: { value: awaitMemberResponse, writable: true, configurable: true },
|
|
629
|
+
createModalSubmitCollector: { value: createModalSubmitCollector, writable: true, configurable: true },
|
|
630
|
+
awaitModalSubmitComponent: { value: awaitModalSubmitComponent, writable: true, configurable: true }
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// src/Builders.ts
|
|
634
|
+
import {
|
|
635
|
+
SlashCommandAttachmentOption,
|
|
636
|
+
SlashCommandBooleanOption,
|
|
637
|
+
SlashCommandChannelOption,
|
|
638
|
+
SlashCommandIntegerOption,
|
|
639
|
+
SlashCommandMentionableOption,
|
|
640
|
+
SlashCommandNumberOption,
|
|
641
|
+
SlashCommandRoleOption,
|
|
642
|
+
SlashCommandStringOption,
|
|
643
|
+
SlashCommandSubcommandBuilder,
|
|
644
|
+
SlashCommandSubcommandGroupBuilder,
|
|
645
|
+
SlashCommandOptionsOnlyBuilder
|
|
646
|
+
} from "discord.js";
|
|
647
|
+
export {
|
|
648
|
+
SlashCommandAttachmentOption as AttachmentOption,
|
|
649
|
+
Autocompleter,
|
|
650
|
+
SlashCommandBooleanOption as BooleanOption,
|
|
651
|
+
SlashCommandChannelOption as ChannelOption,
|
|
652
|
+
ContextCommand,
|
|
653
|
+
FrameworkClient,
|
|
654
|
+
SlashCommandIntegerOption as IntegerOption,
|
|
655
|
+
Listener,
|
|
656
|
+
SlashCommandMentionableOption as MentionableOption,
|
|
657
|
+
MessageCommand,
|
|
658
|
+
SlashCommandNumberOption as NumberOption,
|
|
659
|
+
SlashCommandRoleOption as RoleOption,
|
|
660
|
+
SlashCommand,
|
|
661
|
+
SlashCommandOptionsOnlyBuilder as SlashCommandOptionsBuilder,
|
|
662
|
+
SlashCommandStringOption as StringOption,
|
|
663
|
+
SlashCommandSubcommandBuilder as SubcommandBuilder,
|
|
664
|
+
SlashCommandSubcommandGroupBuilder as SubcommandGroupBuilder
|
|
665
|
+
};
|