@ezzi/base 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 +29 -0
- package/dist/app.d.ts +47 -0
- package/dist/app.js +54 -0
- package/dist/bootstrap.d.ts +68 -0
- package/dist/bootstrap.js +35 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +42 -0
- package/dist/creators/commands/command.d.ts +140 -0
- package/dist/creators/commands/command.js +63 -0
- package/dist/creators/commands/context.d.ts +33 -0
- package/dist/creators/commands/context.js +204 -0
- package/dist/creators/commands/manager.d.ts +50 -0
- package/dist/creators/commands/manager.js +560 -0
- package/dist/creators/events/event.d.ts +21 -0
- package/dist/creators/events/event.js +10 -0
- package/dist/creators/events/manager.d.ts +12 -0
- package/dist/creators/events/manager.js +71 -0
- package/dist/creators/index.d.ts +5 -0
- package/dist/creators/index.js +11 -0
- package/dist/creators/manager.d.ts +6 -0
- package/dist/creators/manager.js +12 -0
- package/dist/creators/responders/emit.d.ts +12 -0
- package/dist/creators/responders/emit.js +10 -0
- package/dist/creators/responders/manager.d.ts +14 -0
- package/dist/creators/responders/manager.js +61 -0
- package/dist/creators/responders/responder.d.ts +45 -0
- package/dist/creators/responders/responder.js +26 -0
- package/dist/creators/setup.d.ts +25 -0
- package/dist/creators/setup.js +86 -0
- package/dist/env.d.ts +25 -0
- package/dist/env.js +15 -0
- package/dist/error.d.ts +7 -0
- package/dist/error.js +78 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/modules.d.ts +9 -0
- package/dist/modules.js +18 -0
- package/dist/standard-schema.d.ts +59 -0
- package/dist/standard-schema.js +0 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/logger.d.ts +12 -0
- package/dist/tools/logger.js +23 -0
- package/dist/tools/parseCommand.d.ts +15 -0
- package/dist/tools/parseCommand.js +144 -0
- package/dist/utils/router.d.ts +9 -0
- package/dist/utils/router.js +24 -0
- package/dist/utils/types.d.ts +7 -0
- package/dist/utils/types.js +0 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.js +5 -0
- package/package.json +79 -0
- package/tsconfig/base.json +21 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
// src/creators/commands/manager.ts
|
|
2
|
+
import { isDefined, limitText, spaceBuilder } from "@magicyan/discord";
|
|
3
|
+
import ck from "chalk";
|
|
4
|
+
import {
|
|
5
|
+
ApplicationCommandOptionType,
|
|
6
|
+
ApplicationCommandType,
|
|
7
|
+
Collection,
|
|
8
|
+
GuildMember
|
|
9
|
+
} from "discord.js";
|
|
10
|
+
import {
|
|
11
|
+
IgnoreCommand
|
|
12
|
+
} from "./command.js";
|
|
13
|
+
import { CommandContext } from "./context.js";
|
|
14
|
+
function resolveOptionValueType(type) {
|
|
15
|
+
switch (type) {
|
|
16
|
+
case ApplicationCommandOptionType.String:
|
|
17
|
+
return "text";
|
|
18
|
+
case ApplicationCommandOptionType.Number:
|
|
19
|
+
return "number";
|
|
20
|
+
case ApplicationCommandOptionType.Integer:
|
|
21
|
+
return "integer";
|
|
22
|
+
case ApplicationCommandOptionType.User:
|
|
23
|
+
return "user";
|
|
24
|
+
case ApplicationCommandOptionType.Role:
|
|
25
|
+
return "role";
|
|
26
|
+
case ApplicationCommandOptionType.Mentionable:
|
|
27
|
+
return "mentionable";
|
|
28
|
+
case ApplicationCommandOptionType.Channel:
|
|
29
|
+
return "channel";
|
|
30
|
+
case ApplicationCommandOptionType.Attachment:
|
|
31
|
+
return "attachment";
|
|
32
|
+
case ApplicationCommandOptionType.Boolean:
|
|
33
|
+
return "boolean";
|
|
34
|
+
default:
|
|
35
|
+
return "text";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function resolveMissingPermissions(member, required) {
|
|
39
|
+
if (!member || !required.length)
|
|
40
|
+
return [];
|
|
41
|
+
return required.filter((perm) => !member.permissions.has(perm));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class CommandManager {
|
|
45
|
+
app;
|
|
46
|
+
constructor(app) {
|
|
47
|
+
this.app = app;
|
|
48
|
+
}
|
|
49
|
+
get config() {
|
|
50
|
+
return this.app.config.commands;
|
|
51
|
+
}
|
|
52
|
+
collection = new Collection;
|
|
53
|
+
runtimeCollection = new Collection;
|
|
54
|
+
aliasCollection = new Collection;
|
|
55
|
+
commandRunners = new Collection;
|
|
56
|
+
autocompleteRunners = new Collection;
|
|
57
|
+
optionDefs = new Collection;
|
|
58
|
+
shortcuts = new Collection;
|
|
59
|
+
botPermissionsMap = new Collection;
|
|
60
|
+
logs = [];
|
|
61
|
+
formatName(name, type = ApplicationCommandType.ChatInput) {
|
|
62
|
+
return limitText(type === ApplicationCommandType.ChatInput ? name.toLowerCase().replaceAll(" ", "") : name, 32);
|
|
63
|
+
}
|
|
64
|
+
clear() {
|
|
65
|
+
this.runtimeCollection = new Collection(this.collection);
|
|
66
|
+
for (const [k, v] of this.aliasCollection) {
|
|
67
|
+
this.runtimeCollection.set(k, v);
|
|
68
|
+
}
|
|
69
|
+
this.collection.clear();
|
|
70
|
+
this.aliasCollection.clear();
|
|
71
|
+
}
|
|
72
|
+
getAutocompleteHandler(...path) {
|
|
73
|
+
const commandName = path[0];
|
|
74
|
+
const type = ApplicationCommandType.ChatInput;
|
|
75
|
+
const resolved = `/${type}/${path.filter(isDefined).join("/")}`;
|
|
76
|
+
return this.autocompleteRunners.get(resolved) ?? this.autocompleteRunners.get(`/${type}/${commandName}`);
|
|
77
|
+
}
|
|
78
|
+
getHandler(type, ...path) {
|
|
79
|
+
const commandName = path[0];
|
|
80
|
+
const resolved = `/${type}/${path.filter(isDefined).join("/")}`;
|
|
81
|
+
return this.commandRunners.get(resolved) ?? this.commandRunners.get(`/${type}/${commandName}`);
|
|
82
|
+
}
|
|
83
|
+
getBotPermissions(path) {
|
|
84
|
+
return this.botPermissionsMap.get(path);
|
|
85
|
+
}
|
|
86
|
+
resolveBotPermissions(commandName, subcommandGroup, subcommand) {
|
|
87
|
+
const type = ApplicationCommandType.ChatInput;
|
|
88
|
+
const parts = [commandName, subcommandGroup, subcommand].filter(isDefined);
|
|
89
|
+
const specificPath = `/${type}/${parts.join("/")}`;
|
|
90
|
+
const basePath = `/${type}/${commandName}`;
|
|
91
|
+
return this.botPermissionsMap.get(specificPath) ?? this.botPermissionsMap.get(basePath);
|
|
92
|
+
}
|
|
93
|
+
resolvePrefixCommand(commandName, args, resolvedCommandData) {
|
|
94
|
+
const type = ApplicationCommandType.ChatInput;
|
|
95
|
+
const shortcutPath = this.shortcuts.get(commandName);
|
|
96
|
+
if (shortcutPath) {
|
|
97
|
+
const runners2 = this.commandRunners.get(shortcutPath);
|
|
98
|
+
const defs2 = this.optionDefs.get(shortcutPath) ?? [];
|
|
99
|
+
const botPermissions2 = this.botPermissionsMap.get(shortcutPath);
|
|
100
|
+
if (runners2)
|
|
101
|
+
return { runners: runners2, optionDefs: defs2, botPermissions: botPermissions2 };
|
|
102
|
+
}
|
|
103
|
+
const rawCommand = resolvedCommandData ?? this.runtimeCollection.get(commandName) ?? this.collection.get(commandName) ?? this.aliasCollection.get(commandName);
|
|
104
|
+
if (!rawCommand)
|
|
105
|
+
return;
|
|
106
|
+
const path = [rawCommand.name];
|
|
107
|
+
if (rawCommand.modules && rawCommand.modules.length > 0) {
|
|
108
|
+
const nextArg = args[0]?.toLowerCase();
|
|
109
|
+
if (nextArg) {
|
|
110
|
+
const foundModule = rawCommand.modules.find((m) => m.name.toLowerCase() === nextArg || m.aliases?.some((a) => a.toLowerCase() === nextArg));
|
|
111
|
+
if (foundModule) {
|
|
112
|
+
args.shift();
|
|
113
|
+
path.push(foundModule.name);
|
|
114
|
+
if (foundModule.type === ApplicationCommandOptionType.SubcommandGroup) {
|
|
115
|
+
const subArg = args[0]?.toLowerCase();
|
|
116
|
+
if (subArg) {
|
|
117
|
+
const foundSub = rawCommand.modules.find((m) => m.type === ApplicationCommandOptionType.Subcommand && m.group === foundModule.name && (m.name.toLowerCase() === subArg || m.aliases?.some((a) => a.toLowerCase() === subArg)));
|
|
118
|
+
if (foundSub) {
|
|
119
|
+
args.shift();
|
|
120
|
+
path.push(foundSub.name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const resolved = `/${type}/${path.join("/")}`;
|
|
128
|
+
const runners = this.commandRunners.get(resolved);
|
|
129
|
+
if (!runners)
|
|
130
|
+
return;
|
|
131
|
+
const defs = this.optionDefs.get(resolved) ?? [];
|
|
132
|
+
const botPermissions = this.botPermissionsMap.get(resolved);
|
|
133
|
+
return { runners, optionDefs: defs, botPermissions };
|
|
134
|
+
}
|
|
135
|
+
getTitle(data) {
|
|
136
|
+
const type = data.type ?? ApplicationCommandType.ChatInput;
|
|
137
|
+
if (type === ApplicationCommandType.User) {
|
|
138
|
+
return ["{☰}", "User context menu"];
|
|
139
|
+
}
|
|
140
|
+
if (type === ApplicationCommandType.Message) {
|
|
141
|
+
return ["{☰}", "Message context menu"];
|
|
142
|
+
}
|
|
143
|
+
const ignoreValue = data.ignore;
|
|
144
|
+
if (ignoreValue === IgnoreCommand.Slash) {
|
|
145
|
+
return ["{~}", "Prefix command"];
|
|
146
|
+
}
|
|
147
|
+
if (ignoreValue === IgnoreCommand.Message) {
|
|
148
|
+
return ["{/}", "Slash command"];
|
|
149
|
+
}
|
|
150
|
+
return ["{☰}", "Hybrid command"];
|
|
151
|
+
}
|
|
152
|
+
buildOptions(options, path) {
|
|
153
|
+
const resolved = [];
|
|
154
|
+
for (const option of options) {
|
|
155
|
+
const description = option.description ?? option.name;
|
|
156
|
+
if ("autocomplete" in option && option.autocomplete && typeof option.autocomplete === "function") {
|
|
157
|
+
this.autocompleteRunners.set(`${path}/${option.name}`, option.autocomplete);
|
|
158
|
+
}
|
|
159
|
+
switch (option.type) {
|
|
160
|
+
case ApplicationCommandOptionType.SubcommandGroup: {
|
|
161
|
+
const {
|
|
162
|
+
options: subcommands,
|
|
163
|
+
defaultMemberPermissions,
|
|
164
|
+
botPermissions,
|
|
165
|
+
...data
|
|
166
|
+
} = option;
|
|
167
|
+
if (botPermissions?.length) {
|
|
168
|
+
this.botPermissionsMap.set(`${path}/${data.name}`, botPermissions);
|
|
169
|
+
}
|
|
170
|
+
resolved.push({
|
|
171
|
+
...data,
|
|
172
|
+
description,
|
|
173
|
+
...defaultMemberPermissions !== undefined ? { defaultMemberPermissions } : {},
|
|
174
|
+
options: this.buildOptions(subcommands, `${path}/${data.name}`)
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
case ApplicationCommandOptionType.Subcommand: {
|
|
179
|
+
const {
|
|
180
|
+
options: subOpts,
|
|
181
|
+
defaultMemberPermissions,
|
|
182
|
+
botPermissions,
|
|
183
|
+
...data
|
|
184
|
+
} = option;
|
|
185
|
+
if (botPermissions?.length) {
|
|
186
|
+
this.botPermissionsMap.set(`${path}/${data.name}`, botPermissions);
|
|
187
|
+
}
|
|
188
|
+
resolved.push({
|
|
189
|
+
...data,
|
|
190
|
+
description,
|
|
191
|
+
...defaultMemberPermissions !== undefined ? { defaultMemberPermissions } : {},
|
|
192
|
+
...subOpts?.length ? {
|
|
193
|
+
options: this.buildOptions(subOpts, `${path}/${data.name}`)
|
|
194
|
+
} : {}
|
|
195
|
+
});
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
case ApplicationCommandOptionType.String:
|
|
199
|
+
case ApplicationCommandOptionType.Integer:
|
|
200
|
+
case ApplicationCommandOptionType.Number: {
|
|
201
|
+
const { choices, autocomplete, ...data } = option;
|
|
202
|
+
const validation = data.type === ApplicationCommandOptionType.String ? { minLength: data.minLength, maxLength: data.maxLength } : { minValue: data.minValue, maxValue: data.maxValue };
|
|
203
|
+
const extra = autocomplete ? { autocomplete: true, ...validation } : choices?.length ? { choices: choices.slice(0, 25) } : validation;
|
|
204
|
+
resolved.push(Object.assign({ ...data, description, ...extra }));
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
default: {
|
|
208
|
+
resolved.push({ ...option, description });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return resolved;
|
|
213
|
+
}
|
|
214
|
+
resolveModules(modules, path, parentRun) {
|
|
215
|
+
const resolved = [];
|
|
216
|
+
if (!modules.length)
|
|
217
|
+
return resolved;
|
|
218
|
+
const groups = modules.filter((m) => m.type === ApplicationCommandOptionType.SubcommandGroup);
|
|
219
|
+
const subcommands = modules.filter((m) => m.type === ApplicationCommandOptionType.Subcommand);
|
|
220
|
+
for (const group of groups) {
|
|
221
|
+
const groupSubs = subcommands.filter((s) => s.group === group.name);
|
|
222
|
+
const slashSubs = groupSubs.filter((s) => s.ignore !== IgnoreCommand.Slash);
|
|
223
|
+
const mappedSubs = slashSubs.map((sub) => ({
|
|
224
|
+
...sub,
|
|
225
|
+
type: ApplicationCommandOptionType.Subcommand
|
|
226
|
+
}));
|
|
227
|
+
resolved.push({ ...group, options: mappedSubs });
|
|
228
|
+
if (group.botPermissions?.length) {
|
|
229
|
+
this.botPermissionsMap.set(`${path}/${group.name}`, group.botPermissions);
|
|
230
|
+
}
|
|
231
|
+
for (const sub of groupSubs) {
|
|
232
|
+
if (sub.ignore === IgnoreCommand.Slash)
|
|
233
|
+
continue;
|
|
234
|
+
const subPath = `${path}/${group.name}/${sub.name}`;
|
|
235
|
+
this.commandRunners.set(subPath, [parentRun, group.run, sub.run].filter(isDefined));
|
|
236
|
+
if (sub.options?.length) {
|
|
237
|
+
this.optionDefs.set(subPath, sub.options);
|
|
238
|
+
}
|
|
239
|
+
if (sub.botPermissions?.length) {
|
|
240
|
+
this.botPermissionsMap.set(subPath, sub.botPermissions);
|
|
241
|
+
}
|
|
242
|
+
if (sub.shortcut) {
|
|
243
|
+
this.shortcuts.set(sub.name, subPath);
|
|
244
|
+
for (const alias of sub.aliases ?? []) {
|
|
245
|
+
this.shortcuts.set(alias, subPath);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (const sub of subcommands.filter((s) => !s.group)) {
|
|
251
|
+
if (sub.ignore === IgnoreCommand.Slash)
|
|
252
|
+
continue;
|
|
253
|
+
const subPath = `${path}/${sub.name}`;
|
|
254
|
+
this.commandRunners.set(subPath, [parentRun, sub.run].filter(isDefined));
|
|
255
|
+
if (sub.options?.length) {
|
|
256
|
+
this.optionDefs.set(subPath, sub.options);
|
|
257
|
+
}
|
|
258
|
+
if (sub.botPermissions?.length) {
|
|
259
|
+
this.botPermissionsMap.set(subPath, sub.botPermissions);
|
|
260
|
+
}
|
|
261
|
+
if (sub.shortcut) {
|
|
262
|
+
this.shortcuts.set(sub.name, subPath);
|
|
263
|
+
for (const alias of sub.aliases ?? []) {
|
|
264
|
+
this.shortcuts.set(alias, subPath);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
resolved.push(sub);
|
|
268
|
+
}
|
|
269
|
+
return resolved;
|
|
270
|
+
}
|
|
271
|
+
set(data) {
|
|
272
|
+
const type = data.type ?? ApplicationCommandType.ChatInput;
|
|
273
|
+
const name = this.formatName(data.name, type);
|
|
274
|
+
const dmPermission = data.dmPermission ?? false;
|
|
275
|
+
const commandData = {
|
|
276
|
+
...data,
|
|
277
|
+
name,
|
|
278
|
+
type,
|
|
279
|
+
dmPermission
|
|
280
|
+
};
|
|
281
|
+
if (this.collection.has(name)) {
|
|
282
|
+
const existing = this.collection.get(name);
|
|
283
|
+
const canonicalPath2 = `/${type}/${name}`;
|
|
284
|
+
this.commandRunners.delete(canonicalPath2);
|
|
285
|
+
this.optionDefs.delete(canonicalPath2);
|
|
286
|
+
this.botPermissionsMap.delete(canonicalPath2);
|
|
287
|
+
this.autocompleteRunners.delete(canonicalPath2);
|
|
288
|
+
if (existing.aliases?.length) {
|
|
289
|
+
for (const alias of existing.aliases) {
|
|
290
|
+
this.aliasCollection.delete(this.formatName(alias, type));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
this.collection.set(name, commandData);
|
|
295
|
+
const canonicalPath = `/${type}/${name}`;
|
|
296
|
+
this.commandRunners.set(canonicalPath, [data.run]);
|
|
297
|
+
if (data.botPermissions?.length) {
|
|
298
|
+
this.botPermissionsMap.set(canonicalPath, data.botPermissions);
|
|
299
|
+
}
|
|
300
|
+
if (data.aliases?.length) {
|
|
301
|
+
for (const alias of data.aliases) {
|
|
302
|
+
const aliasName = this.formatName(alias, type);
|
|
303
|
+
this.aliasCollection.set(aliasName, commandData);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (data.options?.length) {
|
|
307
|
+
const primitive = data.options.filter((o) => o.type !== ApplicationCommandOptionType.Subcommand && o.type !== ApplicationCommandOptionType.SubcommandGroup);
|
|
308
|
+
if (primitive.length) {
|
|
309
|
+
this.optionDefs.set(canonicalPath, primitive);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if ("autocomplete" in data && data.autocomplete) {
|
|
313
|
+
this.autocompleteRunners.set(canonicalPath, data.autocomplete);
|
|
314
|
+
}
|
|
315
|
+
return commandData;
|
|
316
|
+
}
|
|
317
|
+
build() {
|
|
318
|
+
const result = Array.from(this.collection.values()).filter((raw) => raw.ignore !== IgnoreCommand.Slash).map((raw) => {
|
|
319
|
+
const {
|
|
320
|
+
options,
|
|
321
|
+
modules,
|
|
322
|
+
description,
|
|
323
|
+
descriptionLocalizations,
|
|
324
|
+
category,
|
|
325
|
+
botPermissions: _bp,
|
|
326
|
+
...data
|
|
327
|
+
} = raw;
|
|
328
|
+
const path = `/${data.type}/${data.name}`;
|
|
329
|
+
const slashData = data.type === ApplicationCommandType.ChatInput ? {
|
|
330
|
+
description: description ?? data.name,
|
|
331
|
+
descriptionLocalizations,
|
|
332
|
+
options: this.buildOptions([
|
|
333
|
+
...options ?? [],
|
|
334
|
+
...this.resolveModules(modules ?? [], path, data.run)
|
|
335
|
+
], path)
|
|
336
|
+
} : {};
|
|
337
|
+
return { ...data, ...slashData, category };
|
|
338
|
+
});
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
addLog(data) {
|
|
342
|
+
const [icon, label] = this.getTitle(data);
|
|
343
|
+
this.logs.push(ck.green(spaceBuilder(ck.gray(icon), ck.green(`${label}`), ck.gray(">"), ck.underline.blue(data.name), ck.bold("✓"))));
|
|
344
|
+
}
|
|
345
|
+
addModule(commandName, module) {
|
|
346
|
+
const command = this.collection.get(commandName);
|
|
347
|
+
if (!command)
|
|
348
|
+
return;
|
|
349
|
+
command.modules ??= [];
|
|
350
|
+
command.modules.push(module);
|
|
351
|
+
}
|
|
352
|
+
getPrefixCommandCount() {
|
|
353
|
+
return Array.from(this.runtimeCollection.values()).filter((cmd) => cmd.ignore !== IgnoreCommand.Slash).length;
|
|
354
|
+
}
|
|
355
|
+
async onAutocomplete(interaction) {
|
|
356
|
+
const options = interaction.options;
|
|
357
|
+
const handler = this.getAutocompleteHandler(interaction.commandName, options.getSubcommandGroup(false), options.getSubcommand(false), options.getFocused(true).name);
|
|
358
|
+
if (!handler)
|
|
359
|
+
return;
|
|
360
|
+
const choices = await handler(interaction);
|
|
361
|
+
if (choices && Array.isArray(choices)) {
|
|
362
|
+
await interaction.respond(choices.slice(0, 25));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async onPrefixCommand(message) {
|
|
366
|
+
if (message.author.bot)
|
|
367
|
+
return;
|
|
368
|
+
if (!this.config.prefix)
|
|
369
|
+
return;
|
|
370
|
+
const prefixes = await this.config.prefix(message);
|
|
371
|
+
const prefix = prefixes.find((p) => message.content.toLowerCase().startsWith(p.toLowerCase()));
|
|
372
|
+
if (!prefix)
|
|
373
|
+
return;
|
|
374
|
+
const rawArgs = message.content.slice(prefix.length).trim().split(/ +/);
|
|
375
|
+
const commandName = rawArgs.shift()?.toLowerCase();
|
|
376
|
+
if (!commandName)
|
|
377
|
+
return;
|
|
378
|
+
const rawCommandData = this.runtimeCollection.get(commandName) ?? this.collection.get(commandName) ?? this.aliasCollection.get(commandName);
|
|
379
|
+
if (rawCommandData?.ignore === IgnoreCommand.Message)
|
|
380
|
+
return;
|
|
381
|
+
const args = [...rawArgs];
|
|
382
|
+
const resolved = this.resolvePrefixCommand(commandName, args, rawCommandData);
|
|
383
|
+
if (!resolved)
|
|
384
|
+
return;
|
|
385
|
+
const { runners, optionDefs, botPermissions } = resolved;
|
|
386
|
+
const ctx = new CommandContext(message.client, message, args, optionDefs);
|
|
387
|
+
const {
|
|
388
|
+
middleware,
|
|
389
|
+
onMemberPermissionsFailed,
|
|
390
|
+
onBotPermissionsFailed,
|
|
391
|
+
onOptionsError,
|
|
392
|
+
onError
|
|
393
|
+
} = this.config;
|
|
394
|
+
if (middleware) {
|
|
395
|
+
let isBlock = false;
|
|
396
|
+
const block = () => {
|
|
397
|
+
isBlock = true;
|
|
398
|
+
};
|
|
399
|
+
await middleware(ctx, block);
|
|
400
|
+
if (isBlock)
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (onMemberPermissionsFailed && rawCommandData?.defaultMemberPermissions) {
|
|
404
|
+
const perms = Array.isArray(rawCommandData.defaultMemberPermissions) ? rawCommandData.defaultMemberPermissions : [rawCommandData.defaultMemberPermissions];
|
|
405
|
+
const missing = resolveMissingPermissions(message.member, perms);
|
|
406
|
+
if (missing.length) {
|
|
407
|
+
onMemberPermissionsFailed(ctx, missing);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (onBotPermissionsFailed && botPermissions?.length) {
|
|
412
|
+
const botMember = message.guild?.members.me ?? null;
|
|
413
|
+
const missing = resolveMissingPermissions(botMember, botPermissions);
|
|
414
|
+
if (missing.length) {
|
|
415
|
+
onBotPermissionsFailed(ctx, missing);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (onOptionsError && optionDefs.length) {
|
|
420
|
+
const missing = optionDefs.filter((def) => def.required && args[optionDefs.indexOf(def)] === undefined).map((def) => ({
|
|
421
|
+
name: def.name,
|
|
422
|
+
description: def.description ?? def.name,
|
|
423
|
+
value: resolveOptionValueType(def.type)
|
|
424
|
+
}));
|
|
425
|
+
if (missing.length > 0) {
|
|
426
|
+
onOptionsError(message, missing);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
let result;
|
|
432
|
+
for (const run of runners.filter(isDefined)) {
|
|
433
|
+
result = await run(ctx, result);
|
|
434
|
+
}
|
|
435
|
+
} catch (err) {
|
|
436
|
+
if (onError) {
|
|
437
|
+
console.error(`Error in prefix command [${commandName}]:`, err);
|
|
438
|
+
onError(ctx, err);
|
|
439
|
+
} else {
|
|
440
|
+
throw err;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async onCommand(interaction) {
|
|
445
|
+
if (interaction.isPrimaryEntryPointCommand())
|
|
446
|
+
return;
|
|
447
|
+
const {
|
|
448
|
+
onNotFound,
|
|
449
|
+
middleware,
|
|
450
|
+
onError,
|
|
451
|
+
onMemberPermissionsFailed,
|
|
452
|
+
onBotPermissionsFailed
|
|
453
|
+
} = this.config;
|
|
454
|
+
const path = [interaction.commandName];
|
|
455
|
+
let subcommandGroup = null;
|
|
456
|
+
let subcommand = null;
|
|
457
|
+
if (interaction.isChatInputCommand()) {
|
|
458
|
+
subcommandGroup = interaction.options.getSubcommandGroup(false);
|
|
459
|
+
subcommand = interaction.options.getSubcommand(false);
|
|
460
|
+
path.push(subcommandGroup, subcommand);
|
|
461
|
+
}
|
|
462
|
+
const handler = this.getHandler(interaction.commandType, ...path);
|
|
463
|
+
if (!handler) {
|
|
464
|
+
return onNotFound?.(interaction);
|
|
465
|
+
}
|
|
466
|
+
const rawCommandData = this.runtimeCollection.get(interaction.commandName) ?? this.collection.get(interaction.commandName);
|
|
467
|
+
if (rawCommandData?.ignore === IgnoreCommand.Slash) {
|
|
468
|
+
return onNotFound?.(interaction);
|
|
469
|
+
}
|
|
470
|
+
const ctx = new CommandContext(interaction.client, interaction, []);
|
|
471
|
+
if (middleware) {
|
|
472
|
+
let isBlock = false;
|
|
473
|
+
const block = () => {
|
|
474
|
+
isBlock = true;
|
|
475
|
+
};
|
|
476
|
+
await middleware(ctx, block);
|
|
477
|
+
if (isBlock)
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (onMemberPermissionsFailed && rawCommandData?.defaultMemberPermissions) {
|
|
481
|
+
const perms = Array.isArray(rawCommandData.defaultMemberPermissions) ? rawCommandData.defaultMemberPermissions : [rawCommandData.defaultMemberPermissions];
|
|
482
|
+
const missing = resolveMissingPermissions(interaction.member instanceof GuildMember ? interaction.member : null, perms);
|
|
483
|
+
if (missing.length) {
|
|
484
|
+
onMemberPermissionsFailed(ctx, missing);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (onBotPermissionsFailed) {
|
|
489
|
+
const botPermissions = this.resolveBotPermissions(interaction.commandName, subcommandGroup, subcommand);
|
|
490
|
+
if (botPermissions?.length) {
|
|
491
|
+
const botMember = interaction.guild?.members.me ?? null;
|
|
492
|
+
const missing = resolveMissingPermissions(botMember, botPermissions);
|
|
493
|
+
if (missing.length) {
|
|
494
|
+
onBotPermissionsFailed(ctx, missing);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
let result;
|
|
501
|
+
for (const run of handler.filter(isDefined)) {
|
|
502
|
+
result = await run(ctx, result);
|
|
503
|
+
}
|
|
504
|
+
} catch (err) {
|
|
505
|
+
if (onError) {
|
|
506
|
+
onError(ctx, err);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
throw err;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async register(client) {
|
|
513
|
+
const messages = [];
|
|
514
|
+
const pluralize = (n) => n > 1 ? "s" : "";
|
|
515
|
+
const commands = this.build();
|
|
516
|
+
const createVerboseLogs = (cmdsCollection) => cmdsCollection.map(({ id, name, type, client: cl, createdAt, guild }) => {
|
|
517
|
+
const [icon] = this.getTitle({
|
|
518
|
+
type
|
|
519
|
+
});
|
|
520
|
+
return ck.dim.green(spaceBuilder(` └ ${icon}`, ck.underline.cyan(id), "CREATED", ck.underline.blue(name), ck.gray(">"), guild ? `${ck.blue(guild.name)} guild` : `${ck.blue(cl.user.username)} application`, ck.gray(">"), "created at:", ck.greenBright(createdAt.toLocaleTimeString())));
|
|
521
|
+
});
|
|
522
|
+
const logRegistration = (cmdsCollection, location) => {
|
|
523
|
+
if (!cmdsCollection.size)
|
|
524
|
+
return;
|
|
525
|
+
messages.push(ck.greenBright(`└ ${cmdsCollection.size} command${pluralize(cmdsCollection.size)} successfully registered ${location}!`));
|
|
526
|
+
if (this.config.verbose) {
|
|
527
|
+
messages.push(...createVerboseLogs(cmdsCollection));
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
const targetGuilds = client.guilds.cache.filter(({ id }) => this.config.guilds?.includes(id));
|
|
531
|
+
if (targetGuilds.size) {
|
|
532
|
+
const globalCommands = commands.filter((c) => c.global);
|
|
533
|
+
const guildCommands = commands.filter((c) => !c.global);
|
|
534
|
+
await client.application.commands.set(globalCommands).then((cmds) => {
|
|
535
|
+
if (!cmds.size)
|
|
536
|
+
return;
|
|
537
|
+
logRegistration(cmds, "globally");
|
|
538
|
+
});
|
|
539
|
+
for (const guild of targetGuilds.values()) {
|
|
540
|
+
const cmds = await guild.commands.set(guildCommands);
|
|
541
|
+
logRegistration(cmds, `in ${ck.underline(guild.name)} guild`);
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
await Promise.all(client.guilds.cache.map((guild) => guild.commands.set([])));
|
|
545
|
+
await client.application.commands.set(commands).then((cmds) => logRegistration(cmds, "globally"));
|
|
546
|
+
}
|
|
547
|
+
const prefixCount = this.getPrefixCommandCount();
|
|
548
|
+
if (prefixCount > 0 && this.config.prefix) {
|
|
549
|
+
messages.push(ck.greenBright(`└ ${prefixCount} prefix command${pluralize(prefixCount)} available!`));
|
|
550
|
+
}
|
|
551
|
+
this.clear();
|
|
552
|
+
if (messages.length) {
|
|
553
|
+
console.log(messages.join(`
|
|
554
|
+
`));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
export {
|
|
559
|
+
CommandManager
|
|
560
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ClientEvents } from "discord.js";
|
|
2
|
+
export type ClientEventKey = keyof ClientEvents;
|
|
3
|
+
export type EventPropData = {
|
|
4
|
+
[Key in ClientEventKey]: {
|
|
5
|
+
name: Key;
|
|
6
|
+
args: ClientEvents[Key];
|
|
7
|
+
};
|
|
8
|
+
}[ClientEventKey];
|
|
9
|
+
export interface EventData<EventName extends ClientEventKey> {
|
|
10
|
+
name: string;
|
|
11
|
+
event: EventName;
|
|
12
|
+
once?: boolean;
|
|
13
|
+
tags?: string[];
|
|
14
|
+
run(this: void, ...args: ClientEvents[EventName]): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare class Event<EventName extends ClientEventKey> {
|
|
17
|
+
readonly data: EventData<EventName>;
|
|
18
|
+
constructor(data: EventData<EventName>);
|
|
19
|
+
}
|
|
20
|
+
export type GenericEventArgs = ClientEvents[ClientEventKey];
|
|
21
|
+
export type EventsCollection = Map<string, Event<ClientEventKey>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Client, Collection, type ClientEvents } from "discord.js";
|
|
2
|
+
import { BaseManager } from "../manager.js";
|
|
3
|
+
import type { ClientEventKey, Event, EventsCollection, GenericEventArgs } from "./event.js";
|
|
4
|
+
export declare class EventManager extends BaseManager {
|
|
5
|
+
private get config();
|
|
6
|
+
readonly collection: Collection<keyof ClientEvents, EventsCollection>;
|
|
7
|
+
getEvents(key: ClientEventKey): EventsCollection;
|
|
8
|
+
add(event: Event<ClientEventKey>): void;
|
|
9
|
+
onEvent(event: Event<ClientEventKey>, args: GenericEventArgs): Promise<void>;
|
|
10
|
+
register(client: Client): void;
|
|
11
|
+
runReady(client: Client<true>): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/creators/events/manager.ts
|
|
2
|
+
import { Collection } from "discord.js";
|
|
3
|
+
import { styleText } from "node:util";
|
|
4
|
+
import { BaseManager } from "../manager.js";
|
|
5
|
+
|
|
6
|
+
class EventManager extends BaseManager {
|
|
7
|
+
get config() {
|
|
8
|
+
return this.app.config.events;
|
|
9
|
+
}
|
|
10
|
+
collection = new Collection;
|
|
11
|
+
getEvents(key) {
|
|
12
|
+
if (!this.collection.has(key)) {
|
|
13
|
+
this.collection.set(key, new Map);
|
|
14
|
+
}
|
|
15
|
+
return this.collection.get(key);
|
|
16
|
+
}
|
|
17
|
+
add(event) {
|
|
18
|
+
const events = this.getEvents(event.data.event);
|
|
19
|
+
events.set(event.data.name, event);
|
|
20
|
+
this.logs.push([
|
|
21
|
+
styleText("yellow", `✦ Event`),
|
|
22
|
+
styleText("gray", `>`),
|
|
23
|
+
styleText(["yellow"], event.data.event),
|
|
24
|
+
styleText("gray", `>`),
|
|
25
|
+
styleText(["blue", "underline"], event.data.name),
|
|
26
|
+
styleText("green", "✓")
|
|
27
|
+
].join(" "));
|
|
28
|
+
}
|
|
29
|
+
async onEvent(event, args) {
|
|
30
|
+
const { middleware, onError } = this.config;
|
|
31
|
+
let isBlock = false;
|
|
32
|
+
const block = (...selected) => {
|
|
33
|
+
const tags = event.data.tags ?? [];
|
|
34
|
+
if (selected.length === 0) {
|
|
35
|
+
isBlock = tags.length === 0;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
isBlock = selected.some((tag) => tags.includes(tag));
|
|
39
|
+
};
|
|
40
|
+
const eventData = { name: event.data.name, args };
|
|
41
|
+
if (middleware)
|
|
42
|
+
await middleware(eventData, block);
|
|
43
|
+
if (isBlock)
|
|
44
|
+
return;
|
|
45
|
+
await event.data.run(...args).catch((err) => {
|
|
46
|
+
if (onError) {
|
|
47
|
+
onError(err, eventData);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
});
|
|
52
|
+
if (event.data.once) {
|
|
53
|
+
this.getEvents(event.data.event)?.delete(event.data.name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
register(client) {
|
|
57
|
+
const collection = this.collection.filter((_, key) => key !== "clientReady" && key !== "messageCreate");
|
|
58
|
+
for (const [key, events] of collection.entries()) {
|
|
59
|
+
client.on(key, (...args) => {
|
|
60
|
+
Promise.all(Array.from(events.values()).map((event) => this.onEvent(event, args)));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async runReady(client) {
|
|
65
|
+
const collection = this.getEvents("clientReady");
|
|
66
|
+
await Promise.all(Array.from(collection.values()).map((event) => this.onEvent(event, [client])));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
EventManager
|
|
71
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ResponderType, type Responder, type ResponderData, type ResponderInteraction } from "./responders/responder.js";
|
|
2
|
+
export { emitResponder } from "./responders/emit.js";
|
|
3
|
+
export { type Command, type AppCommandData } from "./commands/command.js";
|
|
4
|
+
export { type Event, type EventData, type EventPropData } from "./events/event.js";
|
|
5
|
+
export * from "./setup.js";
|