@open-discord-bots/framework 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE.md +713 -0
  2. package/README.md +104 -0
  3. package/dist/api/api.d.ts +26 -0
  4. package/dist/api/api.js +44 -0
  5. package/dist/api/main.d.ts +133 -0
  6. package/dist/api/main.js +87 -0
  7. package/dist/api/modules/action.d.ts +34 -0
  8. package/dist/api/modules/action.js +58 -0
  9. package/dist/api/modules/base.d.ts +329 -0
  10. package/dist/api/modules/base.js +804 -0
  11. package/dist/api/modules/builder.d.ts +647 -0
  12. package/dist/api/modules/builder.js +1441 -0
  13. package/dist/api/modules/checker.d.ts +648 -0
  14. package/dist/api/modules/checker.js +1324 -0
  15. package/dist/api/modules/client.d.ts +768 -0
  16. package/dist/api/modules/client.js +1859 -0
  17. package/dist/api/modules/code.d.ts +33 -0
  18. package/dist/api/modules/code.js +57 -0
  19. package/dist/api/modules/config.d.ts +70 -0
  20. package/dist/api/modules/config.js +206 -0
  21. package/dist/api/modules/console.d.ts +305 -0
  22. package/dist/api/modules/console.js +598 -0
  23. package/dist/api/modules/cooldown.d.ts +138 -0
  24. package/dist/api/modules/cooldown.js +359 -0
  25. package/dist/api/modules/database.d.ts +135 -0
  26. package/dist/api/modules/database.js +271 -0
  27. package/dist/api/modules/event.d.ts +43 -0
  28. package/dist/api/modules/event.js +100 -0
  29. package/dist/api/modules/flag.d.ts +40 -0
  30. package/dist/api/modules/flag.js +72 -0
  31. package/dist/api/modules/fuse.d.ts +218 -0
  32. package/dist/api/modules/fuse.js +123 -0
  33. package/dist/api/modules/helpmenu.d.ts +106 -0
  34. package/dist/api/modules/helpmenu.js +167 -0
  35. package/dist/api/modules/language.d.ts +85 -0
  36. package/dist/api/modules/language.js +195 -0
  37. package/dist/api/modules/permission.d.ts +121 -0
  38. package/dist/api/modules/permission.js +314 -0
  39. package/dist/api/modules/plugin.d.ts +128 -0
  40. package/dist/api/modules/plugin.js +168 -0
  41. package/dist/api/modules/post.d.ts +44 -0
  42. package/dist/api/modules/post.js +92 -0
  43. package/dist/api/modules/progressbar.d.ts +108 -0
  44. package/dist/api/modules/progressbar.js +233 -0
  45. package/dist/api/modules/responder.d.ts +506 -0
  46. package/dist/api/modules/responder.js +1468 -0
  47. package/dist/api/modules/session.d.ts +58 -0
  48. package/dist/api/modules/session.js +171 -0
  49. package/dist/api/modules/startscreen.d.ts +165 -0
  50. package/dist/api/modules/startscreen.js +293 -0
  51. package/dist/api/modules/stat.d.ts +142 -0
  52. package/dist/api/modules/stat.js +293 -0
  53. package/dist/api/modules/verifybar.d.ts +54 -0
  54. package/dist/api/modules/verifybar.js +60 -0
  55. package/dist/api/modules/worker.d.ts +41 -0
  56. package/dist/api/modules/worker.js +93 -0
  57. package/dist/api/utils.d.ts +61 -0
  58. package/dist/api/utils.js +254 -0
  59. package/dist/index.d.ts +4 -1
  60. package/dist/index.js +40 -0
  61. package/dist/startup/dump.d.ts +14 -0
  62. package/dist/startup/dump.js +79 -0
  63. package/dist/startup/errorHandling.d.ts +2 -0
  64. package/dist/startup/errorHandling.js +43 -0
  65. package/dist/startup/pluginLauncher.d.ts +2 -0
  66. package/dist/startup/pluginLauncher.js +202 -0
  67. package/package.json +9 -3
  68. package/src/api/api.ts +29 -0
  69. package/src/api/main.ts +189 -0
  70. package/src/api/modules/action.ts +58 -0
  71. package/src/api/modules/base.ts +811 -0
  72. package/src/api/modules/builder.ts +1554 -0
  73. package/src/api/modules/checker.ts +1549 -0
  74. package/src/api/modules/client.ts +2247 -0
  75. package/src/api/modules/code.ts +58 -0
  76. package/src/api/modules/config.ts +159 -0
  77. package/src/api/modules/console.ts +665 -0
  78. package/src/api/modules/cooldown.ts +348 -0
  79. package/src/api/modules/database.ts +278 -0
  80. package/src/api/modules/event.ts +99 -0
  81. package/src/api/modules/flag.ts +73 -0
  82. package/src/api/modules/fuse.ts +348 -0
  83. package/src/api/modules/helpmenu.ts +216 -0
  84. package/src/api/modules/language.ts +201 -0
  85. package/src/api/modules/permission.ts +340 -0
  86. package/src/api/modules/plugin.ts +242 -0
  87. package/src/api/modules/post.ts +90 -0
  88. package/src/api/modules/progressbar.ts +232 -0
  89. package/src/api/modules/responder.ts +1420 -0
  90. package/src/api/modules/session.ts +155 -0
  91. package/src/api/modules/startscreen.ts +320 -0
  92. package/src/api/modules/stat.ts +313 -0
  93. package/src/api/modules/verifybar.ts +61 -0
  94. package/src/api/modules/worker.ts +93 -0
  95. package/src/api/utils.ts +206 -0
  96. package/src/cli/cli.ts +151 -0
  97. package/src/cli/editConfig.ts +943 -0
  98. package/src/index.ts +6 -1
  99. package/src/startup/compilation.ts +186 -0
  100. package/src/startup/dump.ts +45 -0
  101. package/src/startup/errorHandling.ts +38 -0
  102. package/src/startup/pluginLauncher.ts +261 -0
  103. package/LICENSE +0 -21
@@ -0,0 +1,1554 @@
1
+ ///////////////////////////////////////
2
+ //BUILDER MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODValidButtonColor, ODValidId, ODSystemError, ODInterfaceWithPartialProperty, ODManagerWithSafety, ODManagerData } from "./base"
5
+ import * as discord from "discord.js"
6
+ import { ODWorkerManager, ODWorkerCallback, ODWorker } from "./worker"
7
+ import { ODDebugger } from "./console"
8
+
9
+ /**## ODBuilderImplementation `class`
10
+ * This is an Open Discord builder implementation.
11
+ *
12
+ * It is a basic implementation of the `ODWorkerManager` used by all `ODBuilder` classes.
13
+ *
14
+ * This class can't be used stand-alone & needs to be extended from!
15
+ */
16
+ export class ODBuilderImplementation<Instance,Source extends string,Params,BuildType extends {id:ODId}> extends ODManagerData {
17
+ /**The manager that has all workers of this implementation */
18
+ workers: ODWorkerManager<Instance,Source,Params>
19
+ /**Cache a build or create it every time from scratch when this.build() gets executed. */
20
+ allowCache: boolean = false
21
+ /**Did the build already got created/cached? */
22
+ didCache: boolean = false
23
+ /**The cache of this build. */
24
+ cache:BuildType|null = null
25
+
26
+ constructor(id:ODValidId, callback?:ODWorkerCallback<Instance,Source,Params>, priority?:number, callbackId?:ODValidId){
27
+ super(id)
28
+ this.workers = new ODWorkerManager("ascending")
29
+ if (callback) this.workers.add(new ODWorker(callbackId ? callbackId : id,priority ?? 0,callback))
30
+ }
31
+
32
+ /**Set if caching is allowed */
33
+ setCacheMode(allowed:boolean){
34
+ this.allowCache = allowed
35
+ this.resetCache()
36
+ return this
37
+ }
38
+ /**Reset the current cache */
39
+ resetCache(){
40
+ this.cache = null
41
+ this.didCache = false
42
+ return this
43
+ }
44
+ /**Execute all workers & return the result. */
45
+ async build(source:Source, params:Params): Promise<BuildType> {
46
+ throw new ODSystemError("Tried to build an unimplemented ODBuilderImplementation")
47
+ }
48
+ }
49
+
50
+ /**## ODBuilderManager `class`
51
+ * This is an Open Discord builder manager.
52
+ *
53
+ * It contains all Open Discord builders. You can find messages, embeds, files & dropdowns, buttons & modals all here!
54
+ *
55
+ * Using the Open Discord builder system has a few advantages compared to vanilla discord.js:
56
+ * - plugins can extend/edit messages
57
+ * - automatically reply on error
58
+ * - independent workers (with priority)
59
+ * - fail-safe design using try-catch
60
+ * - cache frequently used objects
61
+ * - get to know the source of the build request for a specific message, button, etc
62
+ * - And so much more!
63
+ */
64
+ export class ODBuilderManager {
65
+ /**The manager for all button builders */
66
+ buttons: ODButtonManager
67
+ /**The manager for all dropdown builders */
68
+ dropdowns: ODDropdownManager
69
+ /**The manager for all file/attachment builders */
70
+ files: ODFileManager
71
+ /**The manager for all embed builders */
72
+ embeds: ODEmbedManager
73
+ /**The manager for all message builders */
74
+ messages: ODMessageManager
75
+ /**The manager for all modal builders */
76
+ modals: ODModalManager
77
+
78
+ constructor(debug:ODDebugger){
79
+ this.buttons = new ODButtonManager(debug)
80
+ this.dropdowns = new ODDropdownManager(debug)
81
+ this.files = new ODFileManager(debug)
82
+ this.embeds = new ODEmbedManager(debug)
83
+ this.messages = new ODMessageManager(debug)
84
+ this.modals = new ODModalManager(debug)
85
+ }
86
+ }
87
+
88
+ /**## ODComponentBuildResult `interface`
89
+ * This interface contains the result from a built component (button/dropdown). This can be used in the `ODMessage` builder!
90
+ */
91
+ export interface ODComponentBuildResult {
92
+ /**The id of this component (button or dropdown) */
93
+ id:ODId,
94
+ /**The discord component or `\n` when it is a spacer between action rows */
95
+ component:discord.MessageActionRowComponentBuilder|"\n"|null
96
+ }
97
+
98
+ /**## ODButtonManager `class`
99
+ * This is an Open Discord button manager.
100
+ *
101
+ * It contains all Open Discord button builders. Here, you can add your own buttons or edit existing ones!
102
+ *
103
+ * It's recommended to use this system in combination with all the other Open Discord builders!
104
+ */
105
+ export class ODButtonManager extends ODManagerWithSafety<ODButton<string,any>> {
106
+ constructor(debug:ODDebugger){
107
+ super(() => {
108
+ return new ODButton("opendiscord:unknown-button",(instance,params,source,cancel) => {
109
+ instance.setCustomId("od:unknown-button")
110
+ instance.setMode("button")
111
+ instance.setColor("red")
112
+ instance.setLabel("<ODError:Unknown Button>")
113
+ instance.setEmoji("✖")
114
+ instance.setDisabled(true)
115
+ cancel()
116
+ })
117
+ },debug,"button")
118
+ }
119
+
120
+ /**Get a newline component for buttons & dropdowns! */
121
+ getNewLine(id:ODValidId): ODComponentBuildResult {
122
+ return {
123
+ id:new ODId(id),
124
+ component:"\n"
125
+ }
126
+ }
127
+ }
128
+
129
+ /**## ODButtonData `interface`
130
+ * This interface contains the data to build a button.
131
+ */
132
+ export interface ODButtonData {
133
+ /**The custom id of this button */
134
+ customId:string,
135
+ /**The mode of this button */
136
+ mode:"button"|"url",
137
+ /**The url for when the mode is set to "url" */
138
+ url:string|null,
139
+ /**The button color */
140
+ color:ODValidButtonColor|null,
141
+ /**The button label */
142
+ label:string|null,
143
+ /**The button emoji */
144
+ emoji:string|null,
145
+ /**Is the button disabled? */
146
+ disabled:boolean
147
+ }
148
+
149
+ /**## ODButtonInstance `class`
150
+ * This is an Open Discord button instance.
151
+ *
152
+ * It contains all properties & functions to build a button!
153
+ */
154
+ export class ODButtonInstance {
155
+ /**The current data of this button */
156
+ data: ODButtonData = {
157
+ customId:"",
158
+ mode:"button",
159
+ url:null,
160
+ color:null,
161
+ label:null,
162
+ emoji:null,
163
+ disabled:false
164
+ }
165
+
166
+ /**Set the custom id of this button */
167
+ setCustomId(id:ODButtonData["customId"]){
168
+ this.data.customId = id
169
+ return this
170
+ }
171
+ /**Set the mode of this button */
172
+ setMode(mode:ODButtonData["mode"]){
173
+ this.data.mode = mode
174
+ return this
175
+ }
176
+ /**Set the url of this button */
177
+ setUrl(url:ODButtonData["url"]){
178
+ this.data.url = url
179
+ return this
180
+ }
181
+ /**Set the color of this button */
182
+ setColor(color:ODButtonData["color"]){
183
+ this.data.color = color
184
+ return this
185
+ }
186
+ /**Set the label of this button */
187
+ setLabel(label:ODButtonData["label"]){
188
+ this.data.label = label
189
+ return this
190
+ }
191
+ /**Set the emoji of this button */
192
+ setEmoji(emoji:ODButtonData["emoji"]){
193
+ this.data.emoji = emoji
194
+ return this
195
+ }
196
+ /**Disable this button */
197
+ setDisabled(disabled:ODButtonData["disabled"]){
198
+ this.data.disabled = disabled
199
+ return this
200
+ }
201
+ }
202
+
203
+ /**## ODButton `class`
204
+ * This is an Open Discord button builder.
205
+ *
206
+ * With this class, you can create a button to use in a message.
207
+ * The only difference with normal buttons is that this one can be edited by Open Discord plugins!
208
+ *
209
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
210
+ */
211
+ export class ODButton<Source extends string,Params> extends ODBuilderImplementation<ODButtonInstance,Source,Params,ODComponentBuildResult> {
212
+ /**Build this button & compile it for discord.js */
213
+ async build(source:Source, params:Params): Promise<ODComponentBuildResult> {
214
+ if (this.didCache && this.cache && this.allowCache) return this.cache
215
+
216
+ try {
217
+ //create instance
218
+ const instance = new ODButtonInstance()
219
+
220
+ //wait for workers to finish
221
+ await this.workers.executeWorkers(instance,source,params)
222
+
223
+ //create the discord.js button
224
+ const button = new discord.ButtonBuilder()
225
+ if (instance.data.mode == "button") button.setCustomId(instance.data.customId)
226
+ if (instance.data.mode == "url") button.setStyle(discord.ButtonStyle.Link)
227
+ else if (instance.data.color == "gray") button.setStyle(discord.ButtonStyle.Secondary)
228
+ else if (instance.data.color == "blue") button.setStyle(discord.ButtonStyle.Primary)
229
+ else if (instance.data.color == "green") button.setStyle(discord.ButtonStyle.Success)
230
+ else if (instance.data.color == "red") button.setStyle(discord.ButtonStyle.Danger)
231
+ if (instance.data.url) button.setURL(instance.data.url)
232
+ if (instance.data.label) button.setLabel(instance.data.label)
233
+ if (instance.data.emoji) button.setEmoji(instance.data.emoji)
234
+ if (instance.data.disabled) button.setDisabled(instance.data.disabled)
235
+ if (!instance.data.emoji && !instance.data.label) button.setLabel(instance.data.customId)
236
+
237
+ this.cache = {id:this.id,component:button}
238
+ this.didCache = true
239
+ return {id:this.id,component:button}
240
+ }catch(err){
241
+ process.emit("uncaughtException",new ODSystemError("ODButton:build(\""+this.id.value+"\") => Major Error (see next error)"))
242
+ process.emit("uncaughtException",err)
243
+ return {id:this.id,component:null}
244
+ }
245
+ }
246
+ }
247
+
248
+ /**## ODQuickButton `class`
249
+ * This is an Open Discord quick button builder.
250
+ *
251
+ * With this class, you can quickly create a button to use in a message.
252
+ * This quick button can be used by Open Discord plugins instead of the normal builders to speed up the process!
253
+ *
254
+ * Because of the quick functionality, these buttons are less customisable by other plugins.
255
+ */
256
+ export class ODQuickButton {
257
+ /**The id of this button. */
258
+ id: ODId
259
+ /**The current data of this button */
260
+ data: Partial<ODButtonData>
261
+
262
+ constructor(id:ODValidId,data:Partial<ODButtonData>){
263
+ this.id = new ODId(id)
264
+ this.data = data
265
+ }
266
+
267
+ /**Build this button & compile it for discord.js */
268
+ async build(): Promise<ODComponentBuildResult> {
269
+ try {
270
+ //create the discord.js button
271
+ const button = new discord.ButtonBuilder()
272
+ if (this.data.mode == "button" || (!this.data.mode && this.data.customId)) button.setCustomId(this.data.customId ?? "od:unknown-button")
273
+ if (this.data.mode == "url") button.setStyle(discord.ButtonStyle.Link)
274
+ else if (this.data.color == "gray") button.setStyle(discord.ButtonStyle.Secondary)
275
+ else if (this.data.color == "blue") button.setStyle(discord.ButtonStyle.Primary)
276
+ else if (this.data.color == "green") button.setStyle(discord.ButtonStyle.Success)
277
+ else if (this.data.color == "red") button.setStyle(discord.ButtonStyle.Danger)
278
+ else button.setStyle(discord.ButtonStyle.Secondary)
279
+ if (this.data.url) button.setURL(this.data.url)
280
+ if (this.data.label) button.setLabel(this.data.label)
281
+ if (this.data.emoji) button.setEmoji(this.data.emoji)
282
+ if (this.data.disabled) button.setDisabled(this.data.disabled)
283
+ if (!this.data.emoji && !this.data.label) button.setLabel(this.data.customId ?? "od:unknown-button")
284
+
285
+ return {id:this.id,component:button}
286
+ }catch(err){
287
+ process.emit("uncaughtException",new ODSystemError("ODQuickButton:build(\""+this.id.value+"\") => Major Error (see next error)"))
288
+ process.emit("uncaughtException",err)
289
+ return {id:this.id,component:null}
290
+ }
291
+ }
292
+ }
293
+
294
+ /**## ODDropdownManager `class`
295
+ * This is an Open Discord dropdown manager.
296
+ *
297
+ * It contains all Open Discord dropdown builders. Here, you can add your own dropdowns or edit existing ones!
298
+ *
299
+ * It's recommended to use this system in combination with all the other Open Discord builders!
300
+ */
301
+ export class ODDropdownManager extends ODManagerWithSafety<ODDropdown<string,any>> {
302
+ constructor(debug:ODDebugger){
303
+ super(() => {
304
+ return new ODDropdown("opendiscord:unknown-dropdown",(instance,params,source,cancel) => {
305
+ instance.setCustomId("od:unknown-dropdown")
306
+ instance.setType("string")
307
+ instance.setPlaceholder("❌ <ODError:Unknown Dropdown>")
308
+ instance.setDisabled(true)
309
+ instance.setOptions([
310
+ {emoji:"❌",label:"<ODError:Unknown Dropdown>",value:"error"}
311
+ ])
312
+ cancel()
313
+ })
314
+ },debug,"dropdown")
315
+ }
316
+
317
+ /**Get a newline component for buttons & dropdowns! */
318
+ getNewLine(id:ODValidId): ODComponentBuildResult {
319
+ return {
320
+ id:new ODId(id),
321
+ component:"\n"
322
+ }
323
+ }
324
+ }
325
+
326
+ /**## ODDropdownData `interface`
327
+ * This interface contains the data to build a dropdown.
328
+ */
329
+ export interface ODDropdownData {
330
+ /**The custom id of this dropdown */
331
+ customId:string,
332
+ /**The type of this dropdown */
333
+ type:"string"|"role"|"channel"|"user"|"mentionable",
334
+ /**The placeholder of this dropdown */
335
+ placeholder:string|null,
336
+ /**The minimum amount of items to be selected in this dropdown */
337
+ minValues:number|null,
338
+ /**The maximum amount of items to be selected in this dropdown */
339
+ maxValues:number|null,
340
+ /**Is this dropdown disabled? */
341
+ disabled:boolean,
342
+ /**Allowed channel types when the type is "channel" */
343
+ channelTypes:discord.ChannelType[]
344
+
345
+ /**The options when the type is "string" */
346
+ options:discord.SelectMenuComponentOptionData[],
347
+ /**The options when the type is "user" */
348
+ users:discord.User[],
349
+ /**The options when the type is "role" */
350
+ roles:discord.Role[],
351
+ /**The options when the type is "channel" */
352
+ channels:discord.Channel[],
353
+ /**The options when the type is "mentionable" */
354
+ mentionables:(discord.User|discord.Role)[],
355
+ }
356
+
357
+ /**## ODDropdownInstance `class`
358
+ * This is an Open Discord dropdown instance.
359
+ *
360
+ * It contains all properties & functions to build a dropdown!
361
+ */
362
+ export class ODDropdownInstance {
363
+ /**The current data of this dropdown */
364
+ data: ODDropdownData = {
365
+ customId:"",
366
+ type:"string",
367
+ placeholder:null,
368
+ minValues:null,
369
+ maxValues:null,
370
+ disabled:false,
371
+ channelTypes:[],
372
+
373
+ options:[],
374
+ users:[],
375
+ roles:[],
376
+ channels:[],
377
+ mentionables:[]
378
+ }
379
+
380
+ /**Set the custom id of this dropdown */
381
+ setCustomId(id:ODDropdownData["customId"]){
382
+ this.data.customId = id
383
+ return this
384
+ }
385
+ /**Set the type of this dropdown */
386
+ setType(type:ODDropdownData["type"]){
387
+ this.data.type = type
388
+ return this
389
+ }
390
+ /**Set the placeholder of this dropdown */
391
+ setPlaceholder(placeholder:ODDropdownData["placeholder"]){
392
+ this.data.placeholder = placeholder
393
+ return this
394
+ }
395
+ /**Set the minimum amount of values in this dropdown */
396
+ setMinValues(minValues:ODDropdownData["minValues"]){
397
+ this.data.minValues = minValues
398
+ return this
399
+ }
400
+ /**Set the maximum amount of values ax this dropdown */
401
+ setMaxValues(maxValues:ODDropdownData["maxValues"]){
402
+ this.data.maxValues = maxValues
403
+ return this
404
+ }
405
+ /**Set the disabled of this dropdown */
406
+ setDisabled(disabled:ODDropdownData["disabled"]){
407
+ this.data.disabled = disabled
408
+ return this
409
+ }
410
+ /**Set the channel types of this dropdown */
411
+ setChannelTypes(channelTypes:ODDropdownData["channelTypes"]){
412
+ this.data.channelTypes = channelTypes
413
+ return this
414
+ }
415
+ /**Set the options of this dropdown (when `type == "string"`) */
416
+ setOptions(options:ODDropdownData["options"]){
417
+ this.data.options = options
418
+ return this
419
+ }
420
+ /**Set the users of this dropdown (when `type == "user"`) */
421
+ setUsers(users:ODDropdownData["users"]){
422
+ this.data.users = users
423
+ return this
424
+ }
425
+ /**Set the roles of this dropdown (when `type == "role"`) */
426
+ setRoles(roles:ODDropdownData["roles"]){
427
+ this.data.roles = roles
428
+ return this
429
+ }
430
+ /**Set the channels of this dropdown (when `type == "channel"`) */
431
+ setChannels(channels:ODDropdownData["channels"]){
432
+ this.data.channels = channels
433
+ return this
434
+ }
435
+ /**Set the mentionables of this dropdown (when `type == "mentionable"`) */
436
+ setMentionables(mentionables:ODDropdownData["mentionables"]){
437
+ this.data.mentionables = mentionables
438
+ return this
439
+ }
440
+ }
441
+
442
+ /**## ODDropdown `class`
443
+ * This is an Open Discord dropdown builder.
444
+ *
445
+ * With this class, you can create a dropdown to use in a message.
446
+ * The only difference with normal dropdowns is that this one can be edited by Open Discord plugins!
447
+ *
448
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
449
+ */
450
+ export class ODDropdown<Source extends string,Params> extends ODBuilderImplementation<ODDropdownInstance,Source,Params,ODComponentBuildResult> {
451
+ /**Build this dropdown & compile it for discord.js */
452
+ async build(source:Source, params:Params): Promise<ODComponentBuildResult> {
453
+ if (this.didCache && this.cache && this.allowCache) return this.cache
454
+
455
+ try{
456
+ //create instance
457
+ const instance = new ODDropdownInstance()
458
+
459
+ //wait for workers to finish
460
+ await this.workers.executeWorkers(instance,source,params)
461
+
462
+ //create the discord.js dropdown
463
+ if (instance.data.type == "string"){
464
+ const dropdown = new discord.StringSelectMenuBuilder()
465
+ dropdown.setCustomId(instance.data.customId)
466
+ dropdown.setOptions(...instance.data.options)
467
+ if (instance.data.placeholder) dropdown.setPlaceholder(instance.data.placeholder)
468
+ if (instance.data.minValues) dropdown.setMinValues(instance.data.minValues)
469
+ if (instance.data.maxValues) dropdown.setMaxValues(instance.data.maxValues)
470
+ if (instance.data.disabled) dropdown.setDisabled(instance.data.disabled)
471
+
472
+ this.cache = {id:this.id,component:dropdown}
473
+ this.didCache = true
474
+ return {id:this.id,component:dropdown}
475
+
476
+ }else if (instance.data.type == "user"){
477
+ const dropdown = new discord.UserSelectMenuBuilder()
478
+ dropdown.setCustomId(instance.data.customId)
479
+ if (instance.data.users.length > 0) dropdown.setDefaultUsers(...instance.data.users.map((u) => u.id))
480
+ if (instance.data.placeholder) dropdown.setPlaceholder(instance.data.placeholder)
481
+ if (instance.data.minValues) dropdown.setMinValues(instance.data.minValues)
482
+ if (instance.data.maxValues) dropdown.setMaxValues(instance.data.maxValues)
483
+ if (instance.data.disabled) dropdown.setDisabled(instance.data.disabled)
484
+
485
+ this.cache = {id:this.id,component:dropdown}
486
+ this.didCache = true
487
+ return {id:this.id,component:dropdown}
488
+
489
+ }else if (instance.data.type == "role"){
490
+ const dropdown = new discord.RoleSelectMenuBuilder()
491
+ dropdown.setCustomId(instance.data.customId)
492
+ if (instance.data.roles.length > 0) dropdown.setDefaultRoles(...instance.data.roles.map((r) => r.id))
493
+ if (instance.data.placeholder) dropdown.setPlaceholder(instance.data.placeholder)
494
+ if (instance.data.minValues) dropdown.setMinValues(instance.data.minValues)
495
+ if (instance.data.maxValues) dropdown.setMaxValues(instance.data.maxValues)
496
+ if (instance.data.disabled) dropdown.setDisabled(instance.data.disabled)
497
+
498
+ this.cache = {id:this.id,component:dropdown}
499
+ this.didCache = true
500
+ return {id:this.id,component:dropdown}
501
+
502
+ }else if (instance.data.type == "channel"){
503
+ const dropdown = new discord.ChannelSelectMenuBuilder()
504
+ dropdown.setCustomId(instance.data.customId)
505
+ if (instance.data.channels.length > 0) dropdown.setDefaultChannels(...instance.data.channels.map((c) => c.id))
506
+ if (instance.data.placeholder) dropdown.setPlaceholder(instance.data.placeholder)
507
+ if (instance.data.minValues) dropdown.setMinValues(instance.data.minValues)
508
+ if (instance.data.maxValues) dropdown.setMaxValues(instance.data.maxValues)
509
+ if (instance.data.disabled) dropdown.setDisabled(instance.data.disabled)
510
+
511
+ this.cache = {id:this.id,component:dropdown}
512
+ this.didCache = true
513
+ return {id:this.id,component:dropdown}
514
+
515
+ }else if (instance.data.type == "mentionable"){
516
+ const dropdown = new discord.MentionableSelectMenuBuilder()
517
+
518
+ const values: ({type:discord.SelectMenuDefaultValueType.User,id:string}|{type:discord.SelectMenuDefaultValueType.Role,id:string})[] = []
519
+ instance.data.mentionables.forEach((m) => {
520
+ if (m instanceof discord.User){
521
+ values.push({type:discord.SelectMenuDefaultValueType.User,id:m.id})
522
+ }else{
523
+ values.push({type:discord.SelectMenuDefaultValueType.Role,id:m.id})
524
+ }
525
+ })
526
+
527
+ dropdown.setCustomId(instance.data.customId)
528
+ if (instance.data.mentionables.length > 0) dropdown.setDefaultValues(...values)
529
+ if (instance.data.placeholder) dropdown.setPlaceholder(instance.data.placeholder)
530
+ if (instance.data.minValues) dropdown.setMinValues(instance.data.minValues)
531
+ if (instance.data.maxValues) dropdown.setMaxValues(instance.data.maxValues)
532
+ if (instance.data.disabled) dropdown.setDisabled(instance.data.disabled)
533
+
534
+ this.cache = {id:this.id,component:dropdown}
535
+ this.didCache = true
536
+ return {id:this.id,component:dropdown}
537
+ }else{
538
+ throw new Error("Tried to build an ODDropdown with unknown type!")
539
+ }
540
+ }catch(err){
541
+ process.emit("uncaughtException",new ODSystemError("ODDropdown:build(\""+this.id.value+"\") => Major Error (see next error)"))
542
+ process.emit("uncaughtException",err)
543
+ return {id:this.id,component:null}
544
+ }
545
+ }
546
+ }
547
+
548
+ /**## ODQuickDropdown `class`
549
+ * This is an Open Discord quick dropdown builder.
550
+ *
551
+ * With this class, you can quickly create a dropdown to use in a message.
552
+ * This quick dropdown can be used by Open Discord plugins instead of the normal builders to speed up the process!
553
+ *
554
+ * Because of the quick functionality, these dropdowns are less customisable by other plugins.
555
+ */
556
+ export class ODQuickDropdown {
557
+ /**The id of this dropdown. */
558
+ id: ODId
559
+ /**The current data of this dropdown */
560
+ data: Partial<ODDropdownData>
561
+
562
+ constructor(id:ODValidId,data:Partial<ODDropdownData>){
563
+ this.id = new ODId(id)
564
+ this.data = data
565
+ }
566
+
567
+ /**Build this dropdown & compile it for discord.js */
568
+ async build(): Promise<ODComponentBuildResult> {
569
+ try{
570
+ //create the discord.js dropdown
571
+ if (this.data.type == "string"){
572
+ if (!this.data.options) throw new ODSystemError("ODQuickDropdown:build(): "+this.id.value+" => Dropdown requires at least 1 option to be present.")
573
+ const dropdown = new discord.StringSelectMenuBuilder()
574
+ dropdown.setCustomId(this.data.customId ?? "od:unknown-dropdown")
575
+ dropdown.setOptions(...this.data.options)
576
+ if (this.data.placeholder) dropdown.setPlaceholder(this.data.placeholder)
577
+ if (this.data.minValues) dropdown.setMinValues(this.data.minValues)
578
+ if (this.data.maxValues) dropdown.setMaxValues(this.data.maxValues)
579
+ if (this.data.disabled) dropdown.setDisabled(this.data.disabled)
580
+
581
+ return {id:this.id,component:dropdown}
582
+
583
+ }else if (this.data.type == "user"){
584
+ if (!this.data.users) throw new ODSystemError("ODQuickDropdown:build(): "+this.id.value+" => Dropdown requires at least 1 user option to be present.")
585
+ const dropdown = new discord.UserSelectMenuBuilder()
586
+ dropdown.setCustomId(this.data.customId ?? "od:unknown-dropdown")
587
+ if (this.data.users.length > 0) dropdown.setDefaultUsers(...this.data.users.map((u) => u.id))
588
+ if (this.data.placeholder) dropdown.setPlaceholder(this.data.placeholder)
589
+ if (this.data.minValues) dropdown.setMinValues(this.data.minValues)
590
+ if (this.data.maxValues) dropdown.setMaxValues(this.data.maxValues)
591
+ if (this.data.disabled) dropdown.setDisabled(this.data.disabled)
592
+
593
+ return {id:this.id,component:dropdown}
594
+
595
+ }else if (this.data.type == "role"){
596
+ if (!this.data.roles) throw new ODSystemError("ODQuickDropdown:build(): "+this.id.value+" => Dropdown requires at least 1 role option to be present.")
597
+ const dropdown = new discord.RoleSelectMenuBuilder()
598
+ dropdown.setCustomId(this.data.customId ?? "od:unknown-dropdown")
599
+ if (this.data.roles.length > 0) dropdown.setDefaultRoles(...this.data.roles.map((r) => r.id))
600
+ if (this.data.placeholder) dropdown.setPlaceholder(this.data.placeholder)
601
+ if (this.data.minValues) dropdown.setMinValues(this.data.minValues)
602
+ if (this.data.maxValues) dropdown.setMaxValues(this.data.maxValues)
603
+ if (this.data.disabled) dropdown.setDisabled(this.data.disabled)
604
+
605
+ return {id:this.id,component:dropdown}
606
+
607
+ }else if (this.data.type == "channel"){
608
+ if (!this.data.channels) throw new ODSystemError("ODQuickDropdown:build(): "+this.id.value+" => Dropdown requires at least 1 channel option to be present.")
609
+ const dropdown = new discord.ChannelSelectMenuBuilder()
610
+ dropdown.setCustomId(this.data.customId ?? "od:unknown-dropdown")
611
+ if (this.data.channels.length > 0) dropdown.setDefaultChannels(...this.data.channels.map((c) => c.id))
612
+ if (this.data.placeholder) dropdown.setPlaceholder(this.data.placeholder)
613
+ if (this.data.minValues) dropdown.setMinValues(this.data.minValues)
614
+ if (this.data.maxValues) dropdown.setMaxValues(this.data.maxValues)
615
+ if (this.data.disabled) dropdown.setDisabled(this.data.disabled)
616
+
617
+ return {id:this.id,component:dropdown}
618
+
619
+ }else if (this.data.type == "mentionable"){
620
+ if (!this.data.mentionables) throw new ODSystemError("ODQuickDropdown:build(): "+this.id.value+" => Dropdown requires at least 1 mentionable option to be present.")
621
+ const dropdown = new discord.MentionableSelectMenuBuilder()
622
+
623
+ const values: ({type:discord.SelectMenuDefaultValueType.User,id:string}|{type:discord.SelectMenuDefaultValueType.Role,id:string})[] = []
624
+ this.data.mentionables.forEach((m) => {
625
+ if (m instanceof discord.User){
626
+ values.push({type:discord.SelectMenuDefaultValueType.User,id:m.id})
627
+ }else{
628
+ values.push({type:discord.SelectMenuDefaultValueType.Role,id:m.id})
629
+ }
630
+ })
631
+
632
+ dropdown.setCustomId(this.data.customId ?? "od:unknown-dropdown")
633
+ if (this.data.mentionables.length > 0) dropdown.setDefaultValues(...values)
634
+ if (this.data.placeholder) dropdown.setPlaceholder(this.data.placeholder)
635
+ if (this.data.minValues) dropdown.setMinValues(this.data.minValues)
636
+ if (this.data.maxValues) dropdown.setMaxValues(this.data.maxValues)
637
+ if (this.data.disabled) dropdown.setDisabled(this.data.disabled)
638
+
639
+ return {id:this.id,component:dropdown}
640
+ }else{
641
+ throw new Error("Tried to build an ODQuickDropdown with unknown type!")
642
+ }
643
+ }catch(err){
644
+ process.emit("uncaughtException",new ODSystemError("ODQuickDropdown:build(\""+this.id.value+"\") => Major Error (see next error)"))
645
+ process.emit("uncaughtException",err)
646
+ return {id:this.id,component:null}
647
+ }
648
+ }
649
+ }
650
+
651
+ /**## ODFileManager `class`
652
+ * This is an Open Discord file manager.
653
+ *
654
+ * It contains all Open Discord file builders. Here, you can add your own files or edit existing ones!
655
+ *
656
+ * It's recommended to use this system in combination with all the other Open Discord builders!
657
+ */
658
+ export class ODFileManager extends ODManagerWithSafety<ODFile<string,any>> {
659
+ constructor(debug:ODDebugger){
660
+ super(() => {
661
+ return new ODFile("opendiscord:unknown-file",(instance,params,source,cancel) => {
662
+ instance.setName("opendiscord_unknown-file.txt")
663
+ instance.setDescription("❌ <ODError:Unknown File>")
664
+ instance.setContents("Couldn't find file in registery `opendiscord.builders.files`")
665
+ cancel()
666
+ })
667
+ },debug,"file")
668
+ }
669
+ }
670
+
671
+ /**## ODFileData `interface`
672
+ * This interface contains the data to build a file.
673
+ */
674
+ export interface ODFileData {
675
+ /**The file buffer, string or raw data */
676
+ file:discord.BufferResolvable
677
+ /**The name of the file */
678
+ name:string,
679
+ /**The description of the file */
680
+ description:string|null,
681
+ /**Set the file to be a spoiler */
682
+ spoiler:boolean
683
+ }
684
+
685
+ /**## ODFileBuildResult `interface`
686
+ * This interface contains the result from a built file (attachment). This can be used in the `ODMessage` builder!
687
+ */
688
+ export interface ODFileBuildResult {
689
+ /**The id of this file */
690
+ id:ODId,
691
+ /**The discord file */
692
+ file:discord.AttachmentBuilder|null
693
+ }
694
+
695
+ /**## ODFileInstance `class`
696
+ * This is an Open Discord file instance.
697
+ *
698
+ * It contains all properties & functions to build a file!
699
+ */
700
+ export class ODFileInstance {
701
+ /**The current data of this file */
702
+ data: ODFileData = {
703
+ file:"",
704
+ name:"file.txt",
705
+ description:null,
706
+ spoiler:false
707
+ }
708
+
709
+ /**Set the file path of this attachment */
710
+ setFile(file:string){
711
+ this.data.file = file
712
+ return this
713
+ }
714
+ /**Set the file contents of this attachment */
715
+ setContents(contents:string|Buffer){
716
+ this.data.file = (typeof contents == "string") ? Buffer.from(contents) : contents
717
+ return this
718
+ }
719
+ /**Set the name of this attachment */
720
+ setName(name:ODFileData["name"]){
721
+ this.data.name = name
722
+ return this
723
+ }
724
+ /**Set the description of this attachment */
725
+ setDescription(description:ODFileData["description"]){
726
+ this.data.description = description
727
+ return this
728
+ }
729
+ /**Set this attachment to show as a spoiler */
730
+ setSpoiler(spoiler:ODFileData["spoiler"]){
731
+ this.data.spoiler = spoiler
732
+ return this
733
+ }
734
+ }
735
+
736
+ /**## ODFile `class`
737
+ * This is an Open Discord file builder.
738
+ *
739
+ * With this class, you can create a file to use in a message.
740
+ * The only difference with normal files is that this one can be edited by Open Discord plugins!
741
+ *
742
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
743
+ */
744
+ export class ODFile<Source extends string,Params> extends ODBuilderImplementation<ODFileInstance,Source,Params,ODFileBuildResult> {
745
+ /**Build this attachment & compile it for discord.js */
746
+ async build(source:Source, params:Params): Promise<ODFileBuildResult> {
747
+ if (this.didCache && this.cache && this.allowCache) return this.cache
748
+
749
+ try{
750
+ //create instance
751
+ const instance = new ODFileInstance()
752
+
753
+ //wait for workers to finish
754
+ await this.workers.executeWorkers(instance,source,params)
755
+
756
+ //create the discord.js attachment
757
+ const file = new discord.AttachmentBuilder(instance.data.file)
758
+ file.setName(instance.data.name ? instance.data.name : "file.txt")
759
+ if (instance.data.description) file.setDescription(instance.data.description)
760
+ if (instance.data.spoiler) file.setSpoiler(instance.data.spoiler)
761
+
762
+
763
+ this.cache = {id:this.id,file}
764
+ this.didCache = true
765
+ return {id:this.id,file}
766
+ }catch(err){
767
+ process.emit("uncaughtException",new ODSystemError("ODFile:build(\""+this.id.value+"\") => Major Error (see next error)"))
768
+ process.emit("uncaughtException",err)
769
+ return {id:this.id,file:null}
770
+ }
771
+ }
772
+ }
773
+
774
+ /**## ODQuickFile `class`
775
+ * This is an Open Discord quick file builder.
776
+ *
777
+ * With this class, you can quickly create a file to use in a message.
778
+ * This quick file can be used by Open Discord plugins instead of the normal builders to speed up the process!
779
+ *
780
+ * Because of the quick functionality, these files are less customisable by other plugins.
781
+ */
782
+ export class ODQuickFile {
783
+ /**The id of this file. */
784
+ id: ODId
785
+ /**The current data of this file */
786
+ data: Partial<ODFileData>
787
+
788
+ constructor(id:ODValidId,data:Partial<ODFileData>){
789
+ this.id = new ODId(id)
790
+ this.data = data
791
+ }
792
+
793
+ /**Build this attachment & compile it for discord.js */
794
+ async build(): Promise<ODFileBuildResult> {
795
+ try{
796
+ //create the discord.js attachment
797
+ const file = new discord.AttachmentBuilder(this.data.file ?? "<empty-file>")
798
+ file.setName(this.data.name ? this.data.name : "file.txt")
799
+ if (this.data.description) file.setDescription(this.data.description)
800
+ if (this.data.spoiler) file.setSpoiler(this.data.spoiler)
801
+
802
+ return {id:this.id,file}
803
+ }catch(err){
804
+ process.emit("uncaughtException",new ODSystemError("ODQuickFile:build(\""+this.id.value+"\") => Major Error (see next error)"))
805
+ process.emit("uncaughtException",err)
806
+ return {id:this.id,file:null}
807
+ }
808
+ }
809
+ }
810
+
811
+ /**## ODEmbedManager `class`
812
+ * This is an Open Discord embed manager.
813
+ *
814
+ * It contains all Open Discord embed builders. Here, you can add your own embeds or edit existing ones!
815
+ *
816
+ * It's recommended to use this system in combination with all the other Open Discord builders!
817
+ */
818
+ export class ODEmbedManager extends ODManagerWithSafety<ODEmbed<string,any>> {
819
+ constructor(debug:ODDebugger){
820
+ super(() => {
821
+ return new ODEmbed("opendiscord:unknown-embed",(instance,params,source,cancel) => {
822
+ instance.setFooter("opendiscord:unknown-embed")
823
+ instance.setColor("#ff0000")
824
+ instance.setTitle("❌ <ODError:Unknown Embed>")
825
+ instance.setDescription("Couldn't find embed in registery `opendiscord.builders.embeds`")
826
+ cancel()
827
+ })
828
+ },debug,"embed")
829
+ }
830
+ }
831
+
832
+ /**## ODEmbedData `interface`
833
+ * This interface contains the data to build an embed.
834
+ */
835
+ export interface ODEmbedData {
836
+ /**The title of the embed */
837
+ title:string|null,
838
+ /**The color of the embed */
839
+ color:discord.ColorResolvable|string|null,
840
+ /**The url of the embed */
841
+ url:string|null,
842
+ /**The description of the embed */
843
+ description:string|null,
844
+ /**The author text of the embed */
845
+ authorText:string|null,
846
+ /**The author image of the embed */
847
+ authorImage:string|null,
848
+ /**The author url of the embed */
849
+ authorUrl:string|null,
850
+ /**The footer text of the embed */
851
+ footerText:string|null,
852
+ /**The footer image of the embed */
853
+ footerImage:string|null,
854
+ /**The image of the embed */
855
+ image:string|null,
856
+ /**The thumbnail of the embed */
857
+ thumbnail:string|null,
858
+ /**The fields of the embed */
859
+ fields:ODInterfaceWithPartialProperty<discord.EmbedField,"inline">[],
860
+ /**The timestamp of the embed */
861
+ timestamp:number|Date|null
862
+ }
863
+
864
+ /**## ODEmbedBuildResult `interface`
865
+ * This interface contains the result from a built embed. This can be used in the `ODMessage` builder!
866
+ */
867
+ export interface ODEmbedBuildResult {
868
+ /**The id of this embed */
869
+ id:ODId,
870
+ /**The discord embed */
871
+ embed:discord.EmbedBuilder|null
872
+ }
873
+
874
+ /**## ODEmbedInstance `class`
875
+ * This is an Open Discord embed instance.
876
+ *
877
+ * It contains all properties & functions to build an embed!
878
+ */
879
+ export class ODEmbedInstance {
880
+ /**The current data of this embed */
881
+ data: ODEmbedData = {
882
+ title:null,
883
+ color:null,
884
+ url:null,
885
+ description:null,
886
+ authorText:null,
887
+ authorImage:null,
888
+ authorUrl:null,
889
+ footerText:null,
890
+ footerImage:null,
891
+ image:null,
892
+ thumbnail:null,
893
+ fields:[],
894
+ timestamp:null
895
+ }
896
+
897
+ /**Set the title of this embed */
898
+ setTitle(title:ODEmbedData["title"]){
899
+ this.data.title = title
900
+ return this
901
+ }
902
+ /**Set the color of this embed */
903
+ setColor(color:ODEmbedData["color"]){
904
+ this.data.color = color
905
+ return this
906
+ }
907
+ /**Set the url of this embed */
908
+ setUrl(url:ODEmbedData["url"]){
909
+ this.data.url = url
910
+ return this
911
+ }
912
+ /**Set the description of this embed */
913
+ setDescription(description:ODEmbedData["description"]){
914
+ this.data.description = description
915
+ return this
916
+ }
917
+ /**Set the author of this embed */
918
+ setAuthor(text:ODEmbedData["authorText"], image?:ODEmbedData["authorImage"], url?:ODEmbedData["authorUrl"]){
919
+ this.data.authorText = text
920
+ this.data.authorImage = image ?? null
921
+ this.data.authorUrl = url ?? null
922
+ return this
923
+ }
924
+ /**Set the footer of this embed */
925
+ setFooter(text:ODEmbedData["footerText"], image?:ODEmbedData["footerImage"]){
926
+ this.data.footerText = text
927
+ this.data.footerImage = image ?? null
928
+ return this
929
+ }
930
+ /**Set the image of this embed */
931
+ setImage(image:ODEmbedData["image"]){
932
+ this.data.image = image
933
+ return this
934
+ }
935
+ /**Set the thumbnail of this embed */
936
+ setThumbnail(thumbnail:ODEmbedData["thumbnail"]){
937
+ this.data.thumbnail = thumbnail
938
+ return this
939
+ }
940
+ /**Set the fields of this embed */
941
+ setFields(fields:ODEmbedData["fields"]){
942
+ //TEMP CHECKS
943
+ fields.forEach((field,index) => {
944
+ if (field.value.length >= 1024) throw new ODSystemError("ODEmbed:setFields() => field "+index+" reached 1024 character limit!")
945
+ if (field.name.length >= 256) throw new ODSystemError("ODEmbed:setFields() => field "+index+" reached 256 name character limit!")
946
+ })
947
+
948
+ this.data.fields = fields
949
+ return this
950
+ }
951
+ /**Add fields to this embed */
952
+ addFields(...fields:ODEmbedData["fields"]){
953
+ //TEMP CHECKS
954
+ fields.forEach((field,index) => {
955
+ if (field.value.length >= 1024) throw new ODSystemError("ODEmbed:addFields() => field "+index+" reached 1024 character limit!")
956
+ if (field.name.length >= 256) throw new ODSystemError("ODEmbed:addFields() => field "+index+" reached 256 name character limit!")
957
+ })
958
+
959
+ this.data.fields.push(...fields)
960
+ return this
961
+ }
962
+ /**Clear all fields from this embed */
963
+ clearFields(){
964
+ this.data.fields = []
965
+ return this
966
+ }
967
+ /**Set the timestamp of this embed */
968
+ setTimestamp(timestamp:ODEmbedData["timestamp"]){
969
+ this.data.timestamp = timestamp
970
+ return this
971
+ }
972
+ }
973
+
974
+ /**## ODEmbed `class`
975
+ * This is an Open Discord embed builder.
976
+ *
977
+ * With this class, you can create a embed to use in a message.
978
+ * The only difference with normal embeds is that this one can be edited by Open Discord plugins!
979
+ *
980
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
981
+ */
982
+ export class ODEmbed<Source extends string,Params> extends ODBuilderImplementation<ODEmbedInstance,Source,Params,ODEmbedBuildResult> {
983
+ /**Build this embed & compile it for discord.js */
984
+ async build(source:Source, params:Params): Promise<ODEmbedBuildResult> {
985
+ if (this.didCache && this.cache && this.allowCache) return this.cache
986
+
987
+ try{
988
+ //create instance
989
+ const instance = new ODEmbedInstance()
990
+
991
+ //wait for workers to finish
992
+ await this.workers.executeWorkers(instance,source,params)
993
+
994
+ //create the discord.js embed
995
+ const embed = new discord.EmbedBuilder()
996
+ if (instance.data.title) embed.setTitle(instance.data.title)
997
+ if (instance.data.color) embed.setColor(instance.data.color as discord.ColorResolvable)
998
+ if (instance.data.url) embed.setURL(instance.data.url)
999
+ if (instance.data.description) embed.setDescription(instance.data.description)
1000
+ if (instance.data.authorText) embed.setAuthor({
1001
+ name:instance.data.authorText,
1002
+ iconURL:instance.data.authorImage ?? undefined,
1003
+ url:instance.data.authorUrl ?? undefined
1004
+ })
1005
+ if (instance.data.footerText) embed.setFooter({
1006
+ text:instance.data.footerText,
1007
+ iconURL:instance.data.footerImage ?? undefined,
1008
+ })
1009
+ if (instance.data.image) embed.setImage(instance.data.image)
1010
+ if (instance.data.thumbnail) embed.setThumbnail(instance.data.thumbnail)
1011
+ if (instance.data.timestamp) embed.setTimestamp(instance.data.timestamp)
1012
+ if (instance.data.fields.length > 0) embed.setFields(instance.data.fields)
1013
+
1014
+ this.cache = {id:this.id,embed}
1015
+ this.didCache = true
1016
+ return {id:this.id,embed}
1017
+ }catch(err){
1018
+ process.emit("uncaughtException",new ODSystemError("ODEmbed:build(\""+this.id.value+"\") => Major Error (see next error)"))
1019
+ process.emit("uncaughtException",err)
1020
+ return {id:this.id,embed:null}
1021
+ }
1022
+ }
1023
+ }
1024
+
1025
+ /**## ODQuickEmbed `class`
1026
+ * This is an Open Discord quick embed builder.
1027
+ *
1028
+ * With this class, you can quickly create a embed to use in a message.
1029
+ * This quick embed can be used by Open Discord plugins instead of the normal builders to speed up the process!
1030
+ *
1031
+ * Because of the quick functionality, these embeds are less customisable by other plugins.
1032
+ */
1033
+ export class ODQuickEmbed {
1034
+ /**The id of this embed. */
1035
+ id: ODId
1036
+ /**The current data of this embed */
1037
+ data: Partial<ODEmbedData>
1038
+
1039
+ constructor(id:ODValidId,data:Partial<ODEmbedData>){
1040
+ this.id = new ODId(id)
1041
+ this.data = data
1042
+ }
1043
+
1044
+ /**Build this embed & compile it for discord.js */
1045
+ async build(): Promise<ODEmbedBuildResult> {
1046
+ try{
1047
+ //create the discord.js embed
1048
+ const embed = new discord.EmbedBuilder()
1049
+ if (this.data.title) embed.setTitle(this.data.title)
1050
+ if (this.data.color) embed.setColor(this.data.color as discord.ColorResolvable)
1051
+ if (this.data.url) embed.setURL(this.data.url)
1052
+ if (this.data.description) embed.setDescription(this.data.description)
1053
+ if (this.data.authorText) embed.setAuthor({
1054
+ name:this.data.authorText,
1055
+ iconURL:this.data.authorImage ?? undefined,
1056
+ url:this.data.authorUrl ?? undefined
1057
+ })
1058
+ if (this.data.footerText) embed.setFooter({
1059
+ text:this.data.footerText,
1060
+ iconURL:this.data.footerImage ?? undefined,
1061
+ })
1062
+ if (this.data.image) embed.setImage(this.data.image)
1063
+ if (this.data.thumbnail) embed.setThumbnail(this.data.thumbnail)
1064
+ if (this.data.timestamp) embed.setTimestamp(this.data.timestamp)
1065
+ if (this.data.fields && this.data.fields.length > 0) embed.setFields(this.data.fields)
1066
+
1067
+ return {id:this.id,embed}
1068
+ }catch(err){
1069
+ process.emit("uncaughtException",new ODSystemError("ODQuickEmbed:build(\""+this.id.value+"\") => Major Error (see next error)"))
1070
+ process.emit("uncaughtException",err)
1071
+ return {id:this.id,embed:null}
1072
+ }
1073
+ }
1074
+ }
1075
+
1076
+ /**## ODMessageManager `class`
1077
+ * This is an Open Discord message manager.
1078
+ *
1079
+ * It contains all Open Discord message builders. Here, you can add your own messages or edit existing ones!
1080
+ *
1081
+ * It's recommended to use this system in combination with all the other Open Discord builders!
1082
+ */
1083
+ export class ODMessageManager extends ODManagerWithSafety<ODMessage<string,any>> {
1084
+ constructor(debug:ODDebugger){
1085
+ super(() => {
1086
+ return new ODMessage("opendiscord:unknown-message",(instance,params,source,cancel) => {
1087
+ instance.setContent("**❌ <ODError:Unknown Message>**\nCouldn't find message in registery `opendiscord.builders.messages`")
1088
+ cancel()
1089
+ })
1090
+ },debug,"message")
1091
+ }
1092
+ }
1093
+
1094
+ /**## ODMessageData `interface`
1095
+ * This interface contains the data to build a message.
1096
+ */
1097
+ export interface ODMessageData {
1098
+ /**The content of this message. `null` when no content */
1099
+ content:string|null,
1100
+ /**Poll data for this message */
1101
+ poll:discord.PollData|null,
1102
+ /**Try to make this message ephemeral when available */
1103
+ ephemeral:boolean,
1104
+
1105
+ /**Embeds from this message */
1106
+ embeds:ODEmbedBuildResult[],
1107
+ /**Components from this message */
1108
+ components:ODComponentBuildResult[],
1109
+ /**Files from this message */
1110
+ files:ODFileBuildResult[],
1111
+
1112
+ /**Additional options that aren't covered by the Open Discord api!*/
1113
+ additionalOptions:Omit<discord.MessageCreateOptions,"poll"|"content"|"embeds"|"components"|"files"|"flags">
1114
+ }
1115
+
1116
+ /**## ODMessageBuildResult `interface`
1117
+ * This interface contains the result from a built message. This can be sent in a discord channel!
1118
+ */
1119
+ export interface ODMessageBuildResult {
1120
+ /**The id of this message */
1121
+ id:ODId,
1122
+ /**The discord message */
1123
+ message:Omit<discord.MessageCreateOptions,"flags">,
1124
+ /**When enabled, the bot will try to send this as an ephemeral message */
1125
+ ephemeral:boolean
1126
+ }
1127
+
1128
+ /**## ODMessageBuildSentResult `interface`
1129
+ * This interface contains the result from a sent built message. This can be used to edit, view & save the message that got created.
1130
+ */
1131
+ export interface ODMessageBuildSentResult<InGuild extends boolean> {
1132
+ /**Did the message get sent successfully? */
1133
+ success:boolean,
1134
+ /**The message that got sent. */
1135
+ message:discord.Message<InGuild>|null
1136
+ }
1137
+
1138
+ /**## ODMessageInstance `class`
1139
+ * This is an Open Discord message instance.
1140
+ *
1141
+ * It contains all properties & functions to build a message!
1142
+ */
1143
+ export class ODMessageInstance {
1144
+ /**The current data of this message */
1145
+ data: ODMessageData = {
1146
+ content:null,
1147
+ poll:null,
1148
+ ephemeral:false,
1149
+ embeds:[],
1150
+ components:[],
1151
+ files:[],
1152
+ additionalOptions:{}
1153
+ }
1154
+
1155
+ /**Set the content of this message */
1156
+ setContent(content:ODMessageData["content"]){
1157
+ this.data.content = content
1158
+ return this
1159
+ }
1160
+ /**Set the poll of this message */
1161
+ setPoll(poll:ODMessageData["poll"]){
1162
+ this.data.poll = poll
1163
+ return this
1164
+ }
1165
+ /**Make this message ephemeral when possible */
1166
+ setEphemeral(ephemeral:ODMessageData["ephemeral"]){
1167
+ this.data.ephemeral = ephemeral
1168
+ return this
1169
+ }
1170
+ /**Set the embeds of this message */
1171
+ setEmbeds(...embeds:ODEmbedBuildResult[]){
1172
+ this.data.embeds = embeds
1173
+ return this
1174
+ }
1175
+ /**Add an embed to this message! */
1176
+ addEmbed(embed:ODEmbedBuildResult){
1177
+ this.data.embeds.push(embed)
1178
+ return this
1179
+ }
1180
+ /**Remove an embed from this message */
1181
+ removeEmbed(id:ODValidId){
1182
+ const index = this.data.embeds.findIndex((embed) => embed.id.value === new ODId(id).value)
1183
+ if (index > -1) this.data.embeds.splice(index,1)
1184
+ return this
1185
+ }
1186
+ /**Get an embed from this message */
1187
+ getEmbed(id:ODValidId){
1188
+ const embed = this.data.embeds.find((embed) => embed.id.value === new ODId(id).value)
1189
+ if (embed) return embed.embed
1190
+ else return null
1191
+ }
1192
+ /**Set the components of this message */
1193
+ setComponents(...components:ODComponentBuildResult[]){
1194
+ this.data.components = components
1195
+ return this
1196
+ }
1197
+ /**Add a component to this message! */
1198
+ addComponent(component:ODComponentBuildResult){
1199
+ this.data.components.push(component)
1200
+ return this
1201
+ }
1202
+ /**Remove a component from this message */
1203
+ removeComponent(id:ODValidId){
1204
+ const index = this.data.components.findIndex((component) => component.id.value === new ODId(id).value)
1205
+ if (index > -1) this.data.components.splice(index,1)
1206
+ return this
1207
+ }
1208
+ /**Get a component from this message */
1209
+ getComponent(id:ODValidId){
1210
+ const component = this.data.components.find((component) => component.id.value === new ODId(id).value)
1211
+ if (component) return component.component
1212
+ else return null
1213
+ }
1214
+ /**Set the files of this message */
1215
+ setFiles(...files:ODFileBuildResult[]){
1216
+ this.data.files = files
1217
+ return this
1218
+ }
1219
+ /**Add a file to this message! */
1220
+ addFile(file:ODFileBuildResult){
1221
+ this.data.files.push(file)
1222
+ return this
1223
+ }
1224
+ /**Remove a file from this message */
1225
+ removeFile(id:ODValidId){
1226
+ const index = this.data.files.findIndex((file) => file.id.value === new ODId(id).value)
1227
+ if (index > -1) this.data.files.splice(index,1)
1228
+ return this
1229
+ }
1230
+ /**Get a file from this message */
1231
+ getFile(id:ODValidId){
1232
+ const file = this.data.files.find((file) => file.id.value === new ODId(id).value)
1233
+ if (file) return file.file
1234
+ else return null
1235
+ }
1236
+ }
1237
+
1238
+ /**## ODMessage `class`
1239
+ * This is an Open Discord message builder.
1240
+ *
1241
+ * With this class, you can create a message to send in a discord channel.
1242
+ * The only difference with normal messages is that this one can be edited by Open Discord plugins!
1243
+ *
1244
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
1245
+ */
1246
+ export class ODMessage<Source extends string,Params> extends ODBuilderImplementation<ODMessageInstance,Source,Params,ODMessageBuildResult> {
1247
+ /**Build this message & compile it for discord.js */
1248
+ async build(source:Source, params:Params){
1249
+ if (this.didCache && this.cache && this.allowCache) return this.cache
1250
+
1251
+ //create instance
1252
+ const instance = new ODMessageInstance()
1253
+
1254
+ //wait for workers to finish
1255
+ await this.workers.executeWorkers(instance,source,params)
1256
+
1257
+ //create the discord.js message
1258
+ const componentArray: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder>[] = []
1259
+ let currentRow: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder> = new discord.ActionRowBuilder()
1260
+ instance.data.components.forEach((c) => {
1261
+ //return when component crashed
1262
+ if (c.component == null) return
1263
+ else if (c.component == "\n"){
1264
+ //create new current row when required
1265
+ if (currentRow.components.length > 0){
1266
+ componentArray.push(currentRow)
1267
+ currentRow = new discord.ActionRowBuilder()
1268
+ }
1269
+ }else if (c.component instanceof discord.BaseSelectMenuBuilder){
1270
+ //push current row when not empty
1271
+ if (currentRow.components.length > 0){
1272
+ componentArray.push(currentRow)
1273
+ currentRow = new discord.ActionRowBuilder()
1274
+ }
1275
+ currentRow.addComponents(c.component)
1276
+ //create new current row after dropdown
1277
+ componentArray.push(currentRow)
1278
+ currentRow = new discord.ActionRowBuilder()
1279
+ }else{
1280
+ //push button to current row
1281
+ currentRow.addComponents(c.component)
1282
+ }
1283
+
1284
+ //create new row when 5 rows in length
1285
+ if (currentRow.components.length == 5){
1286
+ componentArray.push(currentRow)
1287
+ currentRow = new discord.ActionRowBuilder()
1288
+ }
1289
+ })
1290
+ //push final row to array
1291
+ if (currentRow.components.length > 0) componentArray.push(currentRow)
1292
+
1293
+ const filteredEmbeds = instance.data.embeds.map((e) => e.embed).filter((e) => e instanceof discord.EmbedBuilder) as discord.EmbedBuilder[]
1294
+ const filteredFiles = instance.data.files.map((f) => f.file).filter((f) => f instanceof discord.AttachmentBuilder) as discord.AttachmentBuilder[]
1295
+
1296
+ const message : discord.MessageCreateOptions = {
1297
+ content:instance.data.content ?? "",
1298
+ poll:instance.data.poll ?? undefined,
1299
+ embeds:filteredEmbeds,
1300
+ components:componentArray,
1301
+ files:filteredFiles
1302
+ }
1303
+
1304
+ let result = {id:this.id,message,ephemeral:instance.data.ephemeral}
1305
+
1306
+ Object.assign(result.message,instance.data.additionalOptions)
1307
+
1308
+ this.cache = result
1309
+ this.didCache = true
1310
+ return result
1311
+ }
1312
+ }
1313
+
1314
+ /**## ODQuickMessage `class`
1315
+ * This is an Open Discord quick message builder.
1316
+ *
1317
+ * With this class, you can quickly create a message to send in a discord channel.
1318
+ * This quick message can be used by Open Discord plugins instead of the normal builders to speed up the process!
1319
+ *
1320
+ * Because of the quick functionality, these messages are less customisable by other plugins.
1321
+ */
1322
+ export class ODQuickMessage {
1323
+ /**The id of this message. */
1324
+ id: ODId
1325
+ /**The current data of this message. */
1326
+ data: Partial<ODMessageData>
1327
+
1328
+ constructor(id:ODValidId,data:Partial<ODMessageData>){
1329
+ this.id = new ODId(id)
1330
+ this.data = data
1331
+ }
1332
+
1333
+ /**Build this message & compile it for discord.js */
1334
+ async build(): Promise<ODMessageBuildResult> {
1335
+ //create the discord.js message
1336
+ const componentArray: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder>[] = []
1337
+ let currentRow: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder> = new discord.ActionRowBuilder()
1338
+ this.data.components?.forEach((c) => {
1339
+ //return when component crashed
1340
+ if (c.component == null) return
1341
+ else if (c.component == "\n"){
1342
+ //create new current row when required
1343
+ if (currentRow.components.length > 0){
1344
+ componentArray.push(currentRow)
1345
+ currentRow = new discord.ActionRowBuilder()
1346
+ }
1347
+ }else if (c.component instanceof discord.BaseSelectMenuBuilder){
1348
+ //push current row when not empty
1349
+ if (currentRow.components.length > 0){
1350
+ componentArray.push(currentRow)
1351
+ currentRow = new discord.ActionRowBuilder()
1352
+ }
1353
+ currentRow.addComponents(c.component)
1354
+ //create new current row after dropdown
1355
+ componentArray.push(currentRow)
1356
+ currentRow = new discord.ActionRowBuilder()
1357
+ }else{
1358
+ //push button to current row
1359
+ currentRow.addComponents(c.component)
1360
+ }
1361
+
1362
+ //create new row when 5 rows in length
1363
+ if (currentRow.components.length == 5){
1364
+ componentArray.push(currentRow)
1365
+ currentRow = new discord.ActionRowBuilder()
1366
+ }
1367
+ })
1368
+ //push final row to array
1369
+ if (currentRow.components.length > 0) componentArray.push(currentRow)
1370
+
1371
+ const filteredEmbeds = (this.data.embeds?.map((e) => e.embed).filter((e) => e instanceof discord.EmbedBuilder) as discord.EmbedBuilder[]) ?? []
1372
+ const filteredFiles = (this.data.files?.map((f) => f.file).filter((f) => f instanceof discord.AttachmentBuilder) as discord.AttachmentBuilder[]) ?? []
1373
+
1374
+ const message : discord.MessageCreateOptions = {
1375
+ content:this.data.content ?? "",
1376
+ poll:this.data.poll ?? undefined,
1377
+ embeds:filteredEmbeds,
1378
+ components:componentArray,
1379
+ files:filteredFiles
1380
+ }
1381
+
1382
+ let result = {id:this.id,message,ephemeral:this.data.ephemeral ?? false}
1383
+
1384
+ Object.assign(result.message,this.data.additionalOptions)
1385
+ return result
1386
+ }
1387
+ }
1388
+
1389
+ /**## ODModalManager `class`
1390
+ * This is an Open Discord modal manager.
1391
+ *
1392
+ * It contains all Open Discord modal builders. Here, you can add your own modals or edit existing ones!
1393
+ *
1394
+ * It's recommended to use this system in combination with all the other Open Discord builders!
1395
+ */
1396
+ export class ODModalManager extends ODManagerWithSafety<ODModal<string,any>> {
1397
+ constructor(debug:ODDebugger){
1398
+ super(() => {
1399
+ return new ODModal("opendiscord:unknown-modal",(instance,params,source,cancel) => {
1400
+ instance.setCustomId("od:unknown-modal")
1401
+ instance.setTitle("❌ <ODError:Unknown Modal>")
1402
+ instance.setQuestions(
1403
+ {
1404
+ style:"short",
1405
+ customId:"error",
1406
+ label:"error",
1407
+ placeholder:"Contact the bot creator for more info!"
1408
+ }
1409
+ )
1410
+ cancel()
1411
+ })
1412
+ },debug,"modal")
1413
+ }
1414
+ }
1415
+
1416
+ /**## ODModalDataQuestion `interface`
1417
+ * This interface contains the data to build a modal question.
1418
+ */
1419
+ export interface ODModalDataQuestion {
1420
+ /**The style of this modal question */
1421
+ style:"short"|"paragraph",
1422
+ /**The custom id of this modal question */
1423
+ customId:string
1424
+ /**The label of this modal question */
1425
+ label?:string,
1426
+ /**The min length of this modal question */
1427
+ minLength?:number,
1428
+ /**The max length of this modal question */
1429
+ maxLength?:number,
1430
+ /**Is this modal question required? */
1431
+ required?:boolean,
1432
+ /**The placeholder of this modal question */
1433
+ placeholder?:string,
1434
+ /**The initial value of this modal question */
1435
+ value?:string
1436
+ }
1437
+
1438
+ /**## ODModalData `interface`
1439
+ * This interface contains the data to build a modal.
1440
+ */
1441
+ export interface ODModalData {
1442
+ /**The custom id of this modal */
1443
+ customId:string,
1444
+ /**The title of this modal */
1445
+ title:string|null,
1446
+ /**The collection of questions in this modal */
1447
+ questions:ODModalDataQuestion[],
1448
+ }
1449
+
1450
+ /**## ODModalBuildResult `interface`
1451
+ * This interface contains the result from a built modal (form). This can be used in the `ODMessage` builder!
1452
+ */
1453
+ export interface ODModalBuildResult {
1454
+ /**The id of this modal */
1455
+ id:ODId,
1456
+ /**The discord modal */
1457
+ modal:discord.ModalBuilder
1458
+ }
1459
+
1460
+ /**## ODModalInstance `class`
1461
+ * This is an Open Discord modal instance.
1462
+ *
1463
+ * It contains all properties & functions to build a modal!
1464
+ */
1465
+ export class ODModalInstance {
1466
+ /**The current data of this modal */
1467
+ data: ODModalData = {
1468
+ customId:"",
1469
+ title:null,
1470
+ questions:[]
1471
+ }
1472
+
1473
+ /**Set the custom id of this modal */
1474
+ setCustomId(customId:ODModalData["customId"]){
1475
+ this.data.customId = customId
1476
+ return this
1477
+ }
1478
+ /**Set the title of this modal */
1479
+ setTitle(title:ODModalData["title"]){
1480
+ this.data.title = title
1481
+ return this
1482
+ }
1483
+ /**Set the questions of this modal */
1484
+ setQuestions(...questions:ODModalData["questions"]){
1485
+ this.data.questions = questions
1486
+ return this
1487
+ }
1488
+ /**Add a question to this modal! */
1489
+ addQuestion(question:ODModalDataQuestion){
1490
+ this.data.questions.push(question)
1491
+ return this
1492
+ }
1493
+ /**Remove a question from this modal */
1494
+ removeQuestion(customId:string){
1495
+ const index = this.data.questions.findIndex((question) => question.customId === customId)
1496
+ if (index > -1) this.data.questions.splice(index,1)
1497
+ return this
1498
+ }
1499
+ /**Get a question from this modal */
1500
+ getQuestion(customId:string){
1501
+ const question = this.data.questions.find((question) => question.customId === customId)
1502
+ if (question) return question
1503
+ else return null
1504
+ }
1505
+ }
1506
+
1507
+ /**## ODModal `class`
1508
+ * This is an Open Discord modal builder.
1509
+ *
1510
+ * With this class, you can create a modal to use as response in interactions.
1511
+ * The only difference with normal modals is that this one can be edited by Open Discord plugins!
1512
+ *
1513
+ * This is possible by using "workers" or multiple functions that will be executed in priority order!
1514
+ */
1515
+ export class ODModal<Source extends string,Params> extends ODBuilderImplementation<ODModalInstance,Source,Params,ODModalBuildResult> {
1516
+ /**Build this modal & compile it for discord.js */
1517
+ async build(source:Source, params:Params){
1518
+ if (this.didCache && this.cache && this.allowCache) return this.cache
1519
+
1520
+ //create instance
1521
+ const instance = new ODModalInstance()
1522
+
1523
+ //wait for workers to finish
1524
+ await this.workers.executeWorkers(instance,source,params)
1525
+
1526
+ //create the discord.js modal
1527
+ const modal = new discord.ModalBuilder()
1528
+ modal.setCustomId(instance.data.customId)
1529
+ if (instance.data.title) modal.setTitle(instance.data.title)
1530
+ else modal.setTitle(instance.data.customId)
1531
+
1532
+ instance.data.questions.forEach((question) => {
1533
+ const input = new discord.TextInputBuilder()
1534
+ .setStyle(question.style == "paragraph" ? discord.TextInputStyle.Paragraph : discord.TextInputStyle.Short)
1535
+ .setCustomId(question.customId)
1536
+ .setLabel(question.label ? question.label : question.customId)
1537
+ .setRequired(question.required ? true : false)
1538
+
1539
+ if (question.minLength) input.setMinLength(question.minLength)
1540
+ if (question.maxLength) input.setMaxLength(question.maxLength)
1541
+ if (question.value) input.setValue(question.value)
1542
+ if (question.placeholder) input.setPlaceholder(question.placeholder)
1543
+
1544
+ modal.addComponents(
1545
+ new discord.ActionRowBuilder<discord.ModalActionRowComponentBuilder>()
1546
+ .addComponents(input)
1547
+ )
1548
+ })
1549
+
1550
+ this.cache = {id:this.id,modal}
1551
+ this.didCache = true
1552
+ return {id:this.id,modal}
1553
+ }
1554
+ }