@open-discord-bots/framework 0.2.17 → 0.3.1

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 (76) hide show
  1. package/dist/api/index.d.ts +16 -15
  2. package/dist/api/index.js +16 -15
  3. package/dist/api/main.d.ts +31 -23
  4. package/dist/api/main.js +3 -1
  5. package/dist/api/modules/action.d.ts +2 -2
  6. package/dist/api/modules/action.js +1 -5
  7. package/dist/api/modules/base.d.ts +29 -11
  8. package/dist/api/modules/base.js +78 -80
  9. package/dist/api/modules/builder.d.ts +2 -11
  10. package/dist/api/modules/builder.js +0 -4
  11. package/dist/api/modules/checker.d.ts +28 -7
  12. package/dist/api/modules/checker.js +33 -37
  13. package/dist/api/modules/client.d.ts +66 -14
  14. package/dist/api/modules/client.js +146 -132
  15. package/dist/api/modules/component.d.ts +928 -0
  16. package/dist/api/modules/component.js +1346 -0
  17. package/dist/api/modules/config.d.ts +30 -2
  18. package/dist/api/modules/config.js +90 -7
  19. package/dist/api/modules/console.d.ts +16 -4
  20. package/dist/api/modules/console.js +25 -25
  21. package/dist/api/modules/cooldown.d.ts +5 -5
  22. package/dist/api/modules/cooldown.js +1 -17
  23. package/dist/api/modules/database.d.ts +21 -13
  24. package/dist/api/modules/database.js +0 -23
  25. package/dist/api/modules/event.d.ts +4 -2
  26. package/dist/api/modules/event.js +8 -10
  27. package/dist/api/modules/fuse.d.ts +1 -1
  28. package/dist/api/modules/helpmenu.d.ts +11 -9
  29. package/dist/api/modules/helpmenu.js +24 -22
  30. package/dist/api/modules/language.d.ts +4 -3
  31. package/dist/api/modules/language.js +9 -16
  32. package/dist/api/modules/permission.d.ts +10 -1
  33. package/dist/api/modules/permission.js +17 -20
  34. package/dist/api/modules/plugin.d.ts +2 -1
  35. package/dist/api/modules/plugin.js +2 -2
  36. package/dist/api/modules/post.d.ts +12 -4
  37. package/dist/api/modules/post.js +36 -10
  38. package/dist/api/modules/progressbar.d.ts +18 -6
  39. package/dist/api/modules/progressbar.js +35 -35
  40. package/dist/api/modules/responder.d.ts +97 -28
  41. package/dist/api/modules/responder.js +213 -176
  42. package/dist/api/modules/session.d.ts +11 -2
  43. package/dist/api/modules/session.js +16 -16
  44. package/dist/api/modules/startscreen.d.ts +2 -3
  45. package/dist/api/modules/startscreen.js +8 -9
  46. package/dist/api/modules/statistic.d.ts +2 -1
  47. package/dist/api/modules/statistic.js +4 -7
  48. package/dist/api/modules/worker.d.ts +2 -1
  49. package/dist/api/modules/worker.js +3 -3
  50. package/package.json +3 -2
  51. package/src/api/index.ts +16 -15
  52. package/src/api/main.ts +33 -24
  53. package/src/api/modules/action.ts +2 -4
  54. package/src/api/modules/base.ts +77 -79
  55. package/src/api/modules/builder.ts +2 -14
  56. package/src/api/modules/checker.ts +36 -37
  57. package/src/api/modules/client.ts +144 -136
  58. package/src/api/modules/component.ts +1826 -0
  59. package/src/api/modules/config.ts +86 -7
  60. package/src/api/modules/console.ts +25 -25
  61. package/src/api/modules/cooldown.ts +8 -13
  62. package/src/api/modules/database.ts +24 -32
  63. package/src/api/modules/event.ts +6 -10
  64. package/src/api/modules/fuse.ts +1 -1
  65. package/src/api/modules/helpmenu.ts +31 -27
  66. package/src/api/modules/language.ts +11 -16
  67. package/src/api/modules/permission.ts +17 -20
  68. package/src/api/modules/plugin.ts +2 -2
  69. package/src/api/modules/post.ts +31 -10
  70. package/src/api/modules/progressbar.ts +36 -37
  71. package/src/api/modules/responder.ts +234 -185
  72. package/src/api/modules/session.ts +15 -15
  73. package/src/api/modules/startscreen.ts +9 -10
  74. package/src/api/modules/statistic.ts +4 -7
  75. package/src/api/modules/worker.ts +3 -3
  76. package/src/api/modules/component.txt +0 -350
@@ -0,0 +1,1826 @@
1
+ ///////////////////////////////////////
2
+ //COMPONENTS MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODValidId, ODSystemError, ODManagerData, ODNoGeneric, ODManager, ODValidButtonColor, ODInterfaceWithPartialProperty } 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
+
9
+ /**## ODComponentFactoryInstance `class`
10
+ * An Open Discord component factory instance.
11
+ *
12
+ * It will contain the final root component which is returned by an `ODComponentFactory`.
13
+ * This component can be used in another `ODComponentFactory` or rendered to a message/modal.
14
+ */
15
+ export class ODComponentFactoryInstance<Component extends ODComponent<object,any>> {
16
+ /**The root component of this factory. */
17
+ private rootComponent: Component|null = null
18
+
19
+ /**The root component of this factory. */
20
+ getComponent(){
21
+ return this.rootComponent
22
+ }
23
+ /**Set the root component of this factory. */
24
+ setComponent(c:Component|null){
25
+ this.rootComponent = c
26
+ }
27
+ }
28
+
29
+ /**## ODComponentFactory `class`
30
+ * An Open Discord component factory.
31
+ *
32
+ * It is a collection of functions/workers/hooks which will build a Discord message/modal component from scratch.
33
+ * Plugins can intercept and modify these workers to replace default behaviour or layout.
34
+ */
35
+ export class ODComponentFactory<Component extends ODComponent<object,any>,Origin extends string,Params,WorkerIds extends string = string> extends ODManagerData {
36
+ /**A collection of all workers for this component factory. */
37
+ workers: ODWorkerManager<ODComponentFactoryInstance<Component>,Origin,Params,WorkerIds>
38
+
39
+ constructor(id:ODValidId, callback?:ODWorkerCallback<ODComponentFactoryInstance<Component>,Origin,Params>, priority?:number, callbackId?:ODValidId){
40
+ super(id)
41
+ this.workers = new ODWorkerManager("ascending")
42
+ if (callback) this.workers.add(new ODWorker(callbackId ? callbackId : id,priority ?? 0,callback))
43
+ }
44
+ /**Run all workers and return the resulting component. */
45
+ async build(origin:Origin, params:Params): Promise<ODComponentInferBuildResult<Component>> {
46
+ const instance = new ODComponentFactoryInstance<Component>()
47
+ await this.workers.executeWorkers(instance,origin,params)
48
+ const rootComponent = instance.getComponent()
49
+ if (!rootComponent) throw new ODSystemError("ODComponentFactory.build() --> Failed to build component! (id: "+this.id.value+")")
50
+ return rootComponent.build()
51
+ }
52
+ }
53
+
54
+ /**## ODComponentManagerIdConstraint `type`
55
+ * The constraint/layout for id mappings/interfaces of the `ODComponentManager` class.
56
+ */
57
+ export type ODComponentManagerIdConstraint = Record<string,{origin:string,params:object,workers:string}>
58
+
59
+ /**## ODBaseComponentManager `class`
60
+ * A generic Open Discord component manager.
61
+ *
62
+ * It contains a collection of all Open Discord component factories. You can:
63
+ * - Add your own message/modal component factories
64
+ * - Modify existing message/modal component factories
65
+ *
66
+ * Messages created using this system are not compatible with `ODBuilder` messages!
67
+ */
68
+ export class ODBaseComponentManager<IdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint,Component extends ODComponent<object,any> = ODComponent<object,any>> extends ODManager<ODComponentFactory<Component,string,{},string>> {
69
+ get<FactoryId extends keyof ODNoGeneric<IdList>>(id:FactoryId): ODComponentFactory<Component,IdList[FactoryId]["origin"],IdList[FactoryId]["params"],IdList[FactoryId]["workers"]>
70
+ get(id:ODValidId): ODComponentFactory<Component,string,{},string>|null
71
+
72
+ get(id:ODValidId): ODComponentFactory<Component,string,{},string>|null {
73
+ return super.get(id)
74
+ }
75
+
76
+ remove<FactoryId extends keyof ODNoGeneric<IdList>>(id:FactoryId): ODComponentFactory<Component,IdList[FactoryId]["origin"],IdList[FactoryId]["params"],IdList[FactoryId]["workers"]>
77
+ remove(id:ODValidId): ODComponentFactory<Component,string,{},string>|null
78
+
79
+ remove(id:ODValidId): ODComponentFactory<Component,string,{},string>|null {
80
+ return super.remove(id)
81
+ }
82
+
83
+ exists(id:keyof ODNoGeneric<IdList>): boolean
84
+ exists(id:ODValidId): boolean
85
+
86
+ exists(id:ODValidId): boolean {
87
+ return super.exists(id)
88
+ }
89
+ }
90
+
91
+ /**## ODSharedComponentManager `class
92
+ * A special class with types for shared message/modal `ODComponent`'s.
93
+ * Create button, dropdown or any other layout component template to use them in messages & modals.
94
+ */
95
+ export class ODSharedComponentManager<IdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint> extends ODBaseComponentManager<IdList,ODComponent<object,any>> {
96
+ constructor(debug:ODDebugger){
97
+ super(debug,"shared component")
98
+ }
99
+ }
100
+
101
+ /**## ODMessageComponentManager `class
102
+ * A special class with types for `ODMessageComponent`'s.
103
+ * Create message templates to use as replies and make use of shared components.
104
+ */
105
+ export class ODMessageComponentManager<IdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint> extends ODBaseComponentManager<IdList,ODMessageComponent|ODSimpleMessageComponent> {
106
+ constructor(debug:ODDebugger){
107
+ super(debug,"message component")
108
+ }
109
+ }
110
+
111
+ /**## ODModalComponentManager `class
112
+ * A special class with types for `ODModalComponent`'s.
113
+ * Create modal templates to use as forms and make use of shared components.
114
+ */
115
+ export class ODModalComponentManager<IdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint> extends ODBaseComponentManager<IdList,ODModalComponent> {
116
+ constructor(debug:ODDebugger){
117
+ super(debug,"modal component")
118
+ }
119
+ }
120
+
121
+ /**## ODComponentManager `class`
122
+ * An Open Discord component manager.
123
+ *
124
+ * Create message & modal templates, re-use components and design all visual elements of the bot.
125
+ *
126
+ * Using the Open Discord component system has many advantages compared to vanilla `discord.js`:
127
+ * - Plugins can extend, edit & replace messages & components
128
+ * - Includes automatic error handling
129
+ * - Independent workers/hooks (with priority)
130
+ * - Fail-safe design using try-catch
131
+ * - Automatic switch between components v2 and legacy components depending on message requirements
132
+ * - Get to know the origin of the request (e.g. button, dropdown, modal, ...)
133
+ * - And so much more!
134
+ */
135
+ export class ODComponentManager<
136
+ SharedIdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint,
137
+ MessageIdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint,
138
+ ModalIdList extends ODComponentManagerIdConstraint = ODComponentManagerIdConstraint
139
+ > {
140
+ /**The manager for all shared components. */
141
+ shared: ODSharedComponentManager<SharedIdList>
142
+ /**The manager for all messages components. */
143
+ messages: ODMessageComponentManager<MessageIdList>
144
+ /**The manager for all modals components. */
145
+ modals: ODModalComponentManager<ModalIdList>
146
+
147
+ constructor(debug:ODDebugger){
148
+ this.shared = new ODSharedComponentManager(debug)
149
+ this.messages = new ODMessageComponentManager(debug)
150
+ this.modals = new ODModalComponentManager(debug)
151
+ }
152
+ }
153
+
154
+ ///////////////////////////////////////
155
+ // GENERIC COMPONENT DEFINITIONS //
156
+ ///////////////////////////////////////
157
+
158
+ /**## ODComponentInferBuildResult `type`
159
+ * Infer the build result of a certain component.
160
+ */
161
+ export type ODComponentInferBuildResult<Component> = Component extends ODComponent<object,infer BuildResult> ? BuildResult : never
162
+
163
+ /**## ODComponent `class`
164
+ * An Open Discord message/modal component.
165
+ *
166
+ * This class itself doesn't do anything, but is a blueprint for other
167
+ * `ODComponent` classes which represent the new Discord message/modal components.
168
+ */
169
+ export abstract class ODComponent<Data extends object,BuildResult> {
170
+ /**The id of this message/modal component. */
171
+ id: ODId
172
+ /**The data or configuration of this message/modal component. */
173
+ readonly data: Data
174
+
175
+ constructor(id:ODValidId,data:Data){
176
+ this.id = new ODId(id)
177
+ this.data = data
178
+ }
179
+
180
+ /**Build this component. Returns `null` when invalid. */
181
+ abstract build(): Promise<BuildResult|null>|BuildResult|null
182
+ }
183
+
184
+ /**## ODGroupComponent `class`
185
+ * An Open Discord message/modal component with children.
186
+ *
187
+ * This class itself doesn't do anything, but is a blueprint for other
188
+ * `ODGroupComponent` classes which represent the new Discord message/modal components.
189
+ */
190
+ export abstract class ODGroupComponent<Data extends object,ChildComponent extends ODComponent<object,any>,BuildResult> extends ODComponent<Data,BuildResult> {
191
+ /**The collection of child components. */
192
+ readonly children: ChildComponent[] = []
193
+
194
+ /**Add a new component to this group. There are multiple modes available:
195
+ * - `start`: insert at the start of the list.
196
+ * - `end`: insert at the end of the list.
197
+ * - `before`: insert before an existing component `referenceId` (`start` if `referenceId` is invalid)
198
+ * - `after`: insert after an existing component `referenceId` (`end` if `referenceId` is invalid)
199
+ * - `index`: insert at a certain index `referenceIndex` (`end` if `referenceIndex` is invalid)
200
+ */
201
+ addComponent(c:ChildComponent,mode:"start"|"end"): void
202
+ addComponent(c:ChildComponent,mode:"before"|"after",referenceId:ODValidId): void
203
+ addComponent(c:ChildComponent,mode:"index",referenceIndex:number): void
204
+ addComponent(c:ChildComponent,mode:"start"|"end"|"before"|"after"|"index" = "start",reference?:ODValidId|number){
205
+ if (mode == "start") this.children.unshift(c)
206
+ else if (mode == "end") this.children.push(c)
207
+ else if (mode == "before"){
208
+ if (!reference || !this.children.find((c) => c.id.value === new ODId(reference).value)) this.children.unshift(c)
209
+ else{
210
+ const referenceIndex = this.children.findIndex((c) => c.id.value === new ODId(reference).value)
211
+ this.children.splice(referenceIndex,0,c) //insert at position 'referenceIndex' (before)
212
+ }
213
+ }else if (mode == "after"){
214
+ if (!reference || !this.children.find((c) => c.id.value === new ODId(reference).value)) this.children.push(c)
215
+ else{
216
+ const referenceIndex = this.children.findIndex((c) => c.id.value === new ODId(reference).value)
217
+ this.children.splice(referenceIndex+1,0,c) //insert at position 'referenceIndex+1' (after)
218
+ }
219
+ }else if (mode == "index"){
220
+ if (!reference || typeof reference !== "number") this.children.push(c)
221
+ else{
222
+ this.children.splice(reference,0,c) //insert at position 'reference'
223
+ }
224
+ }
225
+ }
226
+ /**Get a component with a certain ID in this group. Returns `null` if non-existent. */
227
+ getComponent(id:ODValidId){
228
+ const component = this.children.find((c) => c.id.value === new ODId(id).value)
229
+ return component ?? null
230
+ }
231
+ /**Get the position of a component with a certain ID in this group. Returns `-1` if non-existent. */
232
+ getComponentPosition(id:ODValidId){
233
+ return this.children.findIndex((c) => c.id.value === new ODId(id).value)
234
+ }
235
+ /**Returns if a component with a certain ID exists in this group. */
236
+ existsComponent(id:ODValidId){
237
+ const component = this.children.find((c) => c.id.value === new ODId(id).value)
238
+ return component ? true : false
239
+ }
240
+ /**Remove a component with a certain ID from this group. Returns the removed component or `null if non-existent. */
241
+ removeComponent(id:ODValidId){
242
+ const index = this.children.findIndex((c) => c.id.value === new ODId(id).value)
243
+ if (index < 0) return null
244
+ else return this.children.splice(index,1)[0]
245
+ }
246
+ /**Moves an existing component to a new location in this group. There are multiple modes available:
247
+ * - `start`: move to the start of the list.
248
+ * - `end`: move to the end of the list.
249
+ * - `before`: move before an existing component `referenceId` (`start` if `referenceId` is invalid)
250
+ * - `after`: move after an existing component `referenceId` (`end` if `referenceId` is invalid)
251
+ * - `index`: move to a certain index `referenceIndex` (`end` if `referenceIndex` is invalid)
252
+ */
253
+ moveComponent(id:ODValidId,mode:"start"|"end"): void
254
+ moveComponent(id:ODValidId,mode:"before"|"after",referenceId:ODValidId): void
255
+ moveComponent(id:ODValidId,mode:"index",referenceIndex:number): void
256
+ moveComponent(id:ODValidId,mode:"start"|"end"|"before"|"after"|"index" = "start",reference?:ODValidId|number){
257
+ const component = this.removeComponent(id)
258
+ if (component){
259
+ if (mode == "start" || mode == "end") this.addComponent(component,mode)
260
+ if ((mode == "before" || mode == "after") && reference) this.addComponent(component,mode,reference)
261
+ if (mode == "index" && typeof reference == "number") this.addComponent(component,mode,reference)
262
+ return
263
+ }
264
+ }
265
+ }
266
+
267
+ /**## ODParentComponent `class`
268
+ * An Open Discord message/modal component with a single child.
269
+ *
270
+ * This class itself doesn't do anything, but is a blueprint for other
271
+ * `ODParentComponent` classes which represent the new Discord message/modal components.
272
+ */
273
+ export abstract class ODParentComponent<Data extends object,ChildComponent extends ODComponent<object,any>,BuildResult> extends ODComponent<Data,BuildResult> {
274
+ /**The child component of this parent. */
275
+ private rawChild: ChildComponent|null = null
276
+
277
+ /**The child component of this parent. */
278
+ get child(){
279
+ return this.rawChild
280
+ }
281
+ /**Set the child component of this parent. */
282
+ setComponent(c:ChildComponent|null){
283
+ this.rawChild = c
284
+ }
285
+ }
286
+
287
+ //////////////////////////////////////
288
+ // GLOBAL COMPONENT DEFINITIONS //
289
+ //////////////////////////////////////
290
+
291
+ /**## ODMessageComponentData `type`
292
+ * The configurable settings/options for the `ODMessageComponent`.
293
+ */
294
+ export interface ODMessageComponentData {
295
+ /**Should the message be sent as ephemeral? */
296
+ ephemeral?:boolean,
297
+ /**Suppress/hide embeds. */
298
+ supressEmbeds?:boolean,
299
+ /**Do not send notifications to mentioned users or roles. */
300
+ supressNotifications?:boolean
301
+ /**Additional options that aren't covered by the Open Discord api!*/
302
+ additionalOptions?:Omit<discord.MessageCreateOptions,"poll"|"content"|"embeds"|"components"|"files"|"flags"|"stickers">,
303
+ /**Add additional files which can be used in components as `attachment://...` */
304
+ additionalAttachments?:discord.AttachmentBuilder[]
305
+ }
306
+
307
+ /**## ODValidMessageComponents `type`
308
+ * A collection of all valid top-level components that can be sent in a message.
309
+ */
310
+ export type ODValidMessageComponents = ODTextComponent|ODFileComponent|ODGalleryComponent
311
+
312
+ /**## ODMessageComponentBuildResult `type`
313
+ * The constructed message from an `ODMessageComponent`.
314
+ */
315
+ export interface ODMessageComponentBuildResult {
316
+ /**The message to send. */
317
+ msg:discord.MessageCreateOptions,
318
+ /**Is this message using Components V2? */
319
+ componentsV2:boolean,
320
+ /**Should the message be sent as ephemeral? */
321
+ ephemeral:boolean,
322
+ /**Suppress/hide embeds. */
323
+ supressEmbeds:boolean,
324
+ /**Do not send notifications to mentioned users or roles. */
325
+ supressNotifications:boolean,
326
+ /**The id of the `ODMessageComponent` this message was built with. */
327
+ id:ODId
328
+ }
329
+
330
+ /**## ODMessageComponent `class`
331
+ * A message builder with **components v2** support.
332
+ * Add items to this message using `addComponent()`.
333
+ *
334
+ * Use `ODSimpleMessageComponent` for components v1, polls, embeds, etc
335
+ */
336
+ export class ODMessageComponent extends ODGroupComponent<ODMessageComponentData,ODValidMessageComponents,ODMessageComponentBuildResult> {
337
+ constructor(id:ODValidId,data?:Partial<ODMessageComponentData>){
338
+ const initData: ODMessageComponentData = {...data}
339
+ super(id,initData)
340
+ }
341
+
342
+ async build(){
343
+ if (this.children.length < 1) throw new ODSystemError("ODMessageComponent:build('"+this.id.value+"') => Requires at least one child component.")
344
+
345
+ const attachments: discord.AttachmentBuilder[] = [...(this.data.additionalAttachments ?? [])]
346
+ const components: discord.JSONEncodable<discord.APIMessageTopLevelComponent>[] = []
347
+
348
+ for (const component of this.children){
349
+ if (component instanceof ODFileComponent){
350
+ //ODFileComponent (special)
351
+ const res = await component.build()
352
+ if (res) components.push(res.file)
353
+ if (res?.attachment) attachments.push(res.attachment)
354
+
355
+ }else if (component instanceof ODGalleryComponent){
356
+ //ODGalleryComponent (special)
357
+ const res = await component.build()
358
+ if (res) components.push(res.gallery)
359
+ if (res?.attachments) attachments.push(...res.attachments)
360
+ }else{
361
+ //general ODComponent's
362
+ const res = await component.build()
363
+ if (res) components.push(res)
364
+ }
365
+ }
366
+
367
+ return {
368
+ id:new ODId(this.id),
369
+ msg:{
370
+ components,
371
+ files:attachments,
372
+ ...this.data.additionalOptions
373
+ },
374
+ componentsV2:true,
375
+ ephemeral:this.data.ephemeral ?? false,
376
+ supressEmbeds:this.data.supressEmbeds ?? false,
377
+ supressNotifications:this.data.supressNotifications ?? false
378
+ }
379
+
380
+ }
381
+
382
+ /**Enable/disable ephemeral mode. */
383
+ setEphemeral(value:boolean){
384
+ this.data.ephemeral = value
385
+ }
386
+ /**Enable supress (hide) embeds mode. */
387
+ setSupressEmbeds(value:boolean){
388
+ this.data.supressEmbeds = value
389
+ }
390
+ /**Enable supress (hide) notifications mode. */
391
+ setSupressNotifications(value:boolean){
392
+ this.data.supressNotifications = value
393
+ }
394
+ /**Add an additional attachment which can be used in components as `attachment://...` */
395
+ addAdditionalAttachments(...attachments:discord.AttachmentBuilder[]){
396
+ if (!this.data.additionalAttachments) this.data.additionalAttachments = []
397
+ this.data.additionalAttachments.push(...attachments)
398
+ }
399
+ }
400
+
401
+
402
+ /**## ODSimpleMessageComponentData `type`
403
+ * The configurable settings/options for the `ODSimpleMessageComponent`.
404
+ */
405
+ export interface ODSimpleMessageComponentData {
406
+ /**Should the message be sent as ephemeral? */
407
+ ephemeral?:boolean,
408
+ /**Suppress/hide embeds. */
409
+ supressEmbeds?:boolean,
410
+ /**Do not send notifications to mentioned users or roles. */
411
+ supressNotifications?:boolean
412
+ /**Additional options that aren't covered by the Open Discord api!*/
413
+ additionalOptions?:Omit<discord.MessageCreateOptions,"poll"|"content"|"embeds"|"components"|"files"|"flags">,
414
+ /**Add additional files which can be used in components as `attachment://...` */
415
+ additionalAttachments?:discord.AttachmentBuilder[]
416
+ }
417
+
418
+ /**## ODValidSimpleMessageComponents `type`
419
+ * A collection of all valid top-level components that can be sent in a simple message (components v1).
420
+ */
421
+ export type ODValidSimpleMessageComponents = ODContentComponent|ODEmbedComponent|ODFileComponent|ODPollComponent
422
+
423
+ /**## ODSimpleMessageComponent `class`
424
+ * A message builder with **components v1** support.
425
+ * Add items to this message using `addComponent()`.
426
+ *
427
+ * Use `ODMessageComponent` for components v2, components, containers, etc
428
+ */
429
+ export class ODSimpleMessageComponent extends ODGroupComponent<ODSimpleMessageComponentData,ODValidSimpleMessageComponents,ODMessageComponentBuildResult> {
430
+ constructor(id:ODValidId,data?:Partial<ODMessageComponentData>){
431
+ const initData: ODMessageComponentData = {...data}
432
+ super(id,initData)
433
+ }
434
+
435
+ async build(){
436
+ if (this.children.length < 1) throw new ODSystemError("ODMessageComponent:build('"+this.id.value+"') => Requires at least one child component.")
437
+
438
+ const attachments: discord.AttachmentBuilder[] = [...(this.data.additionalAttachments ?? [])]
439
+ const embeds: discord.EmbedBuilder[] = []
440
+ let content: string|undefined = undefined
441
+ let poll: discord.PollData|undefined = undefined
442
+
443
+ for (const component of this.children){
444
+ if (component instanceof ODFileComponent){
445
+ //ODFileComponent (special)
446
+ const res = await component.build()
447
+ if (res?.attachment) attachments.push(res.attachment)
448
+
449
+ }else if (component instanceof ODContentComponent){
450
+ //ODContentComponent (special)
451
+ const res = await component.build()
452
+ if (res) content = res.content
453
+
454
+ }else if (component instanceof ODEmbedComponent){
455
+ //ODEmbedComponent (special)
456
+ const res = await component.build()
457
+ if (res) embeds.push(res)
458
+
459
+ }else{
460
+ //ODPollComponent (special)
461
+ const res = await component.build()
462
+ if (res) poll = res
463
+ }
464
+ }
465
+
466
+ return {
467
+ id:new ODId(this.id),
468
+ msg:{
469
+ content,
470
+ embeds,
471
+ poll,
472
+ files:attachments,
473
+ ...this.data.additionalOptions
474
+ },
475
+ componentsV2:false,
476
+ ephemeral:this.data.ephemeral ?? false,
477
+ supressEmbeds:this.data.supressEmbeds ?? false,
478
+ supressNotifications:this.data.supressNotifications ?? false
479
+ }
480
+ }
481
+
482
+ /**Enable/disable ephemeral mode. */
483
+ setEphemeral(value:boolean){
484
+ this.data.ephemeral = value
485
+ }
486
+ /**Enable supress (hide) embeds mode. */
487
+ setSupressEmbeds(value:boolean){
488
+ this.data.supressEmbeds = value
489
+ }
490
+ /**Enable supress (hide) notifications mode. */
491
+ setSupressNotifications(value:boolean){
492
+ this.data.supressNotifications = value
493
+ }
494
+ /**Add an additional attachment which can be used in components as `attachment://...` */
495
+ addAdditionalAttachments(...attachments:discord.AttachmentBuilder[]){
496
+ if (!this.data.additionalAttachments) this.data.additionalAttachments = []
497
+ this.data.additionalAttachments.push(...attachments)
498
+ }
499
+ }
500
+
501
+
502
+ /**## ODModalComponentData `type`
503
+ * The configurable settings/options for the `ODModalComponent`.
504
+ */
505
+ export interface ODModalComponentData {
506
+ /**The title of the modal (max 45 characters). */
507
+ title:string,
508
+ /**The custom id of this modal. */
509
+ customId?:string
510
+ }
511
+
512
+ /**## ODValidModalComponents `type`
513
+ * A collection of all valid top-level components that can be sent in a message.
514
+ */
515
+ export type ODValidModalComponents = ODLabelComponent|ODTextComponent
516
+
517
+ /**## ODModalComponent `class`
518
+ * A modal builder with **components v2** support.
519
+ * Add questions, select menu's & labels to this modal using `addComponent()`.
520
+ */
521
+ export class ODModalComponent extends ODGroupComponent<ODModalComponentData,ODValidModalComponents,discord.ModalBuilder> {
522
+ constructor(id:ODValidId,data?:Partial<ODModalComponentData>){
523
+ const initData: ODModalComponentData = {title:"<empty>",...data}
524
+ super(id,initData)
525
+ }
526
+
527
+ async build(){
528
+ if (this.children.length < 1) throw new ODSystemError("ODModalComponent:build('"+this.id.value+"') => Requires at least one child component.")
529
+ if (this.children.length > 5) throw new ODSystemError("ODModalComponent:build('"+this.id.value+"') => A modal doesn't support more than 5 components.")
530
+
531
+ const components: (discord.APITextDisplayComponent|discord.APILabelComponent)[] = []
532
+
533
+ for (const component of this.children){
534
+ if (component instanceof ODLabelComponent){
535
+ //ODLabelComponent (special)
536
+ const res = await component.build()
537
+ if (res) components.push(res.toJSON())
538
+
539
+ }else{
540
+ //ODTextComponent (special)
541
+ const res = await component.build()
542
+ if (res) components.push(res.toJSON())
543
+ }
544
+ }
545
+
546
+ return new discord.ModalBuilder({
547
+ components,
548
+ title:this.data.title,
549
+ customId:this.data.customId
550
+ })
551
+ }
552
+
553
+ /**Set the title of the modal. */
554
+ setTitle(title:string){
555
+ this.data.title = title
556
+ }
557
+ /**Set the custom id of this modal. */
558
+ setCustomId(id:string|null){
559
+ this.data.customId = id ?? undefined
560
+ return this
561
+ }
562
+ }
563
+
564
+
565
+ //////////////////////////////////////
566
+ // LAYOUT COMPONENT DEFINITIONS //
567
+ //////////////////////////////////////
568
+
569
+ /**## ODActionRowComponentData `type`
570
+ * The configurable settings/options for the `ODActionRowComponent`.
571
+ */
572
+ export interface ODActionRowComponentData {
573
+ //no additional top-level data
574
+ }
575
+
576
+ /**## ODValidActionRowComponents `type`
577
+ * A collection of all valid action row components.
578
+ */
579
+ export type ODValidActionRowComponents = ODButtonComponent|ODDropdownComponent
580
+
581
+ /**## ODActionRowComponent `class`
582
+ * An actionrow component which is a container for buttons, a select menu or inputs in a message or modal.
583
+ */
584
+ export class ODActionRowComponent extends ODGroupComponent<ODActionRowComponentData,ODValidActionRowComponents,discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder>> {
585
+ constructor(id:ODValidId,data?:Partial<ODActionRowComponentData>){
586
+ const initData: ODActionRowComponentData = {...data}
587
+ super(id,initData)
588
+ }
589
+
590
+ async build(){
591
+ if (this.children.length < 1) throw new ODSystemError("ODActionRowComponent:build('"+this.id.value+"') => Requires at least one child component.")
592
+ if (this.children.length > 5) throw new ODSystemError("ODActionRowComponent:build('"+this.id.value+"') => An action row doesn't support more than 5 components.")
593
+
594
+ const components: discord.JSONEncodable<discord.APIComponentInMessageActionRow>[] = []
595
+
596
+ for (const component of this.children){
597
+ //actionrow ODComponent's
598
+ const res = await component.build()
599
+ if (res) components.push(res)
600
+ }
601
+
602
+ return new discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder>({components})
603
+ }
604
+ }
605
+
606
+ /**## ODContainerComponentData `type`
607
+ * The configurable settings/options for the `ODContainerComponent`.
608
+ */
609
+ export interface ODContainerComponentData {
610
+ /**The color of this container. */
611
+ color?:discord.ColorResolvable,
612
+ /**Mark the contents of this container as spoiler. */
613
+ spoiler:boolean
614
+ }
615
+
616
+ /**## ODValidContainerComponents `type`
617
+ * A collection of all valid container components.
618
+ */
619
+ export type ODValidContainerComponents = ODActionRowComponent|ODTextComponent|ODSectionComponent|ODGalleryComponent|ODSeparatorComponent|ODFileComponent
620
+
621
+ /**## ODContainerComponent `class`
622
+ * An embed-like container for text, titles, buttons, sections, separators and other components.
623
+ */
624
+ export class ODContainerComponent extends ODGroupComponent<ODContainerComponentData,ODValidContainerComponents,{container:discord.ContainerBuilder,attachments:discord.AttachmentBuilder[]}> {
625
+ constructor(id:ODValidId,data?:Partial<ODContainerComponentData>){
626
+ const initData: ODContainerComponentData = {spoiler:false,...data}
627
+ super(id,initData)
628
+ }
629
+
630
+ async build(){
631
+ if (this.children.length < 1) throw new ODSystemError("ODContainerComponent:build('"+this.id.value+"') => Requires at least one child component.")
632
+
633
+ const components: discord.APIComponentInContainer[] = []
634
+ const attachments: discord.AttachmentBuilder[] = []
635
+
636
+ for (const component of this.children){
637
+ if (component instanceof ODFileComponent){
638
+ //ODFileComponent (special)
639
+ const res = await component.build()
640
+ if (res) components.push(res.file.toJSON())
641
+ if (res?.attachment) attachments.push(res.attachment)
642
+
643
+ }else if (component instanceof ODGalleryComponent){
644
+ //ODGalleryComponent (special)
645
+ const res = await component.build()
646
+ if (res) components.push(res.gallery.toJSON())
647
+ if (res?.attachments) attachments.push(...res.attachments)
648
+ }else{
649
+ //general ODComponent's
650
+ const res = await component.build()
651
+ if (res) components.push(res.toJSON())
652
+ }
653
+ }
654
+
655
+ return {
656
+ container:new discord.ContainerBuilder({
657
+ components,
658
+ accent_color:this.data.color ? discord.resolveColor(this.data.color) : undefined,
659
+ spoiler:this.data.spoiler
660
+ }),
661
+ attachments
662
+ }
663
+ }
664
+
665
+ /**Set the accent color of this embed-like container. */
666
+ setColor(color:discord.ColorResolvable|null){
667
+ this.data.color = color ?? undefined
668
+ }
669
+ /**Mark the contents of this container as spoiler. */
670
+ setSpoiler(spoiler:boolean){
671
+ this.data.spoiler = spoiler
672
+ }
673
+ }
674
+
675
+
676
+ /**## ODSectionComponentData `type`
677
+ * The configurable settings/options for the `ODSectionComponent`.
678
+ */
679
+ export interface ODSectionComponentData {
680
+ /**The accessory component shown on the right side of the section. */
681
+ accessory?:ODButtonComponent|ODThumbnailComponent
682
+ }
683
+
684
+ /**## ODSectionComponent `class`
685
+ * A layout component that allows you to contextually associate content with an accessory component.
686
+ * - Components: Left
687
+ * - Accessory: Right
688
+ */
689
+ export class ODSectionComponent extends ODGroupComponent<ODSectionComponentData,ODTextComponent,discord.SectionBuilder> {
690
+ constructor(id:ODValidId,data?:Partial<ODSectionComponentData>){
691
+ const initData: ODSectionComponentData = {...data}
692
+ super(id,initData)
693
+ }
694
+
695
+ async build(){
696
+ if (this.children.length < 1) throw new ODSystemError("ODSectionComponent:build('"+this.id.value+"') => Requires at least one child component.")
697
+ if (this.children.length > 3) throw new ODSystemError("ODSectionComponent:build('"+this.id.value+"') => A maximum of 3 child components are allowed in a section.")
698
+
699
+ const components: discord.APITextDisplayComponent[] = []
700
+
701
+ for (const component of this.children){
702
+ //section ODComponent's
703
+ const res = await component.build()
704
+ if (res) components.push(res.toJSON())
705
+
706
+ }
707
+
708
+ let accessory: discord.APISectionAccessoryComponent|undefined = undefined
709
+ if (this.data.accessory){
710
+ const accessoryRes = await this.data.accessory.build()
711
+ if (accessoryRes) accessory = accessoryRes.toJSON()
712
+ }
713
+
714
+ return new discord.SectionBuilder({components,accessory})
715
+ }
716
+
717
+ /**Set the accessory component shown on the right side of the section. */
718
+ setAccessory(accessory:ODButtonComponent|ODThumbnailComponent|null){
719
+ this.data.accessory = accessory ?? undefined
720
+ }
721
+ }
722
+
723
+ /**## ODLabelComponentData `type`
724
+ * The configurable settings/options for the `ODLabelComponent`.
725
+ */
726
+ export interface ODLabelComponentData {
727
+ /**The title for the child component in the modal. */
728
+ title:string,
729
+ /**An option description for the child component in the modal. */
730
+ description?:string
731
+ }
732
+
733
+ /**## ODValidLabelComponents `type`
734
+ * A collection of all valid label components.
735
+ */
736
+ export type ODValidLabelComponents = ODShortInputComponent|ODParagraphInputComponent|ODDropdownComponent|ODRadioGroupComponent|ODCheckboxGroupComponent|ODCheckboxComponent|ODFileUploadComponent
737
+
738
+ /**## ODLabelComponent `class`
739
+ * A visual separator between components. The visibility and padding of this separator can be changed.
740
+ */
741
+ export class ODLabelComponent extends ODParentComponent<ODLabelComponentData,ODValidLabelComponents,discord.LabelBuilder> {
742
+ constructor(id:ODValidId,data:Partial<ODLabelComponentData>){
743
+ const initData: ODLabelComponentData = {title:"<empty>",...data}
744
+ super(id,initData)
745
+ }
746
+
747
+ async build(){
748
+ let component: discord.APIComponentInLabel|undefined = undefined
749
+ if (this.child){
750
+ const accessoryRes = await this.child.build()
751
+ if (accessoryRes) component = accessoryRes.toJSON()
752
+ }
753
+
754
+ return new discord.LabelBuilder({
755
+ label:this.data.title,
756
+ description:this.data.description,
757
+ component
758
+ })
759
+ }
760
+
761
+ /**Set the title of the child component in the modal. */
762
+ setTitle(title:string){
763
+ this.data.title = title
764
+ }
765
+ /**Set the description of the child component in the modal. */
766
+ setDescription(description:string|null){
767
+ this.data.description = description ?? undefined
768
+ }
769
+ }
770
+
771
+
772
+ /**## ODSeparatorComponentData `type`
773
+ * The configurable settings/options for the `ODSeparatorComponent`.
774
+ */
775
+ export interface ODSeparatorComponentData {
776
+ /**Whether a visual divider should be displayed in the component. (Default: `true`) */
777
+ divider:boolean,
778
+ /**Size of separator padding (Default: `small`) */
779
+ spacing:"small"|"large"
780
+ }
781
+
782
+ /**## ODSeparatorComponent `class`
783
+ * A visual separator between components. The visibility and padding of this separator can be changed.
784
+ */
785
+ export class ODSeparatorComponent extends ODComponent<ODSeparatorComponentData,discord.SeparatorBuilder> {
786
+ constructor(id:ODValidId,data:Partial<ODSeparatorComponentData>){
787
+ const initData: ODSeparatorComponentData = {divider:true,spacing:"small",...data}
788
+ super(id,initData)
789
+ }
790
+
791
+ async build(){
792
+ return new discord.SeparatorBuilder({
793
+ divider:this.data.divider,
794
+ spacing:(this.data.spacing == "small") ? discord.SeparatorSpacingSize.Small : discord.SeparatorSpacingSize.Large
795
+ })
796
+ }
797
+
798
+ /**Set whether a visual divider should be displayed in the component. (Default: `true`). */
799
+ setDivider(divider:boolean){
800
+ this.data.divider = divider
801
+ }
802
+ /**Set the size of separator padding (Default `small`). */
803
+ setSpacing(spacing:"small"|"large"){
804
+ this.data.spacing = spacing
805
+ }
806
+ }
807
+
808
+ ///////////////////////////////////////
809
+ // CONTENT COMPONENT DEFINITIONS //
810
+ ///////////////////////////////////////
811
+
812
+ /**## ODTextComponentData `type`
813
+ * The configurable settings/options for the `ODTextComponent`.
814
+ */
815
+ export interface ODTextComponentData {
816
+ /**The text to display. */
817
+ content:string
818
+ }
819
+
820
+ /**## ODTextComponent `class`
821
+ * A text component which renders markdown text in a message.
822
+ */
823
+ export class ODTextComponent extends ODComponent<ODTextComponentData,discord.TextDisplayBuilder> {
824
+ constructor(id:ODValidId,data:Partial<ODTextComponentData>){
825
+ const initData: ODTextComponentData = {content:"",...data}
826
+ super(id,initData)
827
+ }
828
+
829
+ async build(){
830
+ if (this.data.content.length < 1) throw new ODSystemError("ODTextComponent:build('"+this.id.value+"') => Unable to display text component without contents.")
831
+ return new discord.TextDisplayBuilder({
832
+ content:this.data.content
833
+ })
834
+ }
835
+
836
+ /**Set the text to display. */
837
+ setContent(value:string){
838
+ this.data.content = value
839
+ }
840
+ }
841
+
842
+ /**## ODFileComponentData `type`
843
+ * The configurable settings/options for the `ODFileComponent`.
844
+ */
845
+ export interface ODFileComponentData {
846
+ /**The name of this file. */
847
+ name:string,
848
+ /**The description of this file. */
849
+ description?:string,
850
+ /**Should this file be marked as a spoiler? */
851
+ spoiler?:boolean,
852
+ /**The binary/text contents of the file. Ignored when `externalUrl` is used. */
853
+ content?:discord.BufferResolvable,
854
+ /**A URL to an external file or image. When specified, the `content` (setContent) will be ignored. */
855
+ externalUrl?:string
856
+ }
857
+
858
+ /**## ODFileComponent `class`
859
+ * A file component which adds a file in a message.
860
+ */
861
+ export class ODFileComponent extends ODComponent<ODFileComponentData,{file:discord.FileBuilder,attachment:discord.AttachmentBuilder|null}> {
862
+ constructor(id:ODValidId,data:Partial<ODFileComponentData>){
863
+ const initData: ODFileComponentData = {name:"file.txt",...data}
864
+ super(id,initData)
865
+ }
866
+
867
+ async build(){
868
+ if (!this.data.content && !this.data.externalUrl) throw new ODSystemError("ODFileComponent:build('"+this.id.value+"') => Unable to display file component without binary or url.")
869
+
870
+ const attachment = (this.data.content) ? new discord.AttachmentBuilder(this.data.content,{
871
+ name:this.data.name,
872
+ description:this.data.description
873
+ }) : null
874
+
875
+ return {
876
+ attachment,
877
+ file:new discord.FileBuilder({
878
+ file:{
879
+ url:(this.data.externalUrl ?? "attachment://"+this.data.name)
880
+ },
881
+ spoiler:this.data.spoiler
882
+ })
883
+ }
884
+ }
885
+
886
+ /**Set the filename + extension. */
887
+ setName(value:string){
888
+ this.data.name = value
889
+ }
890
+ /**Set the file description. */
891
+ setDescription(value:string|null){
892
+ this.data.description = value ?? undefined
893
+ }
894
+ /**Set the text/binary contents of the file. */
895
+ setContent(value:discord.BufferResolvable|null){
896
+ this.data.content = value ?? undefined
897
+ }
898
+ /**Set a URL to an external file or image. When used, `setContent()` will be ignored! */
899
+ setExternalUrl(value:string|null){
900
+ this.data.externalUrl = value ?? undefined
901
+ }
902
+ /**Mark the file as spoiler. */
903
+ setSpoiler(value:boolean){
904
+ this.data.spoiler = value
905
+ }
906
+ }
907
+
908
+ /**## ODGalleryComponentData `type`
909
+ * The configurable settings/options for the `ODGalleryComponent`.
910
+ */
911
+ export interface ODGalleryComponentData {
912
+ //no additional top-level data
913
+ }
914
+
915
+ /**## ODGalleryComponent `class`
916
+ * A gallery component which renders a grid of media items (images/videos) in a message.
917
+ * Add items to this gallery using `addComponent()`.
918
+ */
919
+ export class ODGalleryComponent extends ODGroupComponent<ODGalleryComponentData,ODFileComponent,{gallery:discord.MediaGalleryBuilder,attachments:discord.AttachmentBuilder[]}> {
920
+ constructor(id:ODValidId,data?:Partial<ODGalleryComponentData>){
921
+ const initData: ODGalleryComponentData = {...data}
922
+ super(id,initData)
923
+ }
924
+
925
+ async build(){
926
+ if (this.children.length < 1) throw new ODSystemError("ODGalleryComponent:build('"+this.id.value+"') => Requires at least one child component.")
927
+
928
+ const gallery = new discord.MediaGalleryBuilder()
929
+ const attachments: discord.AttachmentBuilder[] = []
930
+
931
+ for (const file of this.children){
932
+ if (!file.data.content && !file.data.externalUrl) continue
933
+ if (file.data.content) attachments.push(new discord.AttachmentBuilder(file.data.content,{
934
+ name:file.data.name,
935
+ description:file.data.description
936
+ }))
937
+ gallery.addItems(new discord.MediaGalleryItemBuilder({
938
+ description:file.data.description,
939
+ spoiler:file.data.spoiler,
940
+ media:{
941
+ url:(file.data.externalUrl ?? "attachment://"+file.data.name)
942
+ }
943
+ }))
944
+ }
945
+
946
+ return {gallery,attachments}
947
+ }
948
+ }
949
+
950
+ /**## ODThumbnailComponentData `type`
951
+ * The configurable settings/options for the `ODThumbnailComponent`.
952
+ */
953
+ export interface ODThumbnailComponentData {
954
+ /**The URL of the thumbnail image. Can be an external URL or `attachment://filename.ext`. */
955
+ url:string,
956
+ /**The alt text/description of the thumbnail image. */
957
+ description?:string,
958
+ /**Should this thumbnail be marked as a spoiler? */
959
+ spoiler?:boolean
960
+ }
961
+
962
+ /**## ODThumbnailComponent `class`
963
+ * A thumbnail component which renders a small image as an accessory inside an `ODSectionComponent`.
964
+ * This component must be used via `ODSectionComponent.setComponent()`
965
+ */
966
+ export class ODThumbnailComponent extends ODComponent<ODThumbnailComponentData,discord.ThumbnailBuilder> {
967
+ constructor(id:ODValidId,data:Partial<ODThumbnailComponentData>){
968
+ const initData: ODThumbnailComponentData = {url:"",...data}
969
+ super(id,initData)
970
+ }
971
+
972
+ async build(){
973
+ if (this.data.url.length < 1) throw new ODSystemError("ODThumbnailComponent:build('"+this.id.value+"') => Thumbnail component requires an image URL.")
974
+
975
+ return new discord.ThumbnailBuilder({
976
+ media:{ url:this.data.url },
977
+ description:this.data.description,
978
+ spoiler:this.data.spoiler
979
+ })
980
+ }
981
+
982
+ /**Set the URL of the thumbnail image. */
983
+ setUrl(value:string){
984
+ this.data.url = value
985
+ }
986
+ /**Set the alt text/description of the thumbnail image. */
987
+ setDescription(value:string|null){
988
+ this.data.description = value ?? undefined
989
+ }
990
+ /**Mark the thumbnail as a spoiler. */
991
+ setSpoiler(value:boolean){
992
+ this.data.spoiler = value
993
+ }
994
+ }
995
+
996
+ /**## ODContentComponentData `type`
997
+ * The configurable settings/options for the `ODContentComponent`.
998
+ */
999
+ export interface ODContentComponentData {
1000
+ /**The text to display. */
1001
+ content:string
1002
+ }
1003
+
1004
+ /**## ODContentComponent `class`
1005
+ * A text component which renders markdown text in a message without `Components V2` enabled. (Old behaviour)
1006
+ */
1007
+ export class ODContentComponent extends ODComponent<ODContentComponentData,{content:string}> {
1008
+ constructor(id:ODValidId,data:Partial<ODContentComponentData>){
1009
+ const initData: ODContentComponentData = {content:"",...data}
1010
+ super(id,initData)
1011
+ }
1012
+
1013
+ async build(){
1014
+ if (this.data.content.length < 1) throw new ODSystemError("ODContentComponent:build('"+this.id.value+"') => Unable to display content component without contents.")
1015
+ return {
1016
+ content:this.data.content
1017
+ }
1018
+ }
1019
+
1020
+ /**Set the text to display. */
1021
+ setContent(value:string){
1022
+ this.data.content = value
1023
+ }
1024
+ }
1025
+
1026
+
1027
+ /**## ODEmbedComponentData `type`
1028
+ * The configurable settings/options for the `ODEmbedComponent`.
1029
+ */
1030
+ export interface ODEmbedComponentData {
1031
+ /**The title of the embed. */
1032
+ title?:string,
1033
+ /**The color of the embed. */
1034
+ color?:discord.ColorResolvable,
1035
+ /**The url of the embed. */
1036
+ url?:string,
1037
+ /**The description of the embed. */
1038
+ description?:string,
1039
+ /**The author text of the embed. */
1040
+ authorText?:string,
1041
+ /**The author image of the embed. */
1042
+ authorImage?:string,
1043
+ /**The author url of the embed. */
1044
+ authorUrl?:string,
1045
+ /**The footer text of the embed. */
1046
+ footerText?:string,
1047
+ /**The footer image of the embed. */
1048
+ footerImage?:string,
1049
+ /**The image of the embed. */
1050
+ image?:string,
1051
+ /**The thumbnail of the embed. */
1052
+ thumbnail?:string,
1053
+ /**The fields of the embed. */
1054
+ fields?:ODInterfaceWithPartialProperty<discord.EmbedField,"inline">[],
1055
+ /**The timestamp of the embed. */
1056
+ timestamp?:number|Date
1057
+ }
1058
+
1059
+ /**## ODEmbedComponent `class`
1060
+ * An embed component which renders an embed in a message without `Components V2` enabled. (Old behaviour)
1061
+ */
1062
+ export class ODEmbedComponent extends ODComponent<ODEmbedComponentData,discord.EmbedBuilder> {
1063
+ constructor(id:ODValidId,data:Partial<ODEmbedComponentData>){
1064
+ const initData: ODEmbedComponentData = {...data}
1065
+ super(id,initData)
1066
+ }
1067
+
1068
+ async build(){
1069
+ if (this.data.title && this.data.title.length > 256) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => An embed title can't exceed 256 characters.")
1070
+ if (this.data.description && this.data.description.length > 4096) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => An embed description can't exceed 4096 characters.")
1071
+ if (this.data.fields && this.data.fields.length > 25) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => An embed can't contain more than 25 fields.")
1072
+ if (this.data.footerText && this.data.footerText.length > 2048) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => An embed footer can't exceed 2048 characters.")
1073
+ if (this.data.authorText && this.data.authorText.length > 256) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => An embed author can't exceed 256 characters.")
1074
+
1075
+ return new discord.EmbedBuilder({
1076
+ title:this.data.title,
1077
+ color:(this.data.color) ? discord.resolveColor(this.data.color) : undefined,
1078
+ url:this.data.url,
1079
+ description:this.data.description,
1080
+ author:(this.data.authorText) ? {
1081
+ name:this.data.authorText,
1082
+ icon_url:this.data.authorImage,
1083
+ url:this.data.authorUrl
1084
+ } : undefined,
1085
+ footer:(this.data.footerText) ? {
1086
+ text:this.data.footerText,
1087
+ icon_url:this.data.footerImage
1088
+ } : undefined,
1089
+ image:(this.data.image) ? {
1090
+ url:this.data.image
1091
+ } : undefined,
1092
+ thumbnail:(this.data.thumbnail) ? {
1093
+ url:this.data.thumbnail
1094
+ } : undefined,
1095
+ fields:this.data.fields,
1096
+ timestamp:(this.data.timestamp) ? new Date(this.data.timestamp).toISOString() : undefined
1097
+ })
1098
+ }
1099
+
1100
+ /**Set the title of this embed */
1101
+ setTitle(title:string|null){
1102
+ this.data.title = title ?? undefined
1103
+ return this
1104
+ }
1105
+ /**Set the color of this embed */
1106
+ setColor(color:discord.ColorResolvable|null){
1107
+ this.data.color = color ?? undefined
1108
+ return this
1109
+ }
1110
+ /**Set the url of this embed */
1111
+ setUrl(url:string|null){
1112
+ this.data.url = url ?? undefined
1113
+ return this
1114
+ }
1115
+ /**Set the description of this embed */
1116
+ setDescription(description:string|null){
1117
+ this.data.description = description ?? undefined
1118
+ return this
1119
+ }
1120
+ /**Set the author of this embed */
1121
+ setAuthor(text:string|null, image?:string|null, url?:string|null){
1122
+ this.data.authorText = text ?? undefined
1123
+ this.data.authorImage = image ?? undefined
1124
+ this.data.authorUrl = url ?? undefined
1125
+ return this
1126
+ }
1127
+ /**Set the footer of this embed */
1128
+ setFooter(text:string|null, image?:string|null){
1129
+ this.data.footerText = text ?? undefined
1130
+ this.data.footerImage = image ?? undefined
1131
+ return this
1132
+ }
1133
+ /**Set the image of this embed */
1134
+ setImage(image:string|null){
1135
+ this.data.image = image ?? undefined
1136
+ return this
1137
+ }
1138
+ /**Set the thumbnail of this embed */
1139
+ setThumbnail(thumbnail:string|null){
1140
+ this.data.thumbnail = thumbnail ?? undefined
1141
+ return this
1142
+ }
1143
+ /**Set the fields of this embed */
1144
+ setFields(fields:ODInterfaceWithPartialProperty<discord.EmbedField,"inline">[]){
1145
+ //Check field properties
1146
+ for (const [index,field] of fields.entries()){
1147
+ if (field.value.length >= 1024) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => field "+index+" reached 1024 character limit!")
1148
+ if (field.name.length >= 256) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => field "+index+" reached 256 name character limit!")
1149
+ }
1150
+ this.data.fields = fields
1151
+ return this
1152
+ }
1153
+ /**Add fields to this embed */
1154
+ addFields(...fields:ODInterfaceWithPartialProperty<discord.EmbedField,"inline">[]){
1155
+ //Check field properties
1156
+ for (const [index,field] of fields.entries()){
1157
+ if (field.value.length >= 1024) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => field "+index+" reached 1024 character limit!")
1158
+ if (field.name.length >= 256) throw new ODSystemError("ODEmbedComponent:build('"+this.id.value+"') => field "+index+" reached 256 name character limit!")
1159
+ }
1160
+
1161
+ if (!this.data.fields) this.data.fields = []
1162
+ this.data.fields.push(...fields)
1163
+ return this
1164
+ }
1165
+ /**Clear all fields from this embed */
1166
+ clearFields(){
1167
+ this.data.fields = []
1168
+ return this
1169
+ }
1170
+ /**Set the timestamp of this embed. When set to `true`, the current timestamp is used. */
1171
+ setTimestamp(timestamp:number|Date|true|null){
1172
+ if (timestamp === true) this.data.timestamp = new Date()
1173
+ else this.data.timestamp = timestamp ?? undefined
1174
+ return this
1175
+ }
1176
+ }
1177
+
1178
+ /**## ODPollComponentData `type`
1179
+ * The configurable settings/options for the `ODPollComponent`.
1180
+ */
1181
+ export interface ODPollComponentData {
1182
+ /**The poll question. */
1183
+ question:string,
1184
+ /**The duration of the poll in hours. */
1185
+ durationHours:number,
1186
+ /**Available poll answers. */
1187
+ answers:discord.PollAnswerData[],
1188
+ /**Allow selecting multiple answers. */
1189
+ allowMultiSelect:boolean
1190
+ }
1191
+
1192
+ /**## ODPollComponent `class`
1193
+ * A poll component which adds a poll to a message without `Components V2` enabled. (Old behaviour)
1194
+ */
1195
+ export class ODPollComponent extends ODComponent<ODPollComponentData,discord.PollData> {
1196
+ constructor(id:ODValidId,data:Partial<ODPollComponentData>){
1197
+ const initData: ODPollComponentData = {question:"<empty>",durationHours:1,allowMultiSelect:false,answers:[],...data}
1198
+ super(id,initData)
1199
+ }
1200
+
1201
+ async build(){
1202
+ if (this.data.question.length < 1) throw new ODSystemError("ODPollComponent:build('"+this.id.value+"') => Please provide a valid poll question.")
1203
+ if (this.data.answers.length < 1) throw new ODSystemError("ODPollComponent:build('"+this.id.value+"') => Please provide at least one answer to the poll.")
1204
+ return {
1205
+ layoutType:discord.PollLayoutType.Default,
1206
+ allowMultiselect:this.data.allowMultiSelect,
1207
+ duration:this.data.durationHours,
1208
+ question:{text:this.data.question},
1209
+ answers:this.data.answers
1210
+ }
1211
+ }
1212
+
1213
+ /**Set the poll question. */
1214
+ setQuestion(question:string){
1215
+ this.data.question = question
1216
+ }
1217
+ /**Set the poll duration in hours. */
1218
+ setDurationHours(duration:number){
1219
+ this.data.durationHours = duration
1220
+ }
1221
+ /**Allow selecting multiple answers. */
1222
+ setMultiSelect(multi:boolean){
1223
+ this.data.allowMultiSelect = multi
1224
+ }
1225
+ /**Set the poll answers. */
1226
+ setAnswers(answers:discord.PollAnswerData[]){
1227
+ this.data.answers = answers
1228
+ }
1229
+ /**Add additional poll answers. */
1230
+ addAnswers(...answers:discord.PollAnswerData[]){
1231
+ this.data.answers.push(...answers)
1232
+ }
1233
+ }
1234
+
1235
+ ///////////////////////////////////////////
1236
+ // INTERACTIVE COMPONENT DEFINITIONS //
1237
+ ///////////////////////////////////////////
1238
+
1239
+
1240
+ /**## ODButtonComponentData `type`
1241
+ * The configurable settings/options for the `ODButtonComponent`.
1242
+ */
1243
+ export interface ODButtonComponentData {
1244
+ /**The custom id of this button. Ignored when `url` is specified. */
1245
+ customId?:string,
1246
+ /**The url of this button. Disables interactions & customId */
1247
+ url?:string,
1248
+ /**The button color. Ignored when `url` is specified. */
1249
+ color:ODValidButtonColor,
1250
+ /**The button label */
1251
+ label?:string,
1252
+ /**The button emoji */
1253
+ emoji?:string,
1254
+ /**Is the button disabled? */
1255
+ disabled:boolean
1256
+ }
1257
+
1258
+ /**## ODButtonComponent `class`
1259
+ * A button component which renders an interactive button inside the message.
1260
+ * A reply can be sent using Open Discord responders.
1261
+ */
1262
+ export class ODButtonComponent extends ODComponent<ODButtonComponentData,discord.ButtonBuilder> {
1263
+ constructor(id:ODValidId,data:Partial<ODButtonComponentData>){
1264
+ const initData: ODButtonComponentData = {color:"gray",disabled:false,...data}
1265
+ super(id,initData)
1266
+ }
1267
+
1268
+ async build(){
1269
+ if (!this.data.emoji && !this.data.label) throw new ODSystemError("ODButtonComponent:build('"+this.id.value+"') => A button must include at least one label or emoji.")
1270
+ if (this.data.customId && this.data.customId.length > 100) throw new ODSystemError("ODButtonComponent:build('"+this.id.value+"') => A custom ID '"+this.data.customId+"' must be shorter than 100 characters.")
1271
+
1272
+ return new discord.ButtonBuilder({
1273
+ customId:(!this.data.url) ? this.data.customId : undefined,
1274
+ label:this.data.label,
1275
+ emoji:this.data.emoji ? discord.resolvePartialEmoji(this.data.emoji) : undefined,
1276
+ disabled:this.data.disabled,
1277
+ url:(this.data.url) ? this.data.url : undefined,
1278
+ style:this.getButtonStyle(),
1279
+ })
1280
+ }
1281
+
1282
+ /**Get the `discord.ButtonStyle` for the specified `ODValidButtonColor` */
1283
+ protected getButtonStyle(): discord.ButtonStyle {
1284
+ if (this.data.url) return discord.ButtonStyle.Link
1285
+ else if (this.data.color == "blue") return discord.ButtonStyle.Primary
1286
+ else if (this.data.color == "green") return discord.ButtonStyle.Success
1287
+ else if (this.data.color == "red") return discord.ButtonStyle.Danger
1288
+ else return discord.ButtonStyle.Secondary
1289
+ }
1290
+
1291
+ /**Set the custom id of this button. Ignored when `setUrl()` is used. */
1292
+ setCustomId(id:string|null){
1293
+ this.data.customId = id ?? undefined
1294
+ return this
1295
+ }
1296
+ /**Set the url of this button. */
1297
+ setUrl(url:string|null){
1298
+ this.data.url = url ?? undefined
1299
+ return this
1300
+ }
1301
+ /**Set the color of this button. Ignored when `setUrl()` is used. */
1302
+ setColor(color:ODValidButtonColor){
1303
+ this.data.color = color
1304
+ return this
1305
+ }
1306
+ /**Set the label of this button. */
1307
+ setLabel(label:string|null){
1308
+ this.data.label = label ?? undefined
1309
+ return this
1310
+ }
1311
+ /**Set the emoji of this button. */
1312
+ setEmoji(emoji:string|null){
1313
+ this.data.emoji = emoji ?? undefined
1314
+ return this
1315
+ }
1316
+ /**Disable this button. */
1317
+ setDisabled(disabled:boolean){
1318
+ this.data.disabled = disabled
1319
+ return this
1320
+ }
1321
+ }
1322
+
1323
+
1324
+ /**## ODShortInputComponentData `type`
1325
+ * The configurable settings/options for the `ODShortInputComponent`.
1326
+ */
1327
+ export interface ODShortInputComponentData {
1328
+ /**The custom id of this modal text input. */
1329
+ customId?:string
1330
+ /**The min length of this modal text input. */
1331
+ minLength?:number,
1332
+ /**The max length of this modal text input. */
1333
+ maxLength?:number,
1334
+ /**Is this modal text input required? */
1335
+ required:boolean,
1336
+ /**The placeholder of this modal text input. */
1337
+ placeholder?:string,
1338
+ /**The initial value of this modal text input. */
1339
+ initialValue?:string
1340
+ }
1341
+
1342
+ /**## ODShortInputComponent `class`
1343
+ * A short text input component for modals.
1344
+ * It must be placed inside an `ODLabelComponent`.
1345
+ */
1346
+ export class ODShortInputComponent extends ODComponent<ODShortInputComponentData,discord.TextInputBuilder> {
1347
+ constructor(id:ODValidId,data:Partial<ODShortInputComponentData>){
1348
+ const initData: ODShortInputComponentData = {required:false,...data}
1349
+ super(id,initData)
1350
+ }
1351
+
1352
+ async build(){
1353
+ return new discord.TextInputBuilder({
1354
+ style:discord.TextInputStyle.Short,
1355
+ customId:this.data.customId,
1356
+ minLength:this.data.minLength,
1357
+ maxLength:this.data.maxLength,
1358
+ required:this.data.required,
1359
+ placeholder:this.data.placeholder,
1360
+ value:this.data.initialValue
1361
+ })
1362
+ }
1363
+
1364
+ /**Set the custom id of this modal text input. */
1365
+ setCustomId(id:string|null){
1366
+ this.data.customId = id ?? undefined
1367
+ return this
1368
+ }
1369
+ /**Set the minimum amount of characters of this modal text input. */
1370
+ setMinLength(length:number|null){
1371
+ this.data.minLength = length ?? undefined
1372
+ return this
1373
+ }
1374
+ /**Set the maximum amount of characters of this modal text input. */
1375
+ setMaxLength(length:number|null){
1376
+ this.data.maxLength = length ?? undefined
1377
+ return this
1378
+ }
1379
+ /**Set the placeholder of this modal text input. */
1380
+ setPlaceholder(placeholder:string|null){
1381
+ this.data.placeholder = placeholder ?? undefined
1382
+ return this
1383
+ }
1384
+ /**Set the initial value of this modal text input. */
1385
+ setInitialValue(initialValue:string|null){
1386
+ this.data.initialValue = initialValue ?? undefined
1387
+ return this
1388
+ }
1389
+ /**Mark this modal text input as required. */
1390
+ setRequired(required:boolean){
1391
+ this.data.required = required
1392
+ return this
1393
+ }
1394
+ }
1395
+
1396
+ /**## ODParagraphInputComponentData `type`
1397
+ * The configurable settings/options for the `ODParagraphInputComponent`.
1398
+ */
1399
+ export interface ODParagraphInputComponentData {
1400
+ /**The custom id of this modal text input. */
1401
+ customId?:string
1402
+ /**The min length of this modal text input. */
1403
+ minLength?:number,
1404
+ /**The max length of this modal text input. */
1405
+ maxLength?:number,
1406
+ /**Is this modal text input required? */
1407
+ required:boolean,
1408
+ /**The placeholder of this modal text input. */
1409
+ placeholder?:string,
1410
+ /**The initial value of this modal text input. */
1411
+ initialValue?:string
1412
+ }
1413
+
1414
+ /**## ODParagraphInputComponent `class`
1415
+ * A paragraph text input component for modals.
1416
+ * It must be placed inside an `ODLabelComponent`.
1417
+ */
1418
+ export class ODParagraphInputComponent extends ODComponent<ODParagraphInputComponentData,discord.TextInputBuilder> {
1419
+ constructor(id:ODValidId,data:Partial<ODParagraphInputComponentData>){
1420
+ const initData: ODParagraphInputComponentData = {required:false,...data}
1421
+ super(id,initData)
1422
+ }
1423
+
1424
+ async build(){
1425
+ return new discord.TextInputBuilder({
1426
+ style:discord.TextInputStyle.Paragraph,
1427
+ customId:this.data.customId,
1428
+ minLength:this.data.minLength,
1429
+ maxLength:this.data.maxLength,
1430
+ required:this.data.required,
1431
+ placeholder:this.data.placeholder,
1432
+ value:this.data.initialValue
1433
+ })
1434
+ }
1435
+
1436
+ /**Set the custom id of this modal text input. */
1437
+ setCustomId(id:string|null){
1438
+ this.data.customId = id ?? undefined
1439
+ return this
1440
+ }
1441
+ /**Set the minimum amount of characters of this modal text input. */
1442
+ setMinLength(length:number|null){
1443
+ this.data.minLength = length ?? undefined
1444
+ return this
1445
+ }
1446
+ /**Set the maximum amount of characters of this modal text input. */
1447
+ setMaxLength(length:number|null){
1448
+ this.data.maxLength = length ?? undefined
1449
+ return this
1450
+ }
1451
+ /**Set the placeholder of this modal text input. */
1452
+ setPlaceholder(placeholder:string|null){
1453
+ this.data.placeholder = placeholder ?? undefined
1454
+ return this
1455
+ }
1456
+ /**Set the initial value of this modal text input. */
1457
+ setInitialValue(initialValue:string|null){
1458
+ this.data.initialValue = initialValue ?? undefined
1459
+ return this
1460
+ }
1461
+ /**Mark this modal text input as required. */
1462
+ setRequired(required:boolean){
1463
+ this.data.required = required
1464
+ return this
1465
+ }
1466
+ }
1467
+
1468
+
1469
+ /**## ODDropdownComponentData `type`
1470
+ * The configurable settings/options for the `ODDropdownComponent`.
1471
+ */
1472
+ export interface ODDropdownComponentData {
1473
+ /**The type of this dropdown. */
1474
+ type:"string"|"role"|"channel"|"user"|"mentionable",
1475
+ /**The custom id of this dropdown. */
1476
+ customId?:string,
1477
+ /**The placeholder of this dropdown. */
1478
+ placeholder?:string,
1479
+ /**The minimum amount of items to be selected in this dropdown. */
1480
+ minValues?:number,
1481
+ /**The maximum amount of items to be selected in this dropdown. */
1482
+ maxValues?:number,
1483
+ /**Is this dropdown disabled? */
1484
+ disabled?:boolean,
1485
+
1486
+ /**Allowed channel types when the type is "channel" */
1487
+ channelTypes?:discord.ChannelType[]
1488
+ /**The options when the type is "string" */
1489
+ options?:discord.SelectMenuComponentOptionData[],
1490
+ /**The options when the type is "user" */
1491
+ users?:discord.User[],
1492
+ /**The options when the type is "role" */
1493
+ roles?:discord.Role[],
1494
+ /**The options when the type is "channel" */
1495
+ channels?:discord.Channel[],
1496
+ /**The options when the type is "mentionable" */
1497
+ mentionables?:(discord.User|discord.Role)[],
1498
+ }
1499
+
1500
+ /**## ODDropdownComponent `class`
1501
+ * A dropdown component which renders an interactive dropdown inside the message/modal.
1502
+ * A reply can be sent using Open Discord responders.
1503
+ */
1504
+ export class ODDropdownComponent extends ODComponent<ODDropdownComponentData,discord.BaseSelectMenuBuilder<discord.APISelectMenuComponent>> {
1505
+ constructor(id:ODValidId,data:Partial<ODDropdownComponentData>){
1506
+ const initData: ODDropdownComponentData = {type:"string",...data}
1507
+ super(id,initData)
1508
+ }
1509
+
1510
+ async build(){
1511
+ const genericOpts = {
1512
+ customId:this.data.customId,
1513
+ disabled:this.data.disabled,
1514
+ placeholder:this.data.placeholder,
1515
+ minValues:this.data.minValues,
1516
+ maxValues:this.data.maxValues,
1517
+ }
1518
+
1519
+ if (this.data.type == "string"){
1520
+ if (!this.data.options || this.data.options.length < 1) throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please provide at least one string option using setOptions().")
1521
+ return new discord.StringSelectMenuBuilder({
1522
+ ...genericOpts,
1523
+ options:this.data.options
1524
+ })
1525
+ }else if (this.data.type == "user"){
1526
+ if (!this.data.users || this.data.users.length < 1) throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please provide at least one user option using setUsers().")
1527
+ return new discord.UserSelectMenuBuilder({
1528
+ ...genericOpts,
1529
+ defaultValues:this.data.users.map((u) => ({id:u.id,type:discord.SelectMenuDefaultValueType.User}))
1530
+ })
1531
+ }else if (this.data.type == "role"){
1532
+ if (!this.data.roles || this.data.roles.length < 1) throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please provide at least one role option using setRoles().")
1533
+ return new discord.RoleSelectMenuBuilder({
1534
+ ...genericOpts,
1535
+ defaultValues:this.data.roles.map((r) => ({id:r.id,type:discord.SelectMenuDefaultValueType.Role}))
1536
+ })
1537
+ }else if (this.data.type == "channel"){
1538
+ if (!this.data.channels || this.data.channels.length < 1) throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please provide at least one channel option using setChannels().")
1539
+ return new discord.ChannelSelectMenuBuilder({
1540
+ ...genericOpts,
1541
+ channelTypes:this.data.channelTypes,
1542
+ defaultValues:this.data.channels.map((c) => ({id:c.id,type:discord.SelectMenuDefaultValueType.Channel}))
1543
+ })
1544
+ }else if (this.data.type == "mentionable"){
1545
+ if (!this.data.mentionables || this.data.mentionables.length < 1) throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please provide at least one role/user option using setMentionables().")
1546
+ return new discord.MentionableSelectMenuBuilder({
1547
+ ...genericOpts,
1548
+ defaultValues:this.data.mentionables.map((m) => (m instanceof discord.User ? {id:m.id,type:discord.SelectMenuDefaultValueType.User} : {id:m.id,type:discord.SelectMenuDefaultValueType.Role}))
1549
+ })
1550
+ }else throw new ODSystemError("ODDropdownComponent:build('"+this.id.value+"') => Please set the dropdown type to one of the following: string, user, role, channel, mentionable.")
1551
+ }
1552
+
1553
+ /**Set the custom id of this dropdown. */
1554
+ setCustomId(id:string|null){
1555
+ this.data.customId = id ?? undefined
1556
+ return this
1557
+ }
1558
+ /**Set the type of this dropdown. */
1559
+ setType(type:"string"|"role"|"channel"|"user"|"mentionable"){
1560
+ this.data.type = type
1561
+ return this
1562
+ }
1563
+ /**Set the minimum selection amount of this dropdown. */
1564
+ setMinValues(amount:number|null){
1565
+ this.data.minValues = amount ?? undefined
1566
+ return this
1567
+ }
1568
+ /**Set the maximum selection amount of this dropdown. */
1569
+ setMaxValues(amount:number|null){
1570
+ this.data.maxValues = amount ?? undefined
1571
+ return this
1572
+ }
1573
+ /**Set the placeholder of this dropdown. */
1574
+ setPlaceholder(placeholder:string|null){
1575
+ this.data.placeholder = placeholder ?? undefined
1576
+ return this
1577
+ }
1578
+ /**Disable this dropdown. */
1579
+ setDisabled(disabled:boolean){
1580
+ this.data.disabled = disabled
1581
+ return this
1582
+ }
1583
+ /**Set the available channel types of this dropdown. */
1584
+ setChannelTypes(channelTypes:discord.ChannelType[]){
1585
+ this.data.channelTypes = channelTypes
1586
+ return this
1587
+ }
1588
+ /**Set the options of this dropdown (when `type == "string"`) */
1589
+ setOptions(options:discord.SelectMenuComponentOptionData[]){
1590
+ this.data.options = options
1591
+ return this
1592
+ }
1593
+ /**Set the users of this dropdown (when `type == "user"`) */
1594
+ setUsers(users:discord.User[]){
1595
+ this.data.users = users
1596
+ return this
1597
+ }
1598
+ /**Set the roles of this dropdown (when `type == "role"`) */
1599
+ setRoles(roles:discord.Role[]){
1600
+ this.data.roles = roles
1601
+ return this
1602
+ }
1603
+ /**Set the channels of this dropdown (when `type == "channel"`) */
1604
+ setChannels(channels:discord.Channel[]){
1605
+ this.data.channels = channels
1606
+ return this
1607
+ }
1608
+ /**Set the mentionables of this dropdown (when `type == "mentionable"`) */
1609
+ setMentionables(mentionables:(discord.User|discord.Role)[]){
1610
+ this.data.mentionables = mentionables
1611
+ return this
1612
+ }
1613
+ }
1614
+
1615
+
1616
+ /**## ODRadioGroupComponentData `type`
1617
+ * The configurable settings/options for the `ODRadioGroupComponent`.
1618
+ */
1619
+ export interface ODRadioGroupComponentData {
1620
+ /**The custom id of this radio group. */
1621
+ customId?:string,
1622
+ /**Is this radio group required? (At least one option must be selected) */
1623
+ required:boolean,
1624
+ /**The available radio options. (min 2, max 10) */
1625
+ options:discord.APIRadioGroupOption[]
1626
+ }
1627
+
1628
+ /**## ODRadioGroupComponent `class`
1629
+ * A radio group component which renders an interactive radio group input inside a modal.
1630
+ */
1631
+ export class ODRadioGroupComponent extends ODComponent<ODRadioGroupComponentData,discord.RadioGroupBuilder> {
1632
+ constructor(id:ODValidId,data:Partial<ODRadioGroupComponentData>){
1633
+ const initData: ODRadioGroupComponentData = {required:false,options:[],...data}
1634
+ super(id,initData)
1635
+ }
1636
+
1637
+ async build(){
1638
+ if (!this.data.options || this.data.options.length < 2) throw new ODSystemError("ODRadioGroupComponent:build('"+this.id.value+"') => Please provide at least 2 radio options using setOptions().")
1639
+
1640
+ return new discord.RadioGroupBuilder({
1641
+ custom_id:this.data.customId,
1642
+ required:this.data.required,
1643
+ options:this.data.options
1644
+ })
1645
+ }
1646
+
1647
+ /**Set the custom id of this radio group. */
1648
+ setCustomId(id:string|null){
1649
+ this.data.customId = id ?? undefined
1650
+ return this
1651
+ }
1652
+ /**Mark this radio group as required (At least one option must be selected). */
1653
+ setRequired(required:boolean){
1654
+ this.data.required = required
1655
+ return this
1656
+ }
1657
+ /**Set the available radio options (min 2, max 10) */
1658
+ setOptions(options:discord.APIRadioGroupOption[]){
1659
+ this.data.options = options
1660
+ return this
1661
+ }
1662
+ }
1663
+
1664
+
1665
+ /**## ODCheckboxGroupComponentData `type`
1666
+ * The configurable settings/options for the `ODCheckboxGroupComponent`.
1667
+ */
1668
+ export interface ODCheckboxGroupComponentData {
1669
+ /**The custom id of this checkbox group. */
1670
+ customId?:string,
1671
+ /**Is this checkbox group required? (At least one option must be selected) */
1672
+ required:boolean,
1673
+ /**The available checkbox options. (min 1, max 10) */
1674
+ options:discord.APICheckboxGroupOption[],
1675
+ /**The minimum amount of checkboxes to be selected. */
1676
+ minValues?:number,
1677
+ /**The maximum amount of checkboxes to be selected. */
1678
+ maxValues?:number
1679
+ }
1680
+
1681
+ /**## ODCheckboxGroupComponent `class`
1682
+ * A checkbox group component which renders an interactive checkbox group input inside a modal.
1683
+ */
1684
+ export class ODCheckboxGroupComponent extends ODComponent<ODCheckboxGroupComponentData,discord.CheckboxGroupBuilder> {
1685
+ constructor(id:ODValidId,data:Partial<ODCheckboxGroupComponentData>){
1686
+ const initData: ODCheckboxGroupComponentData = {required:false,options:[],...data}
1687
+ super(id,initData)
1688
+ }
1689
+
1690
+ async build(){
1691
+ if (!this.data.options || this.data.options.length < 2) throw new ODSystemError("ODCheckboxGroupComponent:build('"+this.id.value+"') => Please provide at least 2 radio options using setOptions().")
1692
+
1693
+ return new discord.CheckboxGroupBuilder({
1694
+ custom_id:this.data.customId,
1695
+ required:this.data.required,
1696
+ options:this.data.options,
1697
+ min_values:this.data.minValues,
1698
+ max_values:this.data.maxValues
1699
+ })
1700
+ }
1701
+
1702
+ /**Set the custom id of this checkbox group. */
1703
+ setCustomId(id:string|null){
1704
+ this.data.customId = id ?? undefined
1705
+ return this
1706
+ }
1707
+ /**Mark this checkbox group as required (At least one option must be selected). */
1708
+ setRequired(required:boolean){
1709
+ this.data.required = required
1710
+ return this
1711
+ }
1712
+ /**Set the available checkbox options (min 2, max 10) */
1713
+ setOptions(options:discord.APICheckboxGroupOption[]){
1714
+ this.data.options = options
1715
+ return this
1716
+ }
1717
+ /**Set the minimum amount of selected checkboxes. */
1718
+ setMinValues(amount:number|null){
1719
+ this.data.minValues = amount ?? undefined
1720
+ return this
1721
+ }
1722
+ /**Set the maximum amount of selected checkboxes. */
1723
+ setMaxValues(amount:number|null){
1724
+ this.data.maxValues = amount ?? undefined
1725
+ return this
1726
+ }
1727
+ }
1728
+
1729
+
1730
+ /**## ODCheckboxComponentData `type`
1731
+ * The configurable settings/options for the `ODCheckboxComponent`.
1732
+ */
1733
+ export interface ODCheckboxComponentData {
1734
+ /**The custom id of this checkbox. */
1735
+ customId?:string,
1736
+ /**Is this checkbox enabled by default? */
1737
+ default:boolean,
1738
+ }
1739
+
1740
+ /**## ODCheckboxComponent `class`
1741
+ * A checkbox component which renders an interactive checkbox input inside a modal.
1742
+ * The label & description should be set via an `ODLabelComponent`
1743
+ */
1744
+ export class ODCheckboxComponent extends ODComponent<ODCheckboxComponentData,discord.CheckboxBuilder> {
1745
+ constructor(id:ODValidId,data:Partial<ODCheckboxComponentData>){
1746
+ const initData: ODCheckboxComponentData = {default:false,...data}
1747
+ super(id,initData)
1748
+ }
1749
+
1750
+ async build(){
1751
+ return new discord.CheckboxBuilder({
1752
+ custom_id:this.data.customId,
1753
+ default:this.data.default
1754
+ })
1755
+ }
1756
+
1757
+ /**Set the custom id of this checkbox. */
1758
+ setCustomId(id:string|null){
1759
+ this.data.customId = id ?? undefined
1760
+ return this
1761
+ }
1762
+ /**Mark this checkbox as enabled by default. */
1763
+ setDefault(enabledByDefault:boolean){
1764
+ this.data.default = enabledByDefault
1765
+ return this
1766
+ }
1767
+ }
1768
+
1769
+
1770
+ /**## ODFileUploadComponentData `type`
1771
+ * The configurable settings/options for the `ODFileUploadComponent`.
1772
+ */
1773
+ export interface ODFileUploadComponentData {
1774
+ /**The custom id of this file upload. */
1775
+ customId?:string,
1776
+ /**Is this file upload required? */
1777
+ required:boolean,
1778
+ /**The minimum amount of files to upload (0-10). */
1779
+ minAmount?:number,
1780
+ /**The maximum amount of files to upload (1-10). */
1781
+ maxAmount?:number
1782
+ }
1783
+
1784
+ /**## ODFileUploadComponent `class`
1785
+ * A file upload component which allows users to upload one or multiple files inside a modal.
1786
+ * The label & description should be set via an `ODLabelComponent`
1787
+ */
1788
+ export class ODFileUploadComponent extends ODComponent<ODFileUploadComponentData,discord.FileUploadBuilder> {
1789
+ constructor(id:ODValidId,data:Partial<ODFileUploadComponentData>){
1790
+ const initData: ODFileUploadComponentData = {required:false,...data}
1791
+ super(id,initData)
1792
+ }
1793
+
1794
+ async build(){
1795
+ if (typeof this.data.minAmount == "number" && !(this.data.minAmount >= 0 && this.data.minAmount <= 10)) throw new ODSystemError("ODFileUploadComponent:build('"+this.id.value+"') => Minimum upload amount must be a value from 0 to 10.")
1796
+ if (typeof this.data.maxAmount == "number" && !(this.data.maxAmount >= 1 && this.data.maxAmount <= 10)) throw new ODSystemError("ODFileUploadComponent:build('"+this.id.value+"') => Maximum upload amount must be a value from 1 to 10.")
1797
+
1798
+ return new discord.FileUploadBuilder({
1799
+ custom_id:this.data.customId,
1800
+ required:this.data.required,
1801
+ min_values:this.data.minAmount,
1802
+ max_values:this.data.maxAmount
1803
+ })
1804
+ }
1805
+
1806
+ /**Set the custom id of this dropdown. */
1807
+ setCustomId(id:string|null){
1808
+ this.data.customId = id ?? undefined
1809
+ return this
1810
+ }
1811
+ /**Mark this file upload as required. */
1812
+ setRequired(required:boolean){
1813
+ this.data.required = required
1814
+ return this
1815
+ }
1816
+ /**Set the minimum amount of files to upload (0-10). */
1817
+ setMinAmount(amount:number|null){
1818
+ this.data.minAmount = amount ?? undefined
1819
+ return this
1820
+ }
1821
+ /**Set the maximum amount of files to upload (1-10). */
1822
+ setMaxAmount(amount:number|null){
1823
+ this.data.maxAmount = amount ?? undefined
1824
+ return this
1825
+ }
1826
+ }