@open-discord-bots/framework 0.3.13 → 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.
- package/dist/api/main.js +1 -1
- package/dist/api/modules/responder.js +50 -26
- package/dist/api/modules/verifybar.d.ts +1 -1
- package/dist/api/modules/verifybar.js +1 -1
- package/package.json +1 -1
- package/src/api/index.ts +0 -31
- package/src/api/main.ts +0 -203
- package/src/api/modules/action.ts +0 -89
- package/src/api/modules/base.ts +0 -845
- package/src/api/modules/builder.ts +0 -1755
- package/src/api/modules/checker.ts +0 -1826
- package/src/api/modules/client.ts +0 -2345
- package/src/api/modules/code.ts +0 -84
- package/src/api/modules/component.ts +0 -2000
- package/src/api/modules/config.ts +0 -264
- package/src/api/modules/console.ts +0 -697
- package/src/api/modules/cooldown.ts +0 -369
- package/src/api/modules/database.ts +0 -321
- package/src/api/modules/event.ts +0 -123
- package/src/api/modules/flag.ts +0 -99
- package/src/api/modules/fuse.ts +0 -365
- package/src/api/modules/helpmenu.ts +0 -273
- package/src/api/modules/language.ts +0 -230
- package/src/api/modules/permission.ts +0 -363
- package/src/api/modules/plugin.ts +0 -294
- package/src/api/modules/post.ts +0 -137
- package/src/api/modules/progressbar.ts +0 -370
- package/src/api/modules/responder.ts +0 -1625
- package/src/api/modules/session.ts +0 -181
- package/src/api/modules/startscreen.ts +0 -345
- package/src/api/modules/state.ts +0 -298
- package/src/api/modules/statistic.ts +0 -380
- package/src/api/modules/verifybar.ts +0 -68
- package/src/api/modules/worker.ts +0 -119
- package/src/cli/editConfig.ts +0 -930
- package/src/cli/index.ts +0 -152
- package/src/index.ts +0 -8
- package/src/startup/compilation.ts +0 -204
- package/src/startup/dump.ts +0 -46
- package/src/startup/errorHandling.ts +0 -42
- package/src/startup/pluginLauncher.ts +0 -265
- package/src/utilities/index.ts +0 -229
- 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
|
-
}
|