@hyperneutrino/djs-lite 1.0.1 → 1.1.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/bun.lock +1 -0
- package/index.ts +111 -17
- package/package.json +1 -1
package/bun.lock
CHANGED
package/index.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ApplicationCommandType,
|
|
3
3
|
AutocompleteInteraction,
|
|
4
|
+
ButtonInteraction,
|
|
5
|
+
ChannelSelectMenuInteraction,
|
|
4
6
|
Client,
|
|
5
7
|
CommandInteraction,
|
|
6
8
|
Events,
|
|
9
|
+
MentionableSelectMenuInteraction,
|
|
10
|
+
MessageComponentInteraction,
|
|
11
|
+
ModalSubmitInteraction,
|
|
12
|
+
RoleSelectMenuInteraction,
|
|
13
|
+
StringSelectMenuInteraction,
|
|
14
|
+
UserSelectMenuInteraction,
|
|
7
15
|
type Awaitable,
|
|
8
16
|
type BaseApplicationCommandData,
|
|
9
17
|
type ChatInputApplicationCommandData,
|
|
10
18
|
type ChatInputCommandInteraction,
|
|
19
|
+
type ClientEvents,
|
|
11
20
|
type MessageApplicationCommandData,
|
|
12
21
|
type MessageContextMenuCommandInteraction,
|
|
13
22
|
type UserApplicationCommandData,
|
|
@@ -16,11 +25,16 @@ import {
|
|
|
16
25
|
import fs from "node:fs/promises";
|
|
17
26
|
import path from "node:path";
|
|
18
27
|
|
|
28
|
+
// TODO: comments (requires some refactoring of types to do right)
|
|
29
|
+
|
|
19
30
|
process.on("uncaughtException", (err) => console.error(err));
|
|
20
31
|
|
|
21
|
-
type Handler<T extends CommandInteraction | AutocompleteInteraction> = (
|
|
32
|
+
type Handler<T extends CommandInteraction | AutocompleteInteraction | MessageComponentInteraction | ModalSubmitInteraction> = (
|
|
33
|
+
interaction: T,
|
|
34
|
+
...args: any[]
|
|
35
|
+
) => Awaitable<unknown>;
|
|
22
36
|
|
|
23
|
-
class Command<T extends BaseApplicationCommandData, U extends CommandInteraction, Z extends boolean = false> {
|
|
37
|
+
abstract class Command<T extends BaseApplicationCommandData, U extends CommandInteraction, Z extends boolean = false> {
|
|
24
38
|
data: T;
|
|
25
39
|
handler: Handler<U>;
|
|
26
40
|
autocomplete: Handler<AutocompleteInteraction> | null;
|
|
@@ -43,7 +57,33 @@ export class SlashCommand extends Command<ChatInputApplicationCommandData & { ty
|
|
|
43
57
|
export class UserCommand extends Command<UserApplicationCommandData, UserContextMenuCommandInteraction> {}
|
|
44
58
|
export class MessageCommand extends Command<MessageApplicationCommandData, MessageContextMenuCommandInteraction> {}
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
abstract class ComponentResponder<T extends ModalSubmitInteraction | MessageComponentInteraction> {
|
|
61
|
+
handler: Handler<T>;
|
|
62
|
+
|
|
63
|
+
constructor(handler: Handler<T>) {
|
|
64
|
+
this.handler = handler;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class ModalResponder extends ComponentResponder<ModalSubmitInteraction> {}
|
|
69
|
+
export class ButtonResponder extends ComponentResponder<ButtonInteraction> {}
|
|
70
|
+
export class StringSelectResponder extends ComponentResponder<StringSelectMenuInteraction> {}
|
|
71
|
+
export class UserSelectResponder extends ComponentResponder<UserSelectMenuInteraction> {}
|
|
72
|
+
export class RoleSelectResponder extends ComponentResponder<RoleSelectMenuInteraction> {}
|
|
73
|
+
export class MentionSelectResponder extends ComponentResponder<MentionableSelectMenuInteraction> {}
|
|
74
|
+
export class ChannelSelectResponder extends ComponentResponder<ChannelSelectMenuInteraction> {}
|
|
75
|
+
|
|
76
|
+
export class EventHandler<T extends keyof ClientEvents> {
|
|
77
|
+
event: T;
|
|
78
|
+
handler: (...args: ClientEvents[T]) => unknown;
|
|
79
|
+
|
|
80
|
+
constructor({ event, handler }: { event: T; handler: (...args: ClientEvents[T]) => unknown }) {
|
|
81
|
+
this.event = event;
|
|
82
|
+
this.handler = handler;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function loadCommands(client: Client<true>, directory: string, guildId?: string) {
|
|
47
87
|
const files = await fs.readdir(path.resolve(directory), { recursive: false, withFileTypes: true });
|
|
48
88
|
|
|
49
89
|
const commandData: (ChatInputApplicationCommandData | UserApplicationCommandData | MessageApplicationCommandData)[] = [];
|
|
@@ -56,37 +96,54 @@ export async function loadCommands(client: Client<true>, directory: string) {
|
|
|
56
96
|
|
|
57
97
|
await Promise.all(
|
|
58
98
|
files.map(async (file) => {
|
|
59
|
-
const
|
|
99
|
+
const absolutePath = path.resolve(file.parentPath, file.name);
|
|
100
|
+
|
|
101
|
+
const { default: item } = await import(absolutePath);
|
|
60
102
|
|
|
61
103
|
if (item instanceof SlashCommand) {
|
|
62
|
-
commandData.push(item.data);
|
|
63
104
|
slashCommandHandlers.set(item.data.name, item.handler);
|
|
64
105
|
if (item.autocomplete) slashCommandAutocompletes.set(item.data.name, item.autocomplete);
|
|
65
106
|
} else if (item instanceof UserCommand) {
|
|
66
|
-
commandData.push(item.data);
|
|
67
107
|
userCommandHandlers.set(item.data.name, item.handler);
|
|
68
108
|
} else if (item instanceof MessageCommand) {
|
|
69
|
-
commandData.push(item.data);
|
|
70
109
|
messageCommandHandlers.set(item.data.name, item.handler);
|
|
71
110
|
} else {
|
|
72
|
-
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Loading commands failed: export from ${path.relative(path.resolve(directory), absolutePath)} was not an instance of <Type>Command.`,
|
|
113
|
+
);
|
|
73
114
|
}
|
|
115
|
+
|
|
116
|
+
commandData.push(item.data);
|
|
74
117
|
}),
|
|
75
118
|
);
|
|
76
119
|
|
|
77
|
-
await client.application.commands.set(commandData);
|
|
78
|
-
|
|
79
120
|
client.on(Events.InteractionCreate, (interaction) => {
|
|
80
121
|
if (interaction.isChatInputCommand()) slashCommandHandlers.get(interaction.commandName)?.(interaction);
|
|
81
122
|
else if (interaction.isUserContextMenuCommand()) userCommandHandlers.get(interaction.commandName)?.(interaction);
|
|
82
123
|
else if (interaction.isMessageContextMenuCommand()) messageCommandHandlers.get(interaction.commandName)?.(interaction);
|
|
83
124
|
else if (interaction.isAutocomplete()) slashCommandAutocompletes.get(interaction.commandName)?.(interaction);
|
|
84
125
|
});
|
|
126
|
+
|
|
127
|
+
if (guildId) {
|
|
128
|
+
const testGuild = client.guilds.resolve(guildId);
|
|
129
|
+
if (!testGuild)
|
|
130
|
+
throw new Error(`Provided test guild (${guildId}) can not be found, please make sure the bot you started this project on is in this guild.`);
|
|
131
|
+
await testGuild.commands.set(commandData);
|
|
132
|
+
} else {
|
|
133
|
+
await client.application.commands.set(commandData);
|
|
134
|
+
}
|
|
85
135
|
}
|
|
86
136
|
|
|
87
137
|
export async function loadInteractions(client: Client<true>, directory: string, argumentSeparator: string = ":") {
|
|
88
138
|
const files = await fs.readdir(path.resolve(directory), { recursive: true, withFileTypes: true });
|
|
89
|
-
|
|
139
|
+
|
|
140
|
+
const modalHandlers = new Map<string, Handler<ModalSubmitInteraction>>();
|
|
141
|
+
const buttonHandlers = new Map<string, Handler<ButtonInteraction>>();
|
|
142
|
+
const stringHandlers = new Map<string, Handler<StringSelectMenuInteraction>>();
|
|
143
|
+
const userHandlers = new Map<string, Handler<UserSelectMenuInteraction>>();
|
|
144
|
+
const roleHandlers = new Map<string, Handler<RoleSelectMenuInteraction>>();
|
|
145
|
+
const mentionHandlers = new Map<string, Handler<MentionableSelectMenuInteraction>>();
|
|
146
|
+
const channelHandlers = new Map<string, Handler<ChannelSelectMenuInteraction>>();
|
|
90
147
|
|
|
91
148
|
await Promise.all(
|
|
92
149
|
files.map(async (file) => {
|
|
@@ -94,11 +151,18 @@ export async function loadInteractions(client: Client<true>, directory: string,
|
|
|
94
151
|
|
|
95
152
|
const absolutePath = path.resolve(file.parentPath, file.name);
|
|
96
153
|
const relativePath = path.relative(path.resolve(directory), absolutePath);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
154
|
+
const handlerKey = relativePath.replace(/\.[^/.]+$/, "");
|
|
155
|
+
|
|
156
|
+
const { default: item } = await import(absolutePath).catch(() => null);
|
|
157
|
+
|
|
158
|
+
if (item instanceof ModalResponder) modalHandlers.set(handlerKey, item.handler);
|
|
159
|
+
else if (item instanceof ButtonResponder) buttonHandlers.set(handlerKey, item.handler);
|
|
160
|
+
else if (item instanceof StringSelectResponder) stringHandlers.set(handlerKey, item.handler);
|
|
161
|
+
else if (item instanceof UserSelectResponder) userHandlers.set(handlerKey, item.handler);
|
|
162
|
+
else if (item instanceof RoleSelectResponder) roleHandlers.set(handlerKey, item.handler);
|
|
163
|
+
else if (item instanceof MentionSelectResponder) mentionHandlers.set(handlerKey, item.handler);
|
|
164
|
+
else if (item instanceof ChannelSelectResponder) channelHandlers.set(handlerKey, item.handler);
|
|
165
|
+
else throw new Error(`Loading interactions failed: export from ${relativePath} was not an instance of <InteractionType>Responder.`);
|
|
102
166
|
}),
|
|
103
167
|
);
|
|
104
168
|
|
|
@@ -108,6 +172,36 @@ export async function loadInteractions(client: Client<true>, directory: string,
|
|
|
108
172
|
const [, userId, path, ...args] = interaction.customId.split(argumentSeparator);
|
|
109
173
|
if (!path || (userId && interaction.user.id !== userId)) return;
|
|
110
174
|
|
|
111
|
-
|
|
175
|
+
if (interaction.isModalSubmit()) modalHandlers.get(path)?.(interaction, ...args);
|
|
176
|
+
else if (interaction.isButton()) buttonHandlers.get(path)?.(interaction, ...args);
|
|
177
|
+
else if (interaction.isStringSelectMenu()) stringHandlers.get(path)?.(interaction, ...args);
|
|
178
|
+
else if (interaction.isUserSelectMenu()) userHandlers.get(path)?.(interaction, ...args);
|
|
179
|
+
else if (interaction.isRoleSelectMenu()) roleHandlers.get(path)?.(interaction, ...args);
|
|
180
|
+
else if (interaction.isMentionableSelectMenu()) mentionHandlers.get(path)?.(interaction, ...args);
|
|
181
|
+
else if (interaction.isChannelSelectMenu()) channelHandlers.get(path)?.(interaction, ...args);
|
|
112
182
|
});
|
|
113
183
|
}
|
|
184
|
+
|
|
185
|
+
export async function loadEvents(client: Client<true>, directory: string, recursive: boolean = false) {
|
|
186
|
+
const files = await fs.readdir(path.resolve(directory), { recursive, withFileTypes: true });
|
|
187
|
+
const handlers: Partial<{ [K in keyof ClientEvents]: ((...args: ClientEvents[K]) => unknown)[] }> = {};
|
|
188
|
+
|
|
189
|
+
await Promise.all(
|
|
190
|
+
files.map(async (file) => {
|
|
191
|
+
if (file.isDirectory()) return;
|
|
192
|
+
|
|
193
|
+
const absolutePath = path.resolve(file.parentPath, file.name);
|
|
194
|
+
|
|
195
|
+
const { default: item } = await import(absolutePath);
|
|
196
|
+
|
|
197
|
+
if (item instanceof EventHandler) (handlers[item.event as keyof ClientEvents] ??= []).push(item.handler);
|
|
198
|
+
else {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Loading events failed: export from ${path.relative(path.resolve(directory), absolutePath)} was not an instance of EventHandler<T>.`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
Object.entries(handlers).forEach(([key, handlers]) => client.on(key, (...args) => handlers.forEach((handler) => (handler as any)(...args))));
|
|
207
|
+
}
|