@open-discord-bots/framework 0.3.14 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/api/main.js +1 -1
  2. package/dist/api/modules/responder.js +50 -26
  3. package/package.json +1 -1
  4. package/src/api/index.ts +0 -31
  5. package/src/api/main.ts +0 -203
  6. package/src/api/modules/action.ts +0 -89
  7. package/src/api/modules/base.ts +0 -845
  8. package/src/api/modules/builder.ts +0 -1755
  9. package/src/api/modules/checker.ts +0 -1826
  10. package/src/api/modules/client.ts +0 -2345
  11. package/src/api/modules/code.ts +0 -84
  12. package/src/api/modules/component.ts +0 -2000
  13. package/src/api/modules/config.ts +0 -264
  14. package/src/api/modules/console.ts +0 -697
  15. package/src/api/modules/cooldown.ts +0 -369
  16. package/src/api/modules/database.ts +0 -321
  17. package/src/api/modules/event.ts +0 -123
  18. package/src/api/modules/flag.ts +0 -99
  19. package/src/api/modules/fuse.ts +0 -365
  20. package/src/api/modules/helpmenu.ts +0 -273
  21. package/src/api/modules/language.ts +0 -230
  22. package/src/api/modules/permission.ts +0 -363
  23. package/src/api/modules/plugin.ts +0 -294
  24. package/src/api/modules/post.ts +0 -137
  25. package/src/api/modules/progressbar.ts +0 -370
  26. package/src/api/modules/responder.ts +0 -1625
  27. package/src/api/modules/session.ts +0 -181
  28. package/src/api/modules/startscreen.ts +0 -345
  29. package/src/api/modules/state.ts +0 -298
  30. package/src/api/modules/statistic.ts +0 -380
  31. package/src/api/modules/verifybar.ts +0 -68
  32. package/src/api/modules/worker.ts +0 -119
  33. package/src/cli/editConfig.ts +0 -930
  34. package/src/cli/index.ts +0 -152
  35. package/src/index.ts +0 -8
  36. package/src/startup/compilation.ts +0 -204
  37. package/src/startup/dump.ts +0 -46
  38. package/src/startup/errorHandling.ts +0 -42
  39. package/src/startup/pluginLauncher.ts +0 -265
  40. package/src/utilities/index.ts +0 -229
  41. package/tools/cleanup.js +0 -2
@@ -1,1625 +0,0 @@
1
- ///////////////////////////////////////
2
- //RESPONDER MODULE
3
- ///////////////////////////////////////
4
- import { ODId, ODManager, ODValidId, ODSystemError, ODManagerData, ODNoGeneric } from "./base.js"
5
- import * as discord from "discord.js"
6
- import { ODWorkerManager, ODWorkerCallback, ODWorker } from "./worker.js"
7
- import { ODDebugger } from "./console.js"
8
- import { ODClientManager, ODContextMenu, ODSlashCommand, ODTextCommand, ODTextCommandInteractionOption } from "./client.js"
9
- import { ODDropdownData, ODMessageBuildResult, ODModalBuildResult } from "./builder.js"
10
- import { ODMessageComponentBuildResult } from "./component.js"
11
-
12
- /**## ODResponderImplementation `class`
13
- * This is an Open Discord responder implementation.
14
- *
15
- * It is a basic implementation of the `ODWorkerManager` used by all `ODResponder` classes.
16
- *
17
- * This class can't be used stand-alone & needs to be extended from!
18
- */
19
- export abstract class ODResponderImplementation<Instance,Origin extends string,Params,WorkerIds extends string = string> extends ODManagerData {
20
- /**The manager that has all workers of this implementation */
21
- workers: ODWorkerManager<Instance,Origin,Params,WorkerIds>
22
- /**The `commandName` or `customId` needs to match this string or regex for this responder to be executed. */
23
- match: string|RegExp
24
-
25
- constructor(id:ODValidId, match:string|RegExp, callback?:ODWorkerCallback<Instance,Origin,Params>, priority?:number, callbackId?:ODValidId){
26
- super(id)
27
- this.match = match
28
- this.workers = new ODWorkerManager("descending")
29
- if (callback) this.workers.add(new ODWorker(callbackId ? callbackId : id,priority ?? 0,callback))
30
- }
31
- /**Execute all workers & return the result. */
32
- abstract respond(instance:Instance, origin:Origin, params:Params): Promise<void>
33
- }
34
-
35
- /**## ODResponderTimeoutErrorCallback `type`
36
- * This is the callback for the responder timeout function. It will be executed when something went wrong or the action takes too much time.
37
- */
38
- export type ODResponderTimeoutErrorCallback<Instance, Origin extends "slash"|"text"|"button"|"dropdown"|"modal"|"other"|"context-menu"|"autocomplete"> = (instance:Instance, origin:Origin) => ODResponderSendResult<boolean>|Promise<ODResponderSendResult<boolean>>
39
-
40
- /**## ODResponderSendResult `type`
41
- * The result from a sent message using responders. Can be used to edit, view & save the message that got created.
42
- */
43
- export type ODResponderSendResult<InGuild extends boolean> = {
44
- /**Did the message get sent successfully? */
45
- success:true,
46
- /**The message that got sent. */
47
- message:discord.Message<InGuild>,
48
- /**Was the message sent as ephemeral? */
49
- ephemeral:boolean
50
- } | {
51
- /**Did the message get sent successfully? */
52
- success:false
53
- }
54
-
55
- /**## ODResponderManager `class`
56
- * This is an Open Discord responder manager.
57
- *
58
- * It contains all Open Discord responders. Responders can respond to an interaction, button, dropdown, modal or command.
59
- *
60
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
61
- * - plugins can extend/edit replies
62
- * - automatically reply on error
63
- * - independent workers (with priority)
64
- * - fail-safe design using try-catch
65
- * - write code once => reply to both slash & text commands at the same time!
66
- * - know where the request came from & parse options/subcommands & without errors!
67
- * - And so much more!
68
- */
69
- export class ODResponderManager<
70
- CommandIdList extends ODCommandResponderManagerIdConstraint = ODCommandResponderManagerIdConstraint,
71
- ButtonIdList extends ODButtonResponderManagerIdConstraint = ODButtonResponderManagerIdConstraint,
72
- DropdownIdList extends ODDropdownResponderManagerIdConstraint = ODDropdownResponderManagerIdConstraint,
73
- ModalIdList extends ODModalResponderManagerIdConstraint = ODModalResponderManagerIdConstraint,
74
- ContextMenuIdList extends ODContextMenuResponderManagerIdConstraint = ODContextMenuResponderManagerIdConstraint,
75
- AutocompleteIdList extends ODAutocompleteResponderManagerIdConstraint = ODAutocompleteResponderManagerIdConstraint
76
- > {
77
- /**A manager for all (text & slash) command responders. */
78
- commands: ODCommandResponderManager<CommandIdList>
79
- /**A manager for all button responders. */
80
- buttons: ODButtonResponderManager<ButtonIdList>
81
- /**A manager for all dropdown/select menu responders. */
82
- dropdowns: ODDropdownResponderManager<DropdownIdList>
83
- /**A manager for all modal responders. */
84
- modals: ODModalResponderManager<ModalIdList>
85
- /**A manager for all context menu responders. */
86
- contextMenus: ODContextMenuResponderManager<ContextMenuIdList>
87
- /**A manager for all autocomplete responders. */
88
- autocomplete: ODAutocompleteResponderManager<AutocompleteIdList>
89
-
90
- constructor(debug:ODDebugger, client:ODClientManager){
91
- this.commands = new ODCommandResponderManager(debug,"command responder",client)
92
- this.buttons = new ODButtonResponderManager(debug,"button responder",client)
93
- this.dropdowns = new ODDropdownResponderManager(debug,"dropdown responder",client)
94
- this.modals = new ODModalResponderManager(debug,"modal responder",client)
95
- this.contextMenus = new ODContextMenuResponderManager(debug,"context menu responder",client)
96
- this.autocomplete = new ODAutocompleteResponderManager(debug,"autocomplete responder",client)
97
- }
98
- }
99
-
100
- /**## ODBaseResponderInstance `class`
101
- * A base class for creating responder instances.
102
- */
103
- export abstract class ODBaseResponderInstance {
104
- /**Get the final `messageCreateOptions` from a returned build result from builders/components. */
105
- protected getMessageFromBuildResult(build:ODMessageBuildResult|ODMessageComponentBuildResult,type:"interaction"|"message"){
106
- const msgFlags: number[] = []
107
- let msgData: discord.MessageCreateOptions
108
- if ('message' in build){
109
- //USING BUILDERS (deprecated)
110
- msgData = build.message
111
- if (build.ephemeral) msgFlags.push(discord.MessageFlags.Ephemeral)
112
- }else{
113
- //USING COMPONENTS
114
- msgData = build.msg
115
- if (type == "interaction" && build.ephemeral) msgFlags.push(discord.MessageFlags.Ephemeral) //disabled with regular messages
116
- if (build.componentsV2) msgFlags.push(discord.MessageFlags.IsComponentsV2)
117
- if (build.supressEmbeds) msgFlags.push(discord.MessageFlags.SuppressEmbeds)
118
- if (build.supressNotifications) msgFlags.push(discord.MessageFlags.SuppressNotifications)
119
- }
120
- return Object.assign(msgData,{flags:msgFlags})
121
- }
122
- }
123
-
124
- /**## ODCommandResponderManagerIdConstraint `type`
125
- * The constraint/layout for id mappings/interfaces of the `ODCommandResponderManager` class.
126
- */
127
- export type ODCommandResponderManagerIdConstraint = Record<string,{origin:"slash"|"text",params:object,workers:string}>
128
-
129
- /**## ODCommandResponderManager `class`
130
- * This is an Open Discord command responder manager.
131
- *
132
- * It contains all Open Discord command responders. These can respond to text & slash commands.
133
- *
134
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
135
- * - plugins can extend/edit replies
136
- * - automatically reply on error
137
- * - independent workers (with priority)
138
- * - fail-safe design using try-catch
139
- * - write code once => reply to both slash & text commands at the same time!
140
- * - know where the request came from & parse options/subcommands & without errors!
141
- * - And so much more!
142
- */
143
- export class ODCommandResponderManager<IdList extends ODCommandResponderManagerIdConstraint = ODCommandResponderManagerIdConstraint> extends ODManager<ODCommandResponder<"slash"|"text",any>> {
144
- /**An alias to the Open Discord client manager. */
145
- private client: ODClientManager
146
- /**The callback executed when the default workers take too much time to reply. */
147
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODCommandResponderInstance,"slash"|"text">|null = null
148
- /**The amount of milliseconds before the timeout error callback is executed. */
149
- private timeoutMs: number|null = null
150
-
151
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
152
- super(debug,debugname)
153
- this.client = client
154
- }
155
-
156
- /**Set the message to send when the response times out! */
157
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODCommandResponderInstance,"slash"|"text">|null, ms:number|null){
158
- this.timeoutErrorCallback = callback
159
- this.timeoutMs = ms
160
- }
161
-
162
- add(data:ODCommandResponder<"slash"|"text",any>, overwrite?:boolean){
163
- const res = super.add(data,overwrite)
164
-
165
- //add the callback to the slash command manager
166
- this.client.slashCommands.onInteraction(data.match,(interaction,cmd) => {
167
- const newData = this.get(data.id)
168
- if (!newData) return
169
- newData.respond(new ODCommandResponderInstance(interaction,cmd,this.timeoutErrorCallback,this.timeoutMs),"slash",{})
170
- })
171
-
172
- //add the callback to the text command manager
173
- this.client.textCommands.onInteraction(data.prefix,data.match,(interaction,cmd,options) => {
174
- const newData = this.get(data.id)
175
- if (!newData) return
176
- newData.respond(new ODCommandResponderInstance(interaction,cmd,this.timeoutErrorCallback,this.timeoutMs,options),"text",{})
177
- })
178
-
179
- return res
180
- }
181
-
182
- get<CommandResponderId extends keyof ODNoGeneric<IdList>>(id:CommandResponderId): ODCommandResponder<IdList[CommandResponderId]["origin"],IdList[CommandResponderId]["params"],IdList[CommandResponderId]["workers"]>
183
- get(id:ODValidId): ODCommandResponder<"slash"|"text",any>|null
184
-
185
- get(id:ODValidId): ODCommandResponder<"slash"|"text",any>|null {
186
- return super.get(id)
187
- }
188
-
189
- remove<CommandResponderId extends keyof ODNoGeneric<IdList>>(id:CommandResponderId): ODCommandResponder<IdList[CommandResponderId]["origin"],IdList[CommandResponderId]["params"],IdList[CommandResponderId]["workers"]>
190
- remove(id:ODValidId): ODCommandResponder<"slash"|"text",any>|null
191
-
192
- remove(id:ODValidId): ODCommandResponder<"slash"|"text",any>|null {
193
- return super.remove(id)
194
- }
195
-
196
- exists(id:keyof ODNoGeneric<IdList>): boolean
197
- exists(id:ODValidId): boolean
198
-
199
- exists(id:ODValidId): boolean {
200
- return super.exists(id)
201
- }
202
- }
203
-
204
- /**## ODCommandResponderInstanceOptions `class`
205
- * This is an Open Discord command responder instance options manager.
206
- *
207
- * This class will manage all options & subcommands from slash & text commands.
208
- */
209
- export class ODCommandResponderInstanceOptions {
210
- /**The interaction to get data from. */
211
- private interaction: discord.ChatInputCommandInteraction|discord.Message
212
- /**The command which is related to the interaction. */
213
- private cmd:ODSlashCommand|ODTextCommand
214
- /**A list of options which have been parsed by the text command parser. */
215
- private options: ODTextCommandInteractionOption[]
216
-
217
- constructor(interaction:discord.ChatInputCommandInteraction|discord.Message, cmd:ODSlashCommand|ODTextCommand, options?:ODTextCommandInteractionOption[]){
218
- this.interaction = interaction
219
- this.cmd = cmd
220
- this.options = options ?? []
221
- }
222
-
223
- /**Get a string option. */
224
- getString(name:string,required:true): string
225
- getString(name:string,required:false): string|null
226
- getString(name:string,required:boolean){
227
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
228
- try {
229
- return this.interaction.options.getString(name,required)
230
- }catch{
231
- throw new ODSystemError("ODCommandResponderInstanceOptions:getString() slash command option not found!")
232
- }
233
-
234
- }else if (this.interaction instanceof discord.Message){
235
- const opt = this.options.find((opt) => opt.type == "string" && opt.name == name)
236
- if (opt && typeof opt.value == "string") return opt.value
237
- else return null
238
-
239
- }else return null
240
- }
241
- /**Get a boolean option. */
242
- getBoolean(name:string,required:true): boolean
243
- getBoolean(name:string,required:false): boolean|null
244
- getBoolean(name:string,required:boolean){
245
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
246
- try {
247
- return this.interaction.options.getBoolean(name,required)
248
- }catch{
249
- throw new ODSystemError("ODCommandResponderInstanceOptions:getBoolean() slash command option not found!")
250
- }
251
-
252
- }else if (this.interaction instanceof discord.Message){
253
- const opt = this.options.find((opt) => opt.type == "boolean" && opt.name == name)
254
- if (opt && typeof opt.value == "boolean") return opt.value
255
- else return null
256
-
257
- }else return null
258
- }
259
- /**Get a number option. */
260
- getNumber(name:string,required:true): number
261
- getNumber(name:string,required:false): number|null
262
- getNumber(name:string,required:boolean){
263
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
264
- try {
265
- return this.interaction.options.getNumber(name,required)
266
- }catch{
267
- throw new ODSystemError("ODCommandResponderInstanceOptions:getNumber() slash command option not found!")
268
- }
269
-
270
- }else if (this.interaction instanceof discord.Message){
271
- const opt = this.options.find((opt) => opt.type == "number" && opt.name == name)
272
- if (opt && typeof opt.value == "number") return opt.value
273
- else return null
274
-
275
- }else return null
276
- }
277
- /**Get a channel option. */
278
- getChannel(name:string,required:true): discord.TextChannel|discord.VoiceChannel|discord.StageChannel|discord.NewsChannel|discord.MediaChannel|discord.ForumChannel|discord.CategoryChannel
279
- getChannel(name:string,required:false): discord.TextChannel|discord.VoiceChannel|discord.StageChannel|discord.NewsChannel|discord.MediaChannel|discord.ForumChannel|discord.CategoryChannel|null
280
- getChannel(name:string,required:boolean){
281
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
282
- try {
283
- return this.interaction.options.getChannel(name,required)
284
- }catch{
285
- throw new ODSystemError("ODCommandResponderInstanceOptions:getChannel() slash command option not found!")
286
- }
287
-
288
- }else if (this.interaction instanceof discord.Message){
289
- const opt = this.options.find((opt) => opt.type == "channel" && opt.name == name)
290
- if (opt && (opt.value instanceof discord.TextChannel || opt.value instanceof discord.VoiceChannel || opt.value instanceof discord.StageChannel || opt.value instanceof discord.NewsChannel || opt.value instanceof discord.MediaChannel || opt.value instanceof discord.ForumChannel || opt.value instanceof discord.CategoryChannel)) return opt.value
291
- else return null
292
-
293
- }else return null
294
- }
295
- /**Get a role option. */
296
- getRole(name:string,required:true): discord.Role
297
- getRole(name:string,required:false): discord.Role|null
298
- getRole(name:string,required:boolean){
299
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
300
- try {
301
- return this.interaction.options.getRole(name,required)
302
- }catch{
303
- throw new ODSystemError("ODCommandResponderInstanceOptions:getRole() slash command option not found!")
304
- }
305
-
306
- }else if (this.interaction instanceof discord.Message){
307
- const opt = this.options.find((opt) => opt.type == "role" && opt.name == name)
308
- if (opt && opt.value instanceof discord.Role) return opt.value
309
- else return null
310
-
311
- }else return null
312
- }
313
- /**Get a user option. */
314
- getUser(name:string,required:true): discord.User
315
- getUser(name:string,required:false): discord.User|null
316
- getUser(name:string,required:boolean){
317
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
318
- try {
319
- return this.interaction.options.getUser(name,required)
320
- }catch{
321
- throw new ODSystemError("ODCommandResponderInstanceOptions:getUser() slash command option not found!")
322
- }
323
-
324
- }else if (this.interaction instanceof discord.Message){
325
- const opt = this.options.find((opt) => opt.type == "user" && opt.name == name)
326
- if (opt && opt.value instanceof discord.User) return opt.value
327
- else return null
328
-
329
- }else return null
330
- }
331
- /**Get a guild member option. */
332
- getGuildMember(name:string,required:true): discord.GuildMember
333
- getGuildMember(name:string,required:false): discord.GuildMember|null
334
- getGuildMember(name:string,required:boolean){
335
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
336
- try {
337
- const member = this.interaction.options.getMember(name)
338
- if (!member && required) throw new ODSystemError("ODCommandResponderInstanceOptions:getGuildMember() slash command option not found!")
339
- return member
340
- }catch{
341
- throw new ODSystemError("ODCommandResponderInstanceOptions:getGuildMember() slash command option not found!")
342
- }
343
-
344
- }else if (this.interaction instanceof discord.Message){
345
- const opt = this.options.find((opt) => opt.type == "guildmember" && opt.name == name)
346
- if (opt && opt.value instanceof discord.GuildMember) return opt.value
347
- else return null
348
-
349
- }else return null
350
- }
351
- /**Get a mentionable option. */
352
- getMentionable(name:string,required:true): discord.User|discord.GuildMember|discord.Role
353
- getMentionable(name:string,required:false): discord.User|discord.GuildMember|discord.Role|null
354
- getMentionable(name:string,required:boolean){
355
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
356
- try {
357
- return this.interaction.options.getMentionable(name,required)
358
- }catch{
359
- throw new ODSystemError("ODCommandResponderInstanceOptions:getGuildMember() slash command option not found!")
360
- }
361
-
362
- }else if (this.interaction instanceof discord.Message){
363
- const opt = this.options.find((opt) => opt.type == "mentionable" && opt.name == name)
364
- if (opt && (opt.value instanceof discord.User || opt.value instanceof discord.GuildMember || opt.value instanceof discord.Role)) return opt.value
365
- else return null
366
-
367
- }else return null
368
- }
369
- /**Get a subgroup. */
370
- getSubGroup(): string|null
371
- getSubGroup(){
372
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
373
- try {
374
- return this.interaction.options.getSubcommandGroup(true)
375
- }catch{
376
- throw new ODSystemError("ODCommandResponderInstanceOptions:getSubGroup() slash command option not found!")
377
- }
378
-
379
- }else if (this.interaction instanceof discord.Message && this.cmd instanceof ODTextCommand){
380
- //0: name, 1:sub/group, 2:sub
381
- const splittedName: string[] = this.cmd.builder.name.split(" ")
382
- return splittedName[1] ?? null
383
-
384
- }else return null
385
- }
386
- /**Get a subcommand. */
387
- getSubCommand(): string|null
388
- getSubCommand(){
389
- if (this.interaction instanceof discord.ChatInputCommandInteraction){
390
- try {
391
- return this.interaction.options.getSubcommand(true)
392
- }catch{
393
- throw new ODSystemError("ODCommandResponderInstanceOptions:getSubCommand() slash command option not found!")
394
- }
395
-
396
- }else if (this.interaction instanceof discord.Message && this.cmd instanceof ODTextCommand){
397
- //0: name, 1:sub/group, 2:sub
398
- const splittedName: string[] = this.cmd.builder.name.split(" ")
399
-
400
- //return the second subcommand when there is a subgroup
401
- if (splittedName.length > 2){
402
- return splittedName[2] ?? null
403
- }else return splittedName[1] ?? null
404
-
405
- }else return null
406
- }
407
- }
408
-
409
-
410
- /**## ODCommandResponderInstance `class`
411
- * This is an Open Discord command responder instance.
412
- *
413
- * An instance is an active slash interaction or used text command. You can reply to the command using `reply()` for both slash & text commands.
414
- */
415
- export class ODCommandResponderInstance extends ODBaseResponderInstance {
416
- /**The interaction which is the source of this instance. */
417
- interaction: discord.ChatInputCommandInteraction|discord.Message
418
- /**The command wich is the source of this instance. */
419
- cmd:ODSlashCommand|ODTextCommand
420
- /**The type/source of instance. (from text or slash command) */
421
- type: "message"|"interaction"
422
- /**Switches to `true` when a worker replies, edits or defers interaction. Will stop the timeout error from being shown. */
423
- protected ignoreResponderTimeout: boolean = false
424
- /**The manager for all options of this command. */
425
- options: ODCommandResponderInstanceOptions
426
- /**The user who triggered this command. */
427
- user: discord.User
428
- /**The guild member who triggered this command. */
429
- member: discord.GuildMember|null
430
- /**The guild where this command was triggered. */
431
- guild: discord.Guild|null
432
- /**The channel where this command was triggered. */
433
- channel: discord.TextBasedChannel
434
-
435
- constructor(interaction:discord.ChatInputCommandInteraction|discord.Message, cmd:ODSlashCommand|ODTextCommand, errorCallback:ODResponderTimeoutErrorCallback<ODCommandResponderInstance,"slash"|"text">|null, timeoutMs:number|null, options?:ODTextCommandInteractionOption[]){
436
- super()
437
- if (!interaction.channel) throw new ODSystemError("ODCommandResponderInstance: Unable to find interaction channel!")
438
- this.interaction = interaction
439
- this.cmd = cmd
440
- this.type = (interaction instanceof discord.Message) ? "message" : "interaction"
441
- this.options = new ODCommandResponderInstanceOptions(interaction,cmd,options)
442
- this.user = (interaction instanceof discord.Message) ? interaction.author : interaction.user
443
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
444
- this.guild = interaction.guild
445
- this.channel = interaction.channel
446
-
447
-
448
- setTimeout(async () => {
449
- if (this.ignoreResponderTimeout) return
450
- try {
451
- if (!errorCallback){
452
- this.reply({id:new ODId("opendiscord:unknown-error"), ephemeral:true, message:{
453
- content:":x: **Something went wrong while replying to this command!**"
454
- }})
455
- }else{
456
- const errorResponse = await errorCallback(this,(this.type == "interaction") ? "slash" : "text")
457
-
458
- //auto-delete timeout error message after 5sec when text-based
459
- if (errorResponse.success && this.type == "message") setTimeout(() => {
460
- if (errorResponse.message?.deletable) errorResponse.message?.delete()
461
- },5000)
462
- }
463
-
464
- }catch(err){
465
- process.emit("uncaughtException",err)
466
- }
467
- },timeoutMs ?? 2500)
468
- }
469
-
470
- /**Reply to this command. */
471
- async reply(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
472
- try {
473
- const finalMessage = this.getMessageFromBuildResult(build,this.type)
474
- if (this.type == "interaction" && this.interaction instanceof discord.ChatInputCommandInteraction){
475
- if (this.interaction.replied || this.interaction.deferred){
476
- const sent = await this.interaction.editReply(finalMessage)
477
- this.ignoreResponderTimeout = true
478
- return {success:true,message:sent,ephemeral:build.ephemeral}
479
- }else{
480
- const sent = await this.interaction.reply(finalMessage)
481
- this.ignoreResponderTimeout = true
482
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
483
- }
484
- }else if (this.type == "message" && this.interaction instanceof discord.Message && this.interaction.channel.type != discord.ChannelType.GroupDM){
485
- const sent = await this.interaction.channel.send(finalMessage)
486
- this.ignoreResponderTimeout = true
487
- return {success:true,message:sent,ephemeral:false}
488
- }else return {success:false}
489
- }catch{
490
- return {success:false}
491
- }
492
- }
493
- /**Defer this command. */
494
- async defer(ephemeral:boolean){
495
- this.ignoreResponderTimeout = true
496
- if (this.type != "interaction" || !(this.interaction instanceof discord.ChatInputCommandInteraction)) return false
497
- if (this.interaction.deferred || this.interaction.replied) return false
498
- const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
499
- await this.interaction.deferReply({flags:msgFlags})
500
- return true
501
- }
502
- /**Show a modal as reply to this command. */
503
- async modal(modal:ODModalBuildResult){
504
- this.ignoreResponderTimeout = true
505
- if (this.type != "interaction" || !(this.interaction instanceof discord.ChatInputCommandInteraction)) return false
506
- if (this.interaction.deferred || this.interaction.replied) return false
507
- await this.interaction.showModal(modal.modal)
508
- return true
509
- }
510
- }
511
-
512
- /**## ODCommandResponder `class`
513
- * This is an Open Discord command responder.
514
- *
515
- * This class manages all workers which are executed when the related command is triggered.
516
- */
517
- export class ODCommandResponder<Origin extends "slash"|"text",Params,WorkerIds extends string = string> extends ODResponderImplementation<ODCommandResponderInstance,Origin,Params,WorkerIds> {
518
- /**The prefix of the text command needs to match this */
519
- prefix: string
520
-
521
- constructor(id:ODValidId, prefix:string, match:string|RegExp, callback?:ODWorkerCallback<ODCommandResponderInstance,Origin,Params>, priority?:number, callbackId?:ODValidId){
522
- super(id,match,callback,priority,callbackId)
523
- this.prefix = prefix
524
- }
525
-
526
- /**Respond to this command */
527
- async respond(instance:ODCommandResponderInstance, origin:Origin, params:Params){
528
- //wait for workers to finish
529
- await this.workers.executeWorkers(instance,origin,params)
530
- }
531
- }
532
-
533
- /**## ODButtonResponderManagerIdConstraint `type`
534
- * The constraint/layout for id mappings/interfaces of the `ODButtonResponderManager` class.
535
- */
536
- export type ODButtonResponderManagerIdConstraint = Record<string,{origin:"button",params:object,workers:string}>
537
-
538
- /**## ODButtonResponderManager `class`
539
- * This is an Open Discord button responder manager.
540
- *
541
- * It contains all Open Discord button responders. These can respond to button interactions.
542
- *
543
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
544
- * - plugins can extend/edit replies
545
- * - automatically reply on error
546
- * - independent workers (with priority)
547
- * - fail-safe design using try-catch
548
- * - know where the request came from!
549
- * - And so much more!
550
- */
551
- export class ODButtonResponderManager<IdList extends ODButtonResponderManagerIdConstraint = ODButtonResponderManagerIdConstraint> extends ODManager<ODButtonResponder<"button",any>> {
552
- /**An alias to the Open Discord client manager. */
553
- private client: ODClientManager
554
- /**The callback executed when the default workers take too much time to reply. */
555
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODButtonResponderInstance,"button">|null = null
556
- /**The amount of milliseconds before the timeout error callback is executed. */
557
- private timeoutMs: number|null = null
558
- /**A list of listeners which will listen to the raw interactionCreate event from discord.js */
559
- private listeners: ((interaction:discord.ButtonInteraction) => void)[] = []
560
-
561
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
562
- super(debug,debugname)
563
- this.client = client
564
-
565
- this.client.client.on("interactionCreate",(interaction) => {
566
- if (!interaction.isButton()) return
567
- this.listeners.forEach((cb) => cb(interaction))
568
- })
569
- }
570
-
571
- /**Set the message to send when the response times out! */
572
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODButtonResponderInstance,"button">|null, ms:number|null){
573
- this.timeoutErrorCallback = callback
574
- this.timeoutMs = ms
575
- }
576
-
577
- add(data:ODButtonResponder<"button",any>, overwrite?:boolean){
578
- const res = super.add(data,overwrite)
579
-
580
- this.listeners.push((interaction) => {
581
- const newData = this.get(data.id)
582
- if (!newData) return
583
- if ((typeof newData.match == "string") ? interaction.customId == newData.match : newData.match.test(interaction.customId)) newData.respond(new ODButtonResponderInstance(interaction,this.timeoutErrorCallback,this.timeoutMs),"button",{})
584
- })
585
-
586
- return res
587
- }
588
-
589
- get<ButtonResponderId extends keyof ODNoGeneric<IdList>>(id:ButtonResponderId): ODButtonResponder<IdList[ButtonResponderId]["origin"],IdList[ButtonResponderId]["params"],IdList[ButtonResponderId]["workers"]>
590
- get(id:ODValidId): ODButtonResponder<"button",any>|null
591
-
592
- get(id:ODValidId): ODButtonResponder<"button",any>|null {
593
- return super.get(id)
594
- }
595
-
596
- remove<ButtonResponderId extends keyof ODNoGeneric<IdList>>(id:ButtonResponderId): ODButtonResponder<IdList[ButtonResponderId]["origin"],IdList[ButtonResponderId]["params"],IdList[ButtonResponderId]["workers"]>
597
- remove(id:ODValidId): ODButtonResponder<"button",any>|null
598
-
599
- remove(id:ODValidId): ODButtonResponder<"button",any>|null {
600
- return super.remove(id)
601
- }
602
-
603
- exists(id:keyof ODNoGeneric<IdList>): boolean
604
- exists(id:ODValidId): boolean
605
-
606
- exists(id:ODValidId): boolean {
607
- return super.exists(id)
608
- }
609
- }
610
-
611
- /**## ODButtonResponderInstance `class`
612
- * This is an Open Discord button responder instance.
613
- *
614
- * An instance is an active button interaction. You can reply to the button using `reply()`.
615
- */
616
- export class ODButtonResponderInstance extends ODBaseResponderInstance {
617
- /**The interaction which is the source of this instance. */
618
- interaction: discord.ButtonInteraction
619
- /**Switches to `true` when a worker replies, edits or defers interaction. Will stop the timeout error from being shown. */
620
- protected ignoreResponderTimeout: boolean = false
621
- /**The user who triggered this button. */
622
- user: discord.User
623
- /**The guild member who triggered this button. */
624
- member: discord.GuildMember|null
625
- /**The guild where this button was triggered. */
626
- guild: discord.Guild|null
627
- /**The channel where this button was triggered. */
628
- channel: discord.TextBasedChannel
629
- /**The message this button originates from. */
630
- message: discord.Message
631
-
632
- constructor(interaction:discord.ButtonInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODButtonResponderInstance,"button">|null, timeoutMs:number|null){
633
- super()
634
- if (!interaction.channel) throw new ODSystemError("ODButtonResponderInstance: Unable to find interaction channel!")
635
- this.interaction = interaction
636
- this.user = interaction.user
637
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
638
- this.guild = interaction.guild
639
- this.channel = interaction.channel
640
- this.message = interaction.message
641
-
642
- setTimeout(async () => {
643
- if (this.ignoreResponderTimeout) return
644
- try {
645
- if (!errorCallback){
646
- this.reply({id:new ODId("opendiscord:unknown-error"), ephemeral:true, message:{
647
- content:":x: **Something went wrong while replying to this button!**"
648
- }})
649
- }else{
650
- await errorCallback(this,"button")
651
- }
652
-
653
- }catch(err){
654
- process.emit("uncaughtException",err)
655
- }
656
- },timeoutMs ?? 2500)
657
- }
658
-
659
- /**Reply to this button. */
660
- async reply(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
661
- try {
662
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
663
- if (this.interaction.replied || this.interaction.deferred){
664
- const sent = await this.interaction.editReply(finalMessage)
665
- this.ignoreResponderTimeout = true
666
- return {success:true,message:sent,ephemeral:build.ephemeral}
667
- }else{
668
- const sent = await this.interaction.reply(finalMessage)
669
- this.ignoreResponderTimeout = true
670
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
671
- }
672
- }catch{
673
- return {success:false}
674
- }
675
- }
676
- /**Update the message of this button. */
677
- async update(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
678
- try {
679
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
680
- if (this.interaction.replied || this.interaction.deferred){
681
- const sent = await this.interaction.editReply(finalMessage)
682
- this.ignoreResponderTimeout = true
683
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
684
- }else{
685
- const sent = await this.interaction.update(finalMessage)
686
- this.ignoreResponderTimeout = true
687
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
688
- }
689
- }catch{
690
- return {success:false}
691
- }
692
- }
693
- /**Defer this button. */
694
- async defer(type:"reply"|"update", ephemeral:boolean){
695
- this.ignoreResponderTimeout = true
696
- if (this.interaction.deferred || this.interaction.replied) return false
697
- if (type == "reply"){
698
- const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
699
- await this.interaction.deferReply({flags:msgFlags})
700
- }else{
701
- await this.interaction.deferUpdate()
702
- }
703
- return true
704
- }
705
- /**Show a modal as reply to this button. */
706
- async modal(modal:ODModalBuildResult){
707
- this.ignoreResponderTimeout = true
708
- if (this.interaction.deferred || this.interaction.replied) return false
709
- await this.interaction.showModal(modal.modal)
710
- return true
711
- }
712
-
713
- /**Get a component from the original message of this button. */
714
- getMessageComponent(type:"button",id:string|RegExp): discord.ButtonComponent|null
715
- getMessageComponent(type:"string-dropdown",id:string|RegExp): discord.StringSelectMenuComponent|null
716
- getMessageComponent(type:"user-dropdown",id:string|RegExp): discord.UserSelectMenuComponent|null
717
- getMessageComponent(type:"channel-dropdown",id:string|RegExp): discord.ChannelSelectMenuComponent|null
718
- getMessageComponent(type:"role-dropdown",id:string|RegExp): discord.RoleSelectMenuComponent|null
719
- getMessageComponent(type:"mentionable-dropdown",id:string|RegExp): discord.MentionableSelectMenuComponent|null
720
-
721
- getMessageComponent(type:"button"|"string-dropdown"|"user-dropdown"|"channel-dropdown"|"role-dropdown"|"mentionable-dropdown", id:string|RegExp): discord.ButtonComponent|discord.StringSelectMenuComponent|discord.RoleSelectMenuComponent|discord.ChannelSelectMenuComponent|discord.MentionableSelectMenuComponent|discord.UserSelectMenuComponent|null {
722
- let result: discord.ButtonComponent|discord.StringSelectMenuComponent|discord.RoleSelectMenuComponent|discord.ChannelSelectMenuComponent|discord.MentionableSelectMenuComponent|discord.UserSelectMenuComponent|null = null
723
- this.message.components.forEach((row) => {
724
- if (row.type != discord.ComponentType.ActionRow) return
725
- row.components.forEach((component) => {
726
- if (type == "button" && component.type == discord.ComponentType.Button && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
727
- else if (type == "string-dropdown" && component.type == discord.ComponentType.StringSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
728
- else if (type == "user-dropdown" && component.type == discord.ComponentType.UserSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
729
- else if (type == "channel-dropdown" && component.type == discord.ComponentType.ChannelSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
730
- else if (type == "role-dropdown" && component.type == discord.ComponentType.RoleSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
731
- else if (type == "mentionable-dropdown" && component.type == discord.ComponentType.MentionableSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
732
- })
733
- })
734
-
735
- return result
736
- }
737
-
738
- /**Get the first embed of the original message if it exists. */
739
- getMessageEmbed(): discord.Embed|null {
740
- return this.message.embeds[0] ?? null
741
- }
742
- }
743
-
744
- /**## ODButtonResponder `class`
745
- * This is an Open Discord button responder.
746
- *
747
- * This class manages all workers which are executed when the related button is triggered.
748
- */
749
- export class ODButtonResponder<Origin extends string,Params,WorkerIds extends string = string> extends ODResponderImplementation<ODButtonResponderInstance,Origin,Params,WorkerIds> {
750
- /**Respond to this button */
751
- async respond(instance:ODButtonResponderInstance, origin:Origin, params:Params){
752
- //wait for workers to finish
753
- await this.workers.executeWorkers(instance,origin,params)
754
- }
755
- }
756
-
757
- /**## ODDropdownResponderManagerIdConstraint `type`
758
- * The constraint/layout for id mappings/interfaces of the `ODDropdownResponderManager` class.
759
- */
760
- export type ODDropdownResponderManagerIdConstraint = Record<string,{origin:"dropdown",params:object,workers:string}>
761
-
762
- /**## ODDropdownResponderManager `class`
763
- * This is an Open Discord dropdown responder manager.
764
- *
765
- * It contains all Open Discord dropdown responders. These can respond to dropdown interactions.
766
- *
767
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
768
- * - plugins can extend/edit replies
769
- * - automatically reply on error
770
- * - independent workers (with priority)
771
- * - fail-safe design using try-catch
772
- * - know where the request came from!
773
- * - And so much more!
774
- */
775
- export class ODDropdownResponderManager<IdList extends ODDropdownResponderManagerIdConstraint = ODDropdownResponderManagerIdConstraint> extends ODManager<ODDropdownResponder<"dropdown",any>> {
776
- /**An alias to the Open Discord client manager. */
777
- private client: ODClientManager
778
- /**The callback executed when the default workers take too much time to reply. */
779
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODDropdownResponderInstance,"dropdown">|null = null
780
- /**The amount of milliseconds before the timeout error callback is executed. */
781
- private timeoutMs: number|null = null
782
- /**A list of listeners which will listen to the raw interactionCreate event from discord.js */
783
- private listeners: ((interaction:discord.AnySelectMenuInteraction) => void)[] = []
784
-
785
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
786
- super(debug,debugname)
787
- this.client = client
788
-
789
- this.client.client.on("interactionCreate",(interaction) => {
790
- if (!interaction.isAnySelectMenu()) return
791
- this.listeners.forEach((cb) => cb(interaction))
792
- })
793
- }
794
-
795
- /**Set the message to send when the response times out! */
796
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODDropdownResponderInstance,"dropdown">|null, ms:number|null){
797
- this.timeoutErrorCallback = callback
798
- this.timeoutMs = ms
799
- }
800
-
801
- add(data:ODDropdownResponder<"dropdown",any>, overwrite?:boolean){
802
- const res = super.add(data,overwrite)
803
-
804
- this.listeners.push((interaction) => {
805
- const newData = this.get(data.id)
806
- if (!newData) return
807
- if ((typeof newData.match == "string") ? interaction.customId == newData.match : newData.match.test(interaction.customId)) newData.respond(new ODDropdownResponderInstance(interaction,this.timeoutErrorCallback,this.timeoutMs),"dropdown",{})
808
- })
809
-
810
- return res
811
- }
812
-
813
- get<DropdownResponderId extends keyof ODNoGeneric<IdList>>(id:DropdownResponderId): ODDropdownResponder<IdList[DropdownResponderId]["origin"],IdList[DropdownResponderId]["params"],IdList[DropdownResponderId]["workers"]>
814
- get(id:ODValidId): ODDropdownResponder<"dropdown",any>|null
815
-
816
- get(id:ODValidId): ODDropdownResponder<"dropdown",any>|null {
817
- return super.get(id)
818
- }
819
-
820
- remove<DropdownResponderId extends keyof ODNoGeneric<IdList>>(id:DropdownResponderId): ODDropdownResponder<IdList[DropdownResponderId]["origin"],IdList[DropdownResponderId]["params"],IdList[DropdownResponderId]["workers"]>
821
- remove(id:ODValidId): ODDropdownResponder<"dropdown",any>|null
822
-
823
- remove(id:ODValidId): ODDropdownResponder<"dropdown",any>|null {
824
- return super.remove(id)
825
- }
826
-
827
- exists(id:keyof ODNoGeneric<IdList>): boolean
828
- exists(id:ODValidId): boolean
829
-
830
- exists(id:ODValidId): boolean {
831
- return super.exists(id)
832
- }
833
- }
834
-
835
- /**## ODDropdownResponderInstanceValues `class`
836
- * This is an Open Discord dropdown responder instance values manager.
837
- *
838
- * This class will manage all values from the dropdowns & select menus.
839
- */
840
- export class ODDropdownResponderInstanceValues {
841
- /**The interaction to get data from. */
842
- private interaction: discord.AnySelectMenuInteraction
843
- /**The type of this dropdown. */
844
- private type: ODDropdownData["type"]
845
-
846
- constructor(interaction:discord.AnySelectMenuInteraction, type:ODDropdownData["type"]){
847
- this.interaction = interaction
848
- this.type = type
849
-
850
- if (interaction.isChannelSelectMenu()){
851
- interaction.values
852
- }
853
- }
854
-
855
- /**Get the selected values. */
856
- getStringValues(): string[] {
857
- try {
858
- return this.interaction.values
859
- }catch{
860
- throw new ODSystemError("ODDropdownResponderInstanceValues:getStringValues() invalid values!")
861
- }
862
- }
863
- /**Get the selected roles. */
864
- async getRoleValues(): Promise<discord.Role[]> {
865
- if (this.type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getRoleValues() dropdown type isn't role!")
866
- try {
867
- const result: discord.Role[] = []
868
- for (const id of this.interaction.values){
869
- if (!this.interaction.guild) break
870
- const role = await this.interaction.guild.roles.fetch(id)
871
- if (role) result.push(role)
872
- }
873
- return result
874
- }catch{
875
- throw new ODSystemError("ODDropdownResponderInstanceValues:getRoleValues() invalid values!")
876
- }
877
- }
878
- /**Get the selected users. */
879
- async getUserValues(): Promise<discord.User[]> {
880
- if (this.type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getUserValues() dropdown type isn't user!")
881
- try {
882
- const result: discord.User[] = []
883
- for (const id of this.interaction.values){
884
- const user = await this.interaction.client.users.fetch(id)
885
- if (user) result.push(user)
886
- }
887
- return result
888
- }catch{
889
- throw new ODSystemError("ODDropdownResponderInstanceValues:getUserValues() invalid values!")
890
- }
891
- }
892
- /**Get the selected channels. */
893
- async getChannelValues(): Promise<discord.GuildBasedChannel[]> {
894
- if (this.type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getChannelValues() dropdown type isn't channel!")
895
- try {
896
- const result: discord.GuildBasedChannel[] = []
897
- for (const id of this.interaction.values){
898
- if (!this.interaction.guild) break
899
- const guild = await this.interaction.guild.channels.fetch(id)
900
- if (guild) result.push(guild)
901
- }
902
- return result
903
- }catch{
904
- throw new ODSystemError("ODDropdownResponderInstanceValues:getChannelValues() invalid values!")
905
- }
906
- }
907
- }
908
-
909
- /**## ODDropdownResponderInstance `class`
910
- * This is an Open Discord dropdown responder instance.
911
- *
912
- * An instance is an active dropdown interaction. You can reply to the dropdown using `reply()`.
913
- */
914
- export class ODDropdownResponderInstance extends ODBaseResponderInstance {
915
- /**The interaction which is the source of this instance. */
916
- interaction: discord.AnySelectMenuInteraction
917
- /**Switches to `true` when a worker replies, edits or defers interaction. Will stop the timeout error from being shown. */
918
- protected ignoreResponderTimeout: boolean = false
919
- /**The dropdown type. */
920
- type: ODDropdownData["type"]
921
- /**The manager for all values of this dropdown. */
922
- values: ODDropdownResponderInstanceValues
923
- /**The user who triggered this dropdown. */
924
- user: discord.User
925
- /**The guild member who triggered this dropdown. */
926
- member: discord.GuildMember|null
927
- /**The guild where this dropdown was triggered. */
928
- guild: discord.Guild|null
929
- /**The channel where this dropdown was triggered. */
930
- channel: discord.TextBasedChannel
931
- /**The message this dropdown originates from. */
932
- message: discord.Message
933
-
934
- constructor(interaction:discord.AnySelectMenuInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODDropdownResponderInstance,"dropdown">|null, timeoutMs:number|null){
935
- super()
936
- if (!interaction.channel) throw new ODSystemError("ODDropdownResponderInstance: Unable to find interaction channel!")
937
- this.interaction = interaction
938
- if (interaction.isStringSelectMenu()){
939
- this.type = "string"
940
- }else if (interaction.isRoleSelectMenu()){
941
- this.type = "role"
942
- }else if (interaction.isUserSelectMenu()){
943
- this.type = "user"
944
- }else if (interaction.isChannelSelectMenu()){
945
- this.type = "channel"
946
- }else if (interaction.isMentionableSelectMenu()){
947
- this.type = "mentionable"
948
- }else throw new ODSystemError("ODDropdownResponderInstance: invalid dropdown type!")
949
-
950
- this.values = new ODDropdownResponderInstanceValues(interaction,this.type)
951
- this.user = interaction.user
952
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
953
- this.guild = interaction.guild
954
- this.channel = interaction.channel
955
- this.message = interaction.message
956
-
957
- setTimeout(async () => {
958
- if (this.ignoreResponderTimeout) return
959
- try {
960
- if (!errorCallback){
961
- this.reply({id:new ODId("opendiscord:unknown-error"), ephemeral:true, message:{
962
- content:":x: **Something went wrong while replying to this dropdown!**"
963
- }})
964
- }else{
965
- await errorCallback(this,"dropdown")
966
- }
967
-
968
- }catch(err){
969
- process.emit("uncaughtException",err)
970
- }
971
- },timeoutMs ?? 2500)
972
- }
973
-
974
- /**Reply to this dropdown. */
975
- async reply(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
976
- try {
977
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
978
- if (this.interaction.replied || this.interaction.deferred){
979
- const sent = await this.interaction.editReply(finalMessage)
980
- this.ignoreResponderTimeout = true
981
- return {success:true,message:sent,ephemeral:build.ephemeral}
982
- }else{
983
- const sent = await this.interaction.reply(finalMessage)
984
- this.ignoreResponderTimeout = true
985
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
986
- }
987
- }catch{
988
- return {success:false}
989
- }
990
- }
991
- /**Update the message of this dropdown. */
992
- async update(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
993
- try {
994
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
995
- if (this.interaction.replied || this.interaction.deferred){
996
- const sent = await this.interaction.editReply(finalMessage)
997
- this.ignoreResponderTimeout = true
998
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
999
- }else{
1000
- const sent = await this.interaction.update(finalMessage)
1001
- this.ignoreResponderTimeout = true
1002
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
1003
- }
1004
- }catch{
1005
- return {success:false}
1006
- }
1007
- }
1008
- /**Defer this dropdown. */
1009
- async defer(type:"reply"|"update", ephemeral:boolean){
1010
- this.ignoreResponderTimeout = true
1011
- if (this.interaction.deferred || this.interaction.replied) return false
1012
- if (type == "reply"){
1013
- const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
1014
- await this.interaction.deferReply({flags:msgFlags})
1015
- }else{
1016
- await this.interaction.deferUpdate()
1017
- }
1018
- return true
1019
- }
1020
- /**Show a modal as reply to this dropdown. */
1021
- async modal(modal:ODModalBuildResult){
1022
- this.ignoreResponderTimeout = true
1023
- if (this.interaction.deferred || this.interaction.replied) return false
1024
- await this.interaction.showModal(modal.modal)
1025
- return true
1026
- }
1027
-
1028
- /**Get a component from the original message of this dropdown. */
1029
- getMessageComponent(type:"button",id:string|RegExp): discord.ButtonComponent|null
1030
- getMessageComponent(type:"string-dropdown",id:string|RegExp): discord.StringSelectMenuComponent|null
1031
- getMessageComponent(type:"user-dropdown",id:string|RegExp): discord.UserSelectMenuComponent|null
1032
- getMessageComponent(type:"channel-dropdown",id:string|RegExp): discord.ChannelSelectMenuComponent|null
1033
- getMessageComponent(type:"role-dropdown",id:string|RegExp): discord.RoleSelectMenuComponent|null
1034
- getMessageComponent(type:"mentionable-dropdown",id:string|RegExp): discord.MentionableSelectMenuComponent|null
1035
-
1036
- getMessageComponent(type:"button"|"string-dropdown"|"user-dropdown"|"channel-dropdown"|"role-dropdown"|"mentionable-dropdown", id:string|RegExp): discord.ButtonComponent|discord.StringSelectMenuComponent|discord.RoleSelectMenuComponent|discord.ChannelSelectMenuComponent|discord.MentionableSelectMenuComponent|discord.UserSelectMenuComponent|null {
1037
- let result: discord.ButtonComponent|discord.StringSelectMenuComponent|discord.RoleSelectMenuComponent|discord.ChannelSelectMenuComponent|discord.MentionableSelectMenuComponent|discord.UserSelectMenuComponent|null = null
1038
- this.message.components.forEach((row) => {
1039
- if (row.type != discord.ComponentType.ActionRow) return
1040
- row.components.forEach((component) => {
1041
- if (type == "button" && component.type == discord.ComponentType.Button && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1042
- else if (type == "string-dropdown" && component.type == discord.ComponentType.StringSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1043
- else if (type == "user-dropdown" && component.type == discord.ComponentType.UserSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1044
- else if (type == "channel-dropdown" && component.type == discord.ComponentType.ChannelSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1045
- else if (type == "role-dropdown" && component.type == discord.ComponentType.RoleSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1046
- else if (type == "mentionable-dropdown" && component.type == discord.ComponentType.MentionableSelect && component.customId && ((typeof id == "string") ? component.customId == id : id.test(component.customId))) result = component
1047
- })
1048
- })
1049
-
1050
- return result
1051
- }
1052
-
1053
- /**Get the first embed of the original message if it exists. */
1054
- getMessageEmbed(): discord.Embed|null {
1055
- return this.message.embeds[0] ?? null
1056
- }
1057
- }
1058
-
1059
- /**## ODDropdownResponder `class`
1060
- * This is an Open Discord dropdown responder.
1061
- *
1062
- * This class manages all workers which are executed when the related dropdown is triggered.
1063
- */
1064
- export class ODDropdownResponder<Origin extends string,Params,WorkerIds extends string = string> extends ODResponderImplementation<ODDropdownResponderInstance,Origin,Params,WorkerIds> {
1065
- /**Respond to this dropdown */
1066
- async respond(instance:ODDropdownResponderInstance, origin:Origin, params:Params){
1067
- //wait for workers to finish
1068
- await this.workers.executeWorkers(instance,origin,params)
1069
- }
1070
- }
1071
-
1072
- /**## ODModalResponderManagerIdConstraint `type`
1073
- * The constraint/layout for id mappings/interfaces of the `ODModalResponderManager` class.
1074
- */
1075
- export type ODModalResponderManagerIdConstraint = Record<string,{origin:"modal",params:object,workers:string}>
1076
-
1077
- /**## ODModalResponderManager `class`
1078
- * This is an Open Discord modal responder manager.
1079
- *
1080
- * It contains all Open Discord modal responders. These can respond to modal interactions.
1081
- *
1082
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
1083
- * - plugins can extend/edit replies
1084
- * - automatically reply on error
1085
- * - independent workers (with priority)
1086
- * - fail-safe design using try-catch
1087
- * - know where the request came from!
1088
- * - And so much more!
1089
- */
1090
- export class ODModalResponderManager<IdList extends ODModalResponderManagerIdConstraint = ODModalResponderManagerIdConstraint> extends ODManager<ODModalResponder<"modal",any>> {
1091
- /**An alias to the Open Discord client manager. */
1092
- private client: ODClientManager
1093
- /**The callback executed when the default workers take too much time to reply. */
1094
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODModalResponderInstance,"modal">|null = null
1095
- /**The amount of milliseconds before the timeout error callback is executed. */
1096
- private timeoutMs: number|null = null
1097
- /**A list of listeners which will listen to the raw interactionCreate event from discord.js */
1098
- private listeners: ((interaction:discord.ModalSubmitInteraction) => void)[] = []
1099
-
1100
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
1101
- super(debug,debugname)
1102
- this.client = client
1103
-
1104
- this.client.client.on("interactionCreate",(interaction) => {
1105
- if (!interaction.isModalSubmit()) return
1106
- this.listeners.forEach((cb) => cb(interaction))
1107
- })
1108
- }
1109
-
1110
- /**Set the message to send when the response times out! */
1111
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODModalResponderInstance,"modal">|null, ms:number|null){
1112
- this.timeoutErrorCallback = callback
1113
- this.timeoutMs = ms
1114
- }
1115
-
1116
- add(data:ODModalResponder<"modal",any>, overwrite?:boolean){
1117
- const res = super.add(data,overwrite)
1118
-
1119
- this.listeners.push((interaction) => {
1120
- const newData = this.get(data.id)
1121
- if (!newData) return
1122
- if ((typeof newData.match == "string") ? interaction.customId == newData.match : newData.match.test(interaction.customId)) newData.respond(new ODModalResponderInstance(interaction,this.timeoutErrorCallback,this.timeoutMs),"modal",{})
1123
- })
1124
-
1125
- return res
1126
- }
1127
-
1128
- get<ModalResponderId extends keyof ODNoGeneric<IdList>>(id:ModalResponderId): ODModalResponder<IdList[ModalResponderId]["origin"],IdList[ModalResponderId]["params"],IdList[ModalResponderId]["workers"]>
1129
- get(id:ODValidId): ODModalResponder<"modal",any>|null
1130
-
1131
- get(id:ODValidId): ODModalResponder<"modal",any>|null {
1132
- return super.get(id)
1133
- }
1134
-
1135
- remove<ModalResponderId extends keyof ODNoGeneric<IdList>>(id:ModalResponderId): ODModalResponder<IdList[ModalResponderId]["origin"],IdList[ModalResponderId]["params"],IdList[ModalResponderId]["workers"]>
1136
- remove(id:ODValidId): ODModalResponder<"modal",any>|null
1137
-
1138
- remove(id:ODValidId): ODModalResponder<"modal",any>|null {
1139
- return super.remove(id)
1140
- }
1141
-
1142
- exists(id:keyof ODNoGeneric<IdList>): boolean
1143
- exists(id:ODValidId): boolean
1144
-
1145
- exists(id:ODValidId): boolean {
1146
- return super.exists(id)
1147
- }
1148
- }
1149
-
1150
- /**## ODModalResponderInstanceValues `class`
1151
- * This is an Open Discord modal responder instance values manager.
1152
- *
1153
- * This class will manage all fields from the modals.
1154
- */
1155
- export class ODModalResponderInstanceValues {
1156
- /**The interaction to get data from. */
1157
- private interaction: discord.ModalSubmitInteraction
1158
-
1159
- constructor(interaction:discord.ModalSubmitInteraction){
1160
- this.interaction = interaction
1161
- }
1162
-
1163
- /**Get the value of a text field. */
1164
- getTextField(name:string,required:true): string
1165
- getTextField(name:string,required:false): string|null
1166
- getTextField(name:string,required:boolean){
1167
- try {
1168
- const data = this.interaction.fields.getField(name,discord.ComponentType.TextInput)
1169
- if (!data && required) throw new ODSystemError("ODModalResponderInstanceValues:getTextField() field not found!")
1170
- return (data) ? data.value : null
1171
- }catch{
1172
- throw new ODSystemError("ODModalResponderInstanceValues:getTextField() field not found!")
1173
- }
1174
- }
1175
- }
1176
-
1177
- /**## ODModalResponderInstance `class`
1178
- * This is an Open Discord modal responder instance.
1179
- *
1180
- * An instance is an active modal interaction. You can reply to the modal using `reply()`.
1181
- */
1182
- export class ODModalResponderInstance extends ODBaseResponderInstance {
1183
- /**The interaction which is the source of this instance. */
1184
- interaction: discord.ModalSubmitInteraction
1185
- /**Switches to `true` when a worker replies, edits or defers interaction. Will stop the timeout error from being shown. */
1186
- protected ignoreResponderTimeout: boolean = false
1187
- /**The manager for all fields of this modal. */
1188
- values: ODModalResponderInstanceValues
1189
- /**The user who triggered this modal. */
1190
- user: discord.User
1191
- /**The guild member who triggered this modal. */
1192
- member: discord.GuildMember|null
1193
- /**The guild where this modal was triggered. */
1194
- guild: discord.Guild|null
1195
- /**The channel where this modal was triggered. */
1196
- channel: discord.TextBasedChannel|null
1197
-
1198
- constructor(interaction:discord.ModalSubmitInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODModalResponderInstance,"modal">|null, timeoutMs:number|null){
1199
- super()
1200
- this.interaction = interaction
1201
- this.values = new ODModalResponderInstanceValues(interaction)
1202
- this.user = interaction.user
1203
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
1204
- this.guild = interaction.guild
1205
- this.channel = interaction.channel
1206
-
1207
- setTimeout(async () => {
1208
- if (this.ignoreResponderTimeout) return
1209
- try {
1210
- if (!errorCallback){
1211
- this.reply({id:new ODId("opendiscord:unknown-error"), ephemeral:true, message:{
1212
- content:":x: **Something went wrong while replying to this modal!**"
1213
- }})
1214
- }else{
1215
- await errorCallback(this,"modal")
1216
- }
1217
-
1218
- }catch(err){
1219
- process.emit("uncaughtException",err)
1220
- }
1221
- },timeoutMs ?? 2500)
1222
- }
1223
-
1224
- /**Reply to this modal. */
1225
- async reply(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
1226
- try {
1227
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
1228
- const sent = await this.interaction.followUp(finalMessage)
1229
- this.ignoreResponderTimeout = true
1230
- return {success:true,message:sent,ephemeral:build.ephemeral}
1231
- }catch{
1232
- return {success:false}
1233
- }
1234
- }
1235
- /**Update the message of this modal. */
1236
- async update(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
1237
- try {
1238
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
1239
- if (this.interaction.replied || this.interaction.deferred){
1240
- const sent = await this.interaction.editReply(finalMessage)
1241
- this.ignoreResponderTimeout = true
1242
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
1243
- }else{
1244
- const sent = await this.interaction.reply(finalMessage)
1245
- this.ignoreResponderTimeout = true
1246
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
1247
- }
1248
- }catch{
1249
- return {success:false}
1250
- }
1251
- }
1252
- /**Defer this modal. */
1253
- async defer(type:"reply"|"update", ephemeral:boolean){
1254
- this.ignoreResponderTimeout = true
1255
- if (this.interaction.deferred || this.interaction.replied) return false
1256
- if (type == "reply"){
1257
- const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
1258
- await this.interaction.deferReply({flags:msgFlags})
1259
- }else{
1260
- await this.interaction.deferUpdate()
1261
- }
1262
- return true
1263
- }
1264
- }
1265
-
1266
- /**## ODModalResponder `class`
1267
- * This is an Open Discord modal responder.
1268
- *
1269
- * This class manages all workers which are executed when the related modal is triggered.
1270
- */
1271
- export class ODModalResponder<Origin extends string,Params,WorkerIds extends string = string> extends ODResponderImplementation<ODModalResponderInstance,Origin,Params,WorkerIds> {
1272
- /**Respond to this modal */
1273
- async respond(instance:ODModalResponderInstance, origin:Origin, params:Params){
1274
- //wait for workers to finish
1275
- await this.workers.executeWorkers(instance,origin,params)
1276
- }
1277
- }
1278
-
1279
- /**## ODContextMenuResponderManagerIdConstraint `type`
1280
- * The constraint/layout for id mappings/interfaces of the `ODContextMenuResponderManager` class.
1281
- */
1282
- export type ODContextMenuResponderManagerIdConstraint = Record<string,{origin:"context-menu",params:object,workers:string}>
1283
-
1284
- /**## ODContextMenuResponderManager `class`
1285
- * This is an Open Discord context menu responder manager.
1286
- *
1287
- * It contains all Open Discord context menu responders. These can respond to user/message context menu interactions.
1288
- *
1289
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
1290
- * - plugins can extend/edit replies
1291
- * - automatically reply on error
1292
- * - independent workers (with priority)
1293
- * - fail-safe design using try-catch
1294
- * - know where the request came from!
1295
- * - And so much more!
1296
- */
1297
- export class ODContextMenuResponderManager<IdList extends ODContextMenuResponderManagerIdConstraint = ODContextMenuResponderManagerIdConstraint> extends ODManager<ODContextMenuResponder<"context-menu",any>> {
1298
- /**An alias to the Open Discord client manager. */
1299
- private client: ODClientManager
1300
- /**The callback executed when the default workers take too much time to reply. */
1301
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null = null
1302
- /**The amount of milliseconds before the timeout error callback is executed. */
1303
- private timeoutMs: number|null = null
1304
-
1305
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
1306
- super(debug,debugname)
1307
- this.client = client
1308
- }
1309
-
1310
- /**Set the message to send when the response times out! */
1311
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, ms:number|null){
1312
- this.timeoutErrorCallback = callback
1313
- this.timeoutMs = ms
1314
- }
1315
-
1316
- add(data:ODContextMenuResponder<"context-menu",any>, overwrite?:boolean){
1317
- const res = super.add(data,overwrite)
1318
-
1319
- this.client.contextMenus.onInteraction(data.match,(interaction,cmd) => {
1320
- const newData = this.get(data.id)
1321
- if (!newData) return
1322
- newData.respond(new ODContextMenuResponderInstance(interaction,cmd,this.timeoutErrorCallback,this.timeoutMs),"context-menu",{})
1323
- })
1324
-
1325
- return res
1326
- }
1327
-
1328
- get<ModalResponderId extends keyof ODNoGeneric<IdList>>(id:ModalResponderId): ODContextMenuResponder<IdList[ModalResponderId]["origin"],IdList[ModalResponderId]["params"],IdList[ModalResponderId]["workers"]>
1329
- get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
1330
-
1331
- get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
1332
- return super.get(id)
1333
- }
1334
-
1335
- remove<ModalResponderId extends keyof ODNoGeneric<IdList>>(id:ModalResponderId): ODContextMenuResponder<IdList[ModalResponderId]["origin"],IdList[ModalResponderId]["params"],IdList[ModalResponderId]["workers"]>
1336
- remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
1337
-
1338
- remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
1339
- return super.remove(id)
1340
- }
1341
-
1342
- exists(id:keyof ODNoGeneric<IdList>): boolean
1343
- exists(id:ODValidId): boolean
1344
-
1345
- exists(id:ODValidId): boolean {
1346
- return super.exists(id)
1347
- }
1348
- }
1349
-
1350
- /**## ODContextMenuResponderInstance `class`
1351
- * This is an Open Discord context menu responder instance.
1352
- *
1353
- * An instance is an active context menu interaction. You can reply to the context menu using `reply()`.
1354
- */
1355
- export class ODContextMenuResponderInstance extends ODBaseResponderInstance {
1356
- /**The interaction which is the source of this instance. */
1357
- interaction: discord.ContextMenuCommandInteraction
1358
- /**Switches to `true` when a worker replies, edits or defers interaction. Will stop the timeout error from being shown. */
1359
- protected ignoreResponderTimeout: boolean = false
1360
- /**The context menu wich is the source of this instance. */
1361
- menu:ODContextMenu
1362
- /**The user who triggered this context menu. */
1363
- user: discord.User
1364
- /**The guild member who triggered this context menu. */
1365
- member: discord.GuildMember|null
1366
- /**The guild where this context menu was triggered. */
1367
- guild: discord.Guild|null
1368
- /**The channel where this context menu was triggered. */
1369
- channel: discord.TextBasedChannel
1370
- /**The target of this context menu (user or message). */
1371
- target: discord.Message|discord.User
1372
-
1373
- constructor(interaction:discord.ContextMenuCommandInteraction, menu:ODContextMenu, errorCallback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, timeoutMs:number|null){
1374
- super()
1375
- if (!interaction.channel) throw new ODSystemError("ODContextMenuResponderInstance: Unable to find interaction channel!")
1376
- this.interaction = interaction
1377
- this.menu = menu
1378
- this.user = interaction.user
1379
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
1380
- this.guild = interaction.guild
1381
- this.channel = interaction.channel
1382
- if (interaction.isMessageContextMenuCommand()) this.target = interaction.targetMessage
1383
- else if (interaction.isUserContextMenuCommand()) this.target = interaction.targetUser
1384
- else throw new ODSystemError("ODContextMenuResponderInstance: Invalid context menu type. Should be of the type User/Message!")
1385
-
1386
- setTimeout(async () => {
1387
- if (this.ignoreResponderTimeout) return
1388
- try {
1389
- if (!errorCallback){
1390
- this.reply({id:new ODId("opendiscord:unknown-error"), ephemeral:true, message:{
1391
- content:":x: **Something went wrong while replying to this context menu!**"
1392
- }})
1393
- }else{
1394
- await errorCallback(this,"context-menu")
1395
- }
1396
-
1397
- }catch(err){
1398
- process.emit("uncaughtException",err)
1399
- }
1400
- },timeoutMs ?? 2500)
1401
- }
1402
-
1403
- /**Reply to this context menu. */
1404
- async reply(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
1405
- try {
1406
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
1407
- if (this.interaction.replied || this.interaction.deferred){
1408
- const sent = await this.interaction.editReply(finalMessage)
1409
- this.ignoreResponderTimeout = true
1410
- return {success:true,message:sent,ephemeral:build.ephemeral}
1411
- }else{
1412
- const sent = await this.interaction.reply(finalMessage)
1413
- this.ignoreResponderTimeout = true
1414
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
1415
- }
1416
- }catch{
1417
- return {success:false}
1418
- }
1419
- }
1420
- /**Update the message of this context menu. */
1421
- async update(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<boolean>> {
1422
- try {
1423
- const finalMessage = this.getMessageFromBuildResult(build,"interaction")
1424
- if (this.interaction.replied || this.interaction.deferred){
1425
- const sent = await this.interaction.editReply(finalMessage)
1426
- this.ignoreResponderTimeout = true
1427
- return {success:true,message:await sent.fetch(),ephemeral:build.ephemeral}
1428
- }else throw new ODSystemError("Unable to update context menu interaction!")
1429
- }catch{
1430
- return {success:false}
1431
- }
1432
- }
1433
- /**Defer this context menu. */
1434
- async defer(type:"reply", ephemeral:boolean){
1435
- this.ignoreResponderTimeout = true
1436
- if (this.interaction.deferred || this.interaction.replied) return false
1437
- if (type == "reply"){
1438
- const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
1439
- await this.interaction.deferReply({flags:msgFlags})
1440
- }
1441
- return true
1442
- }
1443
- /**Show a modal as reply to this context menu. */
1444
- async modal(modal:ODModalBuildResult){
1445
- this.ignoreResponderTimeout = true
1446
- if (this.interaction.deferred || this.interaction.replied) return false
1447
- await this.interaction.showModal(modal.modal)
1448
- return true
1449
- }
1450
- }
1451
-
1452
- /**## ODContextMenuResponder `class`
1453
- * This is an Open Discord context menu responder.
1454
- *
1455
- * This class manages all workers which are executed when the related context menu is triggered.
1456
- */
1457
- export class ODContextMenuResponder<Origin extends string,Params,WorkerIds extends string = string> extends ODResponderImplementation<ODContextMenuResponderInstance,Origin,Params,WorkerIds> {
1458
- /**Respond to this button */
1459
- async respond(instance:ODContextMenuResponderInstance, origin:Origin, params:Params){
1460
- //wait for workers to finish
1461
- await this.workers.executeWorkers(instance,origin,params)
1462
- }
1463
- }
1464
-
1465
- /**## ODAutocompleteResponderManagerIdConstraint `type`
1466
- * The constraint/layout for id mappings/interfaces of the `ODAutocompleteResponderManager` class.
1467
- */
1468
- export type ODAutocompleteResponderManagerIdConstraint = Record<string,{origin:"autocomplete",params:object,workers:string}>
1469
-
1470
- /**## ODAutocompleteResponderManager `class`
1471
- * This is an Open Discord autocomplete responder manager.
1472
- *
1473
- * It contains all Open Discord autocomplete responders. These can respond to autocomplete interactions.
1474
- *
1475
- * Using the Open Discord responder system has a few advantages compared to vanilla discord.js:
1476
- * - plugins can extend/edit replies
1477
- * - automatically reply on error
1478
- * - independent workers (with priority)
1479
- * - fail-safe design using try-catch
1480
- * - know where the request came from!
1481
- * - And so much more!
1482
- */
1483
- export class ODAutocompleteResponderManager<IdList extends ODAutocompleteResponderManagerIdConstraint = ODAutocompleteResponderManagerIdConstraint> extends ODManager<ODAutocompleteResponder<"autocomplete",any>> {
1484
- /**An alias to the Open Discord client manager. */
1485
- private client: ODClientManager
1486
- /**The callback executed when the default workers take too much time to reply. */
1487
- private timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null = null
1488
- /**The amount of milliseconds before the timeout error callback is executed. */
1489
- private timeoutMs: number|null = null
1490
-
1491
- constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
1492
- super(debug,debugname)
1493
- this.client = client
1494
- }
1495
-
1496
- /**Set the message to send when the response times out! */
1497
- setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, ms:number|null){
1498
- this.timeoutErrorCallback = callback
1499
- this.timeoutMs = ms
1500
- }
1501
-
1502
- add(data:ODAutocompleteResponder<"autocomplete",any>, overwrite?:boolean){
1503
- const res = super.add(data,overwrite)
1504
-
1505
- this.client.autocompletes.onInteraction(data.cmdMatch,data.match,(interaction) => {
1506
- const newData = this.get(data.id)
1507
- if (!newData) return
1508
- newData.respond(new ODAutocompleteResponderInstance(interaction,this.timeoutErrorCallback,this.timeoutMs),"autocomplete",{})
1509
- })
1510
-
1511
- return res
1512
- }
1513
-
1514
- get<AutocompleteResponderId extends keyof ODNoGeneric<IdList>>(id:AutocompleteResponderId): ODAutocompleteResponder<IdList[AutocompleteResponderId]["origin"],IdList[AutocompleteResponderId]["params"],IdList[AutocompleteResponderId]["workers"]>
1515
- get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
1516
-
1517
- get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
1518
- return super.get(id)
1519
- }
1520
-
1521
- remove<AutocompleteResponderId extends keyof ODNoGeneric<IdList>>(id:AutocompleteResponderId): ODAutocompleteResponder<IdList[AutocompleteResponderId]["origin"],IdList[AutocompleteResponderId]["params"],IdList[AutocompleteResponderId]["workers"]>
1522
- remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
1523
-
1524
- remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
1525
- return super.remove(id)
1526
- }
1527
-
1528
- exists(id:keyof ODNoGeneric<IdList>): boolean
1529
- exists(id:ODValidId): boolean
1530
-
1531
- exists(id:ODValidId): boolean {
1532
- return super.exists(id)
1533
- }
1534
- }
1535
-
1536
- /**## ODAutocompleteResponderInstance `class`
1537
- * This is an Open Discord autocomplete responder instance.
1538
- *
1539
- * An instance is an active autocomplete interaction. You can reply to the autocomplete using `reply()`.
1540
- */
1541
- export class ODAutocompleteResponderInstance extends ODBaseResponderInstance {
1542
- /**The interaction which is the source of this instance. */
1543
- interaction: discord.AutocompleteInteraction
1544
- /**Did a worker already respond to this instance/interaction? */
1545
- didRespond: boolean = false
1546
- /**The user who triggered this autocomplete. */
1547
- user: discord.User
1548
- /**The guild member who triggered this autocomplete. */
1549
- member: discord.GuildMember|null
1550
- /**The guild where this autocomplete was triggered. */
1551
- guild: discord.Guild|null
1552
- /**The channel where this autocomplete was triggered. */
1553
- channel: discord.TextBasedChannel
1554
- /**The target slash command option of this autocomplete. */
1555
- target: discord.AutocompleteFocusedOption
1556
-
1557
- constructor(interaction:discord.AutocompleteInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, timeoutMs:number|null){
1558
- super()
1559
- if (!interaction.channel) throw new ODSystemError("ODAutocompleteResponderInstance: Unable to find interaction channel!")
1560
- this.interaction = interaction
1561
- this.user = interaction.user
1562
- this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
1563
- this.guild = interaction.guild
1564
- this.channel = interaction.channel
1565
- this.target = interaction.options.getFocused(true)
1566
-
1567
- setTimeout(async () => {
1568
- if (!this.didRespond){
1569
- process.emit("uncaughtException",new ODSystemError("Autocomplete responder instance failed to respond within 2.5sec!"))
1570
- }
1571
- },timeoutMs ?? 2500)
1572
- }
1573
-
1574
- /**Reply to this autocomplete. */
1575
- async autocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
1576
- const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
1577
- if (typeof raw == "string") return {name:raw,value:raw}
1578
- else return raw
1579
- })
1580
-
1581
- try{
1582
- if (this.interaction.responded){
1583
- return {success:false}
1584
- }else{
1585
- await this.interaction.respond(newChoices)
1586
- this.didRespond = true
1587
- return {success:true}
1588
- }
1589
- }catch(err){
1590
- process.emit("uncaughtException",err)
1591
- return {success:false}
1592
- }
1593
- }
1594
- /**Reply to this autocomplete, but filter choices based on the input of the user. */
1595
- async filteredAutocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
1596
- const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
1597
- if (typeof raw == "string") return {name:raw,value:raw}
1598
- else return raw
1599
- })
1600
-
1601
- const filteredChoices = newChoices.filter((choice) => choice.name.startsWith(this.target.value) || choice.value.toString().startsWith(this.target.value)).slice(0,25)
1602
- return await this.autocomplete(filteredChoices)
1603
- }
1604
- }
1605
-
1606
- /**## ODAutocompleteResponder `class`
1607
- * This is an Open Discord autocomplete responder.
1608
- *
1609
- * This class manages all workers which are executed when the related autocomplete is triggered.
1610
- */
1611
- export class ODAutocompleteResponder<Origin extends string,Params,WorkerIds extends string = string> extends ODResponderImplementation<ODAutocompleteResponderInstance,Origin,Params,WorkerIds> {
1612
- /**The slash command of the autocomplete should match the following regex. */
1613
- cmdMatch: string|RegExp
1614
-
1615
- constructor(id:ODValidId,cmdMatch:string|RegExp,match:string|RegExp,callback?:ODWorkerCallback<ODAutocompleteResponderInstance,Origin,Params>,priority?:number,callbackId?:ODValidId){
1616
- super(id,match,callback,priority,callbackId)
1617
- this.cmdMatch = cmdMatch
1618
- }
1619
-
1620
- /**Respond to this autocomplete interaction. */
1621
- async respond(instance:ODAutocompleteResponderInstance, origin:Origin, params:Params){
1622
- //wait for workers to finish
1623
- await this.workers.executeWorkers(instance,origin,params)
1624
- }
1625
- }