kythia-core 0.9.5-beta → 0.10.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +4 -0
- package/README.md +13 -4
- package/biome.json +40 -0
- package/bun.lock +445 -0
- package/changelog.md +7 -0
- package/index.js +1 -3
- package/package.json +57 -44
- package/src/Kythia.js +530 -449
- package/src/KythiaClient.js +90 -49
- package/src/database/KythiaMigrator.js +116 -0
- package/src/database/KythiaModel.js +1519 -1050
- package/src/database/KythiaSequelize.js +104 -95
- package/src/database/KythiaStorage.js +117 -0
- package/src/database/ModelLoader.js +79 -0
- package/src/managers/AddonManager.js +1190 -946
- package/src/managers/EventManager.js +80 -75
- package/src/managers/InteractionManager.js +794 -589
- package/src/managers/ShutdownManager.js +200 -179
- package/src/structures/BaseCommand.js +40 -36
- package/src/utils/color.js +157 -153
- package/src/utils/formatter.js +81 -81
- package/src/utils/index.js +2 -2
- package/src/database/KythiaORM.js +0 -520
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/managers/AddonManager.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.10.0-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Handles all addon loading, command registration, and component management.
|
|
@@ -13,954 +13,1198 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
SlashCommandBuilder,
|
|
17
|
+
SlashCommandSubcommandBuilder,
|
|
18
|
+
SlashCommandSubcommandGroupBuilder,
|
|
19
|
+
Collection,
|
|
20
|
+
ContextMenuCommandBuilder,
|
|
21
|
+
ApplicationCommandType,
|
|
22
22
|
} = require('discord.js');
|
|
23
|
-
const path = require('path');
|
|
24
|
-
const fs = require('fs');
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
const fs = require('node:fs');
|
|
25
25
|
|
|
26
26
|
class AddonManager {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
27
|
+
/**
|
|
28
|
+
* 🗝️ AddonManager Constructor
|
|
29
|
+
* Initializes the addon manager with necessary collections and maps.
|
|
30
|
+
* @param {Object} client - Discord client instance
|
|
31
|
+
* @param {Object} container - Dependency container
|
|
32
|
+
*/
|
|
33
|
+
constructor({ client, container }) {
|
|
34
|
+
this.client = client;
|
|
35
|
+
this.container = container;
|
|
36
|
+
this.logger = this.container.logger;
|
|
37
|
+
|
|
38
|
+
this.buttonHandlers = new Map();
|
|
39
|
+
this.modalHandlers = new Map();
|
|
40
|
+
this.selectMenuHandlers = new Map();
|
|
41
|
+
this.autocompleteHandlers = new Map();
|
|
42
|
+
this.commandCategoryMap = new Map();
|
|
43
|
+
this.categoryToFeatureMap = new Map();
|
|
44
|
+
this.embedDrafts = new Collection();
|
|
45
|
+
this.eventHandlers = new Map();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 🔘 Register Button Handler
|
|
50
|
+
* Registers a handler function for a specific button customId.
|
|
51
|
+
* @param {string} customId - The customId of the button
|
|
52
|
+
* @param {Function} handler - The handler function to execute
|
|
53
|
+
*/
|
|
54
|
+
registerButtonHandler(customId, handler) {
|
|
55
|
+
if (this.buttonHandlers.has(customId)) {
|
|
56
|
+
this.logger.warn(
|
|
57
|
+
`[REGISTRATION] Warning: Button handler for [${customId}] already exists and will be overwritten.`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
this.buttonHandlers.set(customId, handler);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 🔽 Register Select Menu Handler
|
|
64
|
+
* Registers a handler function for a specific select menu customId prefix.
|
|
65
|
+
* @param {string} customIdPrefix - The prefix of the select menu customId
|
|
66
|
+
* @param {Function} handler - The handler function to execute
|
|
67
|
+
*/
|
|
68
|
+
registerSelectMenuHandler(customIdPrefix, handler) {
|
|
69
|
+
if (this.selectMenuHandlers.has(customIdPrefix)) {
|
|
70
|
+
this.logger.warn(
|
|
71
|
+
`[REGISTRATION] Warning: Select menu handler for [${customIdPrefix}] already exists and will be overwritten.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
this.selectMenuHandlers.set(customIdPrefix, handler);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 📝 Register Modal Handler
|
|
79
|
+
* Registers a handler function for a modal, using a prefix of the customId.
|
|
80
|
+
* @param {string} customIdPrefix - The prefix of the modal customId
|
|
81
|
+
* @param {Function} handler - The handler function to execute
|
|
82
|
+
*/
|
|
83
|
+
registerModalHandler(customIdPrefix, handler) {
|
|
84
|
+
if (this.modalHandlers.has(customIdPrefix)) {
|
|
85
|
+
this.logger.warn(
|
|
86
|
+
`[REGISTRATION] Warning: Modal handler for [${customIdPrefix}] already exists and will be overwritten.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
this.modalHandlers.set(customIdPrefix, handler);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 📋 Register Autocomplete Handler
|
|
94
|
+
* Registers a handler for autocomplete interactions for a specific command or subcommand.
|
|
95
|
+
* @param {string} commandName - The command or subcommand key
|
|
96
|
+
* @param {Function} handler - The autocomplete handler function
|
|
97
|
+
*/
|
|
98
|
+
registerAutocompleteHandler(commandName, handler) {
|
|
99
|
+
if (this.autocompleteHandlers.has(commandName)) {
|
|
100
|
+
this.logger.warn(
|
|
101
|
+
`[REGISTRATION] Warning: Autocomplete handler for [${commandName}] already exists and will be overwritten.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
this.autocompleteHandlers.set(commandName, handler);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 🔍 Check if module is a BaseCommand class
|
|
109
|
+
* @param {any} module - The module to check
|
|
110
|
+
* @returns {boolean} True if module is a class extending BaseCommand
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
_isBaseCommandClass(module) {
|
|
114
|
+
if (typeof module !== 'function') return false;
|
|
115
|
+
if (!module.prototype) return false;
|
|
116
|
+
|
|
117
|
+
const hasExecute = typeof module.prototype.execute === 'function';
|
|
118
|
+
return hasExecute;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 🏗️ Instantiate and prepare BaseCommand class
|
|
123
|
+
* @param {Function} CommandClass - The command class to instantiate
|
|
124
|
+
* @returns {Object} Command instance with proper structure
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
_instantiateBaseCommand(CommandClass) {
|
|
128
|
+
try {
|
|
129
|
+
return new CommandClass(this.container);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
this.logger.error(`Failed to instantiate BaseCommand class:`, error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* INTERNAL: Creates a Builder instance (Slash, Subcommand, Group) from a module's data property.
|
|
138
|
+
* @param {Object} data - The data property (can be function, builder, or object)
|
|
139
|
+
* @param {Class} BuilderClass - The d.js class to use (SlashCommandBuilder, etc.)
|
|
140
|
+
* @returns {SlashCommandBuilder|SlashCommandSubcommandBuilder|SlashCommandSubcommandGroupBuilder}
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
_createBuilderFromData(data, BuilderClass) {
|
|
144
|
+
let builder = new BuilderClass();
|
|
145
|
+
|
|
146
|
+
if (typeof data === 'function') {
|
|
147
|
+
data(builder);
|
|
148
|
+
} else if (data instanceof BuilderClass) {
|
|
149
|
+
builder = data;
|
|
150
|
+
} else if (typeof data === 'object') {
|
|
151
|
+
builder.setName(data.name || 'unnamed');
|
|
152
|
+
builder.setDescription(data.description || 'No description');
|
|
153
|
+
|
|
154
|
+
if (BuilderClass === SlashCommandBuilder) {
|
|
155
|
+
builder.setDescription(data.description || 'No description');
|
|
156
|
+
if (data.permissions) {
|
|
157
|
+
builder.setDefaultMemberPermissions(data.permissions);
|
|
158
|
+
}
|
|
159
|
+
if (data.guildOnly !== undefined) {
|
|
160
|
+
builder.setDMPermission(!data.guildOnly);
|
|
161
|
+
}
|
|
162
|
+
} else if (BuilderClass === ContextMenuCommandBuilder) {
|
|
163
|
+
builder.setType(data.type || ApplicationCommandType.User);
|
|
164
|
+
if (data.permissions) {
|
|
165
|
+
builder.setDefaultMemberPermissions(data.permissions);
|
|
166
|
+
}
|
|
167
|
+
if (data.guildOnly !== undefined) {
|
|
168
|
+
builder.setDMPermission(!data.guildOnly);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return builder;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 📝 Register Command Helper
|
|
177
|
+
* Registers a single command file/module, adds it to the command collection, and prepares it for deployment.
|
|
178
|
+
* @param {Object} module - The command module
|
|
179
|
+
* @param {string} filePath - The file path of the command
|
|
180
|
+
* @param {Set} commandNamesSet - Set of already registered command names
|
|
181
|
+
* @param {Array} commandDataForDeployment - Array to collect command data for deployment
|
|
182
|
+
* @param {Object} permissionDefaults - Permission defaults for the command
|
|
183
|
+
* @param {Object} options - Additional options (e.g., folderName)
|
|
184
|
+
* @returns {Object|null} Summary object for logging, or null if not registered
|
|
185
|
+
*/
|
|
186
|
+
registerCommand(
|
|
187
|
+
module,
|
|
188
|
+
filePath,
|
|
189
|
+
commandNamesSet,
|
|
190
|
+
commandDataForDeployment,
|
|
191
|
+
permissionDefaults = {},
|
|
192
|
+
options = {},
|
|
193
|
+
) {
|
|
194
|
+
if (this._isBaseCommandClass(module)) {
|
|
195
|
+
module = this._instantiateBaseCommand(module);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!module || !module.data) return null;
|
|
199
|
+
|
|
200
|
+
let builderClass;
|
|
201
|
+
|
|
202
|
+
if (module.data instanceof ContextMenuCommandBuilder) {
|
|
203
|
+
builderClass = ContextMenuCommandBuilder;
|
|
204
|
+
} else {
|
|
205
|
+
builderClass = SlashCommandBuilder;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const commandBuilder = this._createBuilderFromData(
|
|
209
|
+
module.data,
|
|
210
|
+
builderClass,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const commandName = commandBuilder.name;
|
|
214
|
+
const category =
|
|
215
|
+
options.folderName || path.basename(path.dirname(filePath));
|
|
216
|
+
|
|
217
|
+
const categoryDefaults = permissionDefaults[category] || {};
|
|
218
|
+
const finalCommand = {
|
|
219
|
+
...categoryDefaults,
|
|
220
|
+
...module,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
this.commandCategoryMap.set(commandName, category);
|
|
224
|
+
if (commandNamesSet.has(commandName)) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Duplicate command name detected: "${commandName}" in ${filePath}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
commandNamesSet.add(commandName);
|
|
231
|
+
this.client.commands.set(commandName, finalCommand);
|
|
232
|
+
commandDataForDeployment.push(commandBuilder.toJSON());
|
|
233
|
+
|
|
234
|
+
if (typeof finalCommand.autocomplete === 'function') {
|
|
235
|
+
this.registerAutocompleteHandler(commandName, finalCommand.autocomplete);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
type: 'single',
|
|
240
|
+
name: commandName,
|
|
241
|
+
folder: category,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 🧩 Load Addons & Register Commands/Events
|
|
247
|
+
* Loads all addons from the addons directory, registers their commands, events, and components.
|
|
248
|
+
* @param {Object} kythiaInstance - The main Kythia instance for addon registration
|
|
249
|
+
* @returns {Promise<Array>} Array of command data for deployment
|
|
250
|
+
*/
|
|
251
|
+
async loadAddons(kythiaInstance) {
|
|
252
|
+
this.logger.info('🔌 Loading & Registering Kythia Addons...');
|
|
253
|
+
const commandDataForDeployment = [];
|
|
254
|
+
const addonsDir = path.join(this.container.appRoot, 'addons');
|
|
255
|
+
if (!fs.existsSync(addonsDir)) return commandDataForDeployment;
|
|
256
|
+
|
|
257
|
+
let addonFolders = fs
|
|
258
|
+
.readdirSync(addonsDir, { withFileTypes: true })
|
|
259
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith('_'));
|
|
260
|
+
|
|
261
|
+
const coreAddon = addonFolders.find((d) => d.name === 'core');
|
|
262
|
+
const otherAddons = addonFolders.filter((d) => d.name !== 'core');
|
|
263
|
+
if (coreAddon) {
|
|
264
|
+
addonFolders = [coreAddon, ...otherAddons];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const commandNamesSet = new Set();
|
|
268
|
+
const addonSummaries = [];
|
|
269
|
+
|
|
270
|
+
for (const addon of addonFolders) {
|
|
271
|
+
const addonDir = path.join(addonsDir, addon.name);
|
|
272
|
+
let addonVersion = 'v0.0.0-alpha';
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const addonJsonPath = path.join(addonDir, 'addon.json');
|
|
276
|
+
if (fs.existsSync(addonJsonPath)) {
|
|
277
|
+
let addonJson;
|
|
278
|
+
try {
|
|
279
|
+
const addonJsonRaw = fs.readFileSync(addonJsonPath, 'utf8');
|
|
280
|
+
addonJson = JSON.parse(addonJsonRaw);
|
|
281
|
+
} catch (jsonErr) {
|
|
282
|
+
this.logger.warn(
|
|
283
|
+
`🔴 Failed to parse addon.json for ${addon.name}: ${jsonErr.message}`,
|
|
284
|
+
);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
addonVersion = addonJson.version || 'v0.0.0-alpha';
|
|
289
|
+
if (addonJson.active === false) {
|
|
290
|
+
this.logger.info(`🟠 Addon ${addon.name.toUpperCase()} disabled`);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (addonJson.featureFlag) {
|
|
294
|
+
this.commandCategoryMap.set(addon.name, addon.name);
|
|
295
|
+
this.categoryToFeatureMap.set(addon.name, addonJson.featureFlag);
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
this.logger.warn(
|
|
299
|
+
`🔴 Addon ${addon.name.toUpperCase()} is missing addon.json. Skipping.`,
|
|
300
|
+
);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
} catch (e) {
|
|
304
|
+
this.logger.warn(
|
|
305
|
+
`🔴 Error reading addon.json for ${addonDir}: ${e.message}`,
|
|
306
|
+
);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const configAddons = this.container.kythiaConfig?.addons || {};
|
|
312
|
+
if (configAddons.all?.active === false) {
|
|
313
|
+
this.logger.info(
|
|
314
|
+
`🟠 Addon ${addon.name.toUpperCase()} disabled via kythia config`,
|
|
315
|
+
);
|
|
316
|
+
continue;
|
|
317
|
+
} else if (configAddons[addon.name]?.active === false) {
|
|
318
|
+
this.logger.info(
|
|
319
|
+
`🟠 Addon ${addon.name.toUpperCase()} disabled via kythia config`,
|
|
320
|
+
);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
} catch (e) {
|
|
324
|
+
this.logger.warn(
|
|
325
|
+
`🔴 Error checking config for addon ${addon.name.toUpperCase()}: ${e.message}`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let addonPermissionDefaults = {};
|
|
330
|
+
const permissionsFilePath = path.join(addonDir, 'permissions.js');
|
|
331
|
+
|
|
332
|
+
if (fs.existsSync(permissionsFilePath)) {
|
|
333
|
+
try {
|
|
334
|
+
addonPermissionDefaults = require(permissionsFilePath);
|
|
335
|
+
this.logger.info(
|
|
336
|
+
` └─> Found and loaded permission defaults for addon '${addon.name.toUpperCase()}'`,
|
|
337
|
+
);
|
|
338
|
+
} catch (e) {
|
|
339
|
+
this.logger.warn(
|
|
340
|
+
` └─> Failed to load permissions.js for addon '${addon.name.toUpperCase()}': ${e.message}`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const loadedCommandsSummary = [];
|
|
346
|
+
const loadedEventsSummary = [];
|
|
347
|
+
const loadedRegisterSummary = [];
|
|
348
|
+
|
|
349
|
+
const commandsPath = path.join(addonDir, 'commands');
|
|
350
|
+
if (fs.existsSync(commandsPath)) {
|
|
351
|
+
try {
|
|
352
|
+
const commandsResult = await this._loadCommandsFromPath(
|
|
353
|
+
commandsPath,
|
|
354
|
+
addon,
|
|
355
|
+
addonPermissionDefaults,
|
|
356
|
+
commandNamesSet,
|
|
357
|
+
commandDataForDeployment,
|
|
358
|
+
);
|
|
359
|
+
loadedCommandsSummary.push(...commandsResult);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
this.logger.error(
|
|
362
|
+
`❌ Failed to load commands from addon "${addon.name}":`,
|
|
363
|
+
error,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const registerPath = path.join(addonDir, 'register.js');
|
|
369
|
+
if (fs.existsSync(registerPath)) {
|
|
370
|
+
try {
|
|
371
|
+
const registration = require(registerPath);
|
|
372
|
+
if (typeof registration.initialize === 'function') {
|
|
373
|
+
const registrationSummary =
|
|
374
|
+
await registration.initialize(kythiaInstance);
|
|
375
|
+
if (
|
|
376
|
+
Array.isArray(registrationSummary) &&
|
|
377
|
+
registrationSummary.length > 0
|
|
378
|
+
) {
|
|
379
|
+
loadedRegisterSummary.push(...registrationSummary);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch (error) {
|
|
383
|
+
this.logger.error(
|
|
384
|
+
`❌ Failed to register components for [${addon.name}]:`,
|
|
385
|
+
error,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const eventsPath = path.join(addonDir, 'events');
|
|
391
|
+
if (fs.existsSync(eventsPath)) {
|
|
392
|
+
const eventFiles = fs
|
|
393
|
+
.readdirSync(eventsPath)
|
|
394
|
+
.filter((file) => file.endsWith('.js'));
|
|
395
|
+
for (const file of eventFiles) {
|
|
396
|
+
const eventName = path.basename(file, '.js');
|
|
397
|
+
try {
|
|
398
|
+
const eventHandler = require(path.join(eventsPath, file));
|
|
399
|
+
if (typeof eventHandler === 'function') {
|
|
400
|
+
if (!this.eventHandlers.has(eventName)) {
|
|
401
|
+
this.eventHandlers.set(eventName, []);
|
|
402
|
+
}
|
|
403
|
+
this.eventHandlers.get(eventName).push(eventHandler);
|
|
404
|
+
loadedEventsSummary.push(eventName);
|
|
405
|
+
}
|
|
406
|
+
} catch (error) {
|
|
407
|
+
this.logger.error(
|
|
408
|
+
`❌ Failed to register event [${eventName}] for [${addon.name}]:`,
|
|
409
|
+
error,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
addonSummaries.push({
|
|
416
|
+
name: addon.name,
|
|
417
|
+
version: addonVersion,
|
|
418
|
+
commands: loadedCommandsSummary,
|
|
419
|
+
events: loadedEventsSummary,
|
|
420
|
+
register: loadedRegisterSummary,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
this._logAddonSummary(addonSummaries);
|
|
425
|
+
return commandDataForDeployment;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Load commands from a specific path
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
async _loadCommandsFromPath(
|
|
433
|
+
commandsPath,
|
|
434
|
+
addon,
|
|
435
|
+
addonPermissionDefaults,
|
|
436
|
+
commandNamesSet,
|
|
437
|
+
commandDataForDeployment,
|
|
438
|
+
) {
|
|
439
|
+
const isTopLevelCommandGroup = fs.existsSync(
|
|
440
|
+
path.join(commandsPath, '_command.js'),
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
if (isTopLevelCommandGroup) {
|
|
444
|
+
return await this._loadTopLevelCommandGroup(
|
|
445
|
+
commandsPath,
|
|
446
|
+
addon,
|
|
447
|
+
addonPermissionDefaults,
|
|
448
|
+
commandNamesSet,
|
|
449
|
+
commandDataForDeployment,
|
|
450
|
+
);
|
|
451
|
+
} else {
|
|
452
|
+
return await this._loadIndividualCommands(
|
|
453
|
+
commandsPath,
|
|
454
|
+
addon,
|
|
455
|
+
addonPermissionDefaults,
|
|
456
|
+
commandNamesSet,
|
|
457
|
+
commandDataForDeployment,
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Load top-level command group (supports BaseCommand classes)
|
|
464
|
+
* @private
|
|
465
|
+
*/
|
|
466
|
+
async _loadTopLevelCommandGroup(
|
|
467
|
+
commandsPath,
|
|
468
|
+
addon,
|
|
469
|
+
commandNamesSet,
|
|
470
|
+
commandDataForDeployment,
|
|
471
|
+
) {
|
|
472
|
+
const loadedCommandsSummary = [];
|
|
473
|
+
let commandDef = require(path.join(commandsPath, '_command.js'));
|
|
474
|
+
|
|
475
|
+
if (this._isBaseCommandClass(commandDef)) {
|
|
476
|
+
commandDef = this._instantiateBaseCommand(commandDef);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const mainBuilder = this._createBuilderFromData(
|
|
480
|
+
commandDef.data,
|
|
481
|
+
SlashCommandBuilder,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
const mainCommandName = mainBuilder.name;
|
|
485
|
+
|
|
486
|
+
if (commandDef.featureFlag) {
|
|
487
|
+
this.commandCategoryMap.set(mainCommandName, addon.name);
|
|
488
|
+
this.categoryToFeatureMap.set(addon.name, commandDef.featureFlag);
|
|
489
|
+
}
|
|
490
|
+
this.commandCategoryMap.set(mainCommandName, addon.name);
|
|
491
|
+
|
|
492
|
+
if (commandNamesSet.has(mainCommandName))
|
|
493
|
+
throw new Error(`Duplicate command name: ${mainCommandName}`);
|
|
494
|
+
commandNamesSet.add(mainCommandName);
|
|
495
|
+
|
|
496
|
+
this.client.commands.set(mainCommandName, commandDef);
|
|
497
|
+
|
|
498
|
+
if (typeof commandDef.autocomplete === 'function') {
|
|
499
|
+
this.registerAutocompleteHandler(
|
|
500
|
+
mainCommandName,
|
|
501
|
+
commandDef.autocomplete,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const loadedSubcommandsSummary = [];
|
|
506
|
+
const contents = fs.readdirSync(commandsPath, { withFileTypes: true });
|
|
507
|
+
|
|
508
|
+
for (const item of contents) {
|
|
509
|
+
const itemPath = path.join(commandsPath, item.name);
|
|
510
|
+
|
|
511
|
+
if (
|
|
512
|
+
item.isFile() &&
|
|
513
|
+
item.name.endsWith('.js') &&
|
|
514
|
+
!item.name.startsWith('_')
|
|
515
|
+
) {
|
|
516
|
+
let subModule = require(itemPath);
|
|
517
|
+
|
|
518
|
+
const isSubcommand =
|
|
519
|
+
subModule.subcommand === true || this._isBaseCommandClass(subModule);
|
|
520
|
+
|
|
521
|
+
if (this._isBaseCommandClass(subModule)) {
|
|
522
|
+
subModule = this._instantiateBaseCommand(subModule);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (!isSubcommand) continue;
|
|
526
|
+
if (!subModule.data) continue;
|
|
527
|
+
|
|
528
|
+
const subBuilder = this._createBuilderFromData(
|
|
529
|
+
subModule.data,
|
|
530
|
+
SlashCommandSubcommandBuilder,
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
mainBuilder.addSubcommand(subBuilder);
|
|
534
|
+
this.client.commands.set(
|
|
535
|
+
`${mainCommandName} ${subBuilder.name}`,
|
|
536
|
+
subModule,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
if (typeof subModule.autocomplete === 'function') {
|
|
540
|
+
this.registerAutocompleteHandler(
|
|
541
|
+
`${mainCommandName} ${subBuilder.name}`,
|
|
542
|
+
subModule.autocomplete,
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
loadedSubcommandsSummary.push(subBuilder.name);
|
|
547
|
+
} else if (item.isDirectory()) {
|
|
548
|
+
const groupDefPath = path.join(itemPath, '_group.js');
|
|
549
|
+
|
|
550
|
+
if (!fs.existsSync(groupDefPath)) {
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
let groupModule = require(groupDefPath);
|
|
556
|
+
|
|
557
|
+
if (this._isBaseCommandClass(groupModule)) {
|
|
558
|
+
groupModule = this._instantiateBaseCommand(groupModule);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (!groupModule.data) continue;
|
|
562
|
+
|
|
563
|
+
const groupBuilder = this._createBuilderFromData(
|
|
564
|
+
groupModule.data,
|
|
565
|
+
SlashCommandSubcommandGroupBuilder,
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
const subcommandsInGroupSummary = [];
|
|
569
|
+
const subCommandFiles = fs
|
|
570
|
+
.readdirSync(itemPath)
|
|
571
|
+
.filter((f) => f.endsWith('.js') && !f.startsWith('_'));
|
|
572
|
+
|
|
573
|
+
for (const file of subCommandFiles) {
|
|
574
|
+
const subCommandPath = path.join(itemPath, file);
|
|
575
|
+
let subModule = require(subCommandPath);
|
|
576
|
+
|
|
577
|
+
if (this._isBaseCommandClass(subModule)) {
|
|
578
|
+
subModule = this._instantiateBaseCommand(subModule);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (!subModule.data) continue;
|
|
582
|
+
|
|
583
|
+
const subBuilder = this._createBuilderFromData(
|
|
584
|
+
subModule.data,
|
|
585
|
+
SlashCommandSubcommandBuilder,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
groupBuilder.addSubcommand(subBuilder);
|
|
589
|
+
|
|
590
|
+
const commandKey = `${mainCommandName} ${groupBuilder.name} ${subBuilder.name}`;
|
|
591
|
+
|
|
592
|
+
this.client.commands.set(commandKey, subModule);
|
|
593
|
+
|
|
594
|
+
if (typeof subModule.autocomplete === 'function') {
|
|
595
|
+
this.registerAutocompleteHandler(
|
|
596
|
+
commandKey,
|
|
597
|
+
subModule.autocomplete,
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
subcommandsInGroupSummary.push(subBuilder.name);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
mainBuilder.addSubcommandGroup(groupBuilder);
|
|
605
|
+
|
|
606
|
+
loadedSubcommandsSummary.push({
|
|
607
|
+
group: groupBuilder.name,
|
|
608
|
+
subcommands: subcommandsInGroupSummary,
|
|
609
|
+
});
|
|
610
|
+
} catch (e) {
|
|
611
|
+
this.logger.error(
|
|
612
|
+
`❌ Failed to load subcommand group from ${itemPath}:`,
|
|
613
|
+
e,
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
commandDataForDeployment.push(mainBuilder.toJSON());
|
|
620
|
+
loadedCommandsSummary.push({
|
|
621
|
+
type: 'group',
|
|
622
|
+
name: mainCommandName,
|
|
623
|
+
subcommands: loadedSubcommandsSummary,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
return loadedCommandsSummary;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Load individual commands (supports BaseCommand classes)
|
|
631
|
+
* @private
|
|
632
|
+
*/
|
|
633
|
+
async _loadIndividualCommands(
|
|
634
|
+
commandsPath,
|
|
635
|
+
addon,
|
|
636
|
+
addonPermissionDefaults,
|
|
637
|
+
commandNamesSet,
|
|
638
|
+
commandDataForDeployment,
|
|
639
|
+
) {
|
|
640
|
+
const loadedCommandsSummary = [];
|
|
641
|
+
const commandItems = fs.readdirSync(commandsPath, { withFileTypes: true });
|
|
642
|
+
|
|
643
|
+
for (const item of commandItems) {
|
|
644
|
+
const itemPath = path.join(commandsPath, item.name);
|
|
645
|
+
|
|
646
|
+
if (
|
|
647
|
+
item.isDirectory() &&
|
|
648
|
+
fs.existsSync(path.join(itemPath, '_command.js'))
|
|
649
|
+
) {
|
|
650
|
+
let commandDef = require(path.join(itemPath, '_command.js'));
|
|
651
|
+
if (this._isBaseCommandClass(commandDef)) {
|
|
652
|
+
commandDef = this._instantiateBaseCommand(commandDef);
|
|
653
|
+
}
|
|
654
|
+
const mainBuilder = this._createBuilderFromData(
|
|
655
|
+
commandDef.data,
|
|
656
|
+
SlashCommandBuilder,
|
|
657
|
+
);
|
|
658
|
+
const mainCommandName = mainBuilder.name;
|
|
659
|
+
|
|
660
|
+
if (commandDef.featureFlag) {
|
|
661
|
+
this.commandCategoryMap.set(mainCommandName, addon.name);
|
|
662
|
+
this.categoryToFeatureMap.set(addon.name, commandDef.featureFlag);
|
|
663
|
+
}
|
|
664
|
+
if (commandNamesSet.has(mainCommandName))
|
|
665
|
+
throw new Error(`Duplicate name: ${mainCommandName}`);
|
|
666
|
+
commandNamesSet.add(mainCommandName);
|
|
667
|
+
this.client.commands.set(mainCommandName, commandDef);
|
|
668
|
+
if (typeof commandDef.autocomplete === 'function') {
|
|
669
|
+
this.registerAutocompleteHandler(
|
|
670
|
+
mainCommandName,
|
|
671
|
+
commandDef.autocomplete,
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const subcommandsList = [];
|
|
676
|
+
const groupContents = fs.readdirSync(itemPath, { withFileTypes: true });
|
|
677
|
+
|
|
678
|
+
for (const content of groupContents) {
|
|
679
|
+
const contentPath = path.join(itemPath, content.name);
|
|
680
|
+
|
|
681
|
+
if (
|
|
682
|
+
content.isFile() &&
|
|
683
|
+
content.name.endsWith('.js') &&
|
|
684
|
+
!content.name.startsWith('_')
|
|
685
|
+
) {
|
|
686
|
+
let subModule = require(contentPath);
|
|
687
|
+
if (this._isBaseCommandClass(subModule)) {
|
|
688
|
+
subModule = this._instantiateBaseCommand(subModule);
|
|
689
|
+
}
|
|
690
|
+
if (!subModule.data) continue;
|
|
691
|
+
const subBuilder = this._createBuilderFromData(
|
|
692
|
+
subModule.data,
|
|
693
|
+
SlashCommandSubcommandBuilder,
|
|
694
|
+
);
|
|
695
|
+
mainBuilder.addSubcommand(subBuilder);
|
|
696
|
+
this.client.commands.set(
|
|
697
|
+
`${mainCommandName} ${subBuilder.name}`,
|
|
698
|
+
subModule,
|
|
699
|
+
);
|
|
700
|
+
if (typeof subModule.autocomplete === 'function') {
|
|
701
|
+
this.registerAutocompleteHandler(
|
|
702
|
+
`${mainCommandName} ${subBuilder.name}`,
|
|
703
|
+
subModule.autocomplete,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
subcommandsList.push(subBuilder.name);
|
|
707
|
+
} else if (
|
|
708
|
+
content.isDirectory() &&
|
|
709
|
+
fs.existsSync(path.join(contentPath, '_group.js'))
|
|
710
|
+
) {
|
|
711
|
+
let groupDef = require(path.join(contentPath, '_group.js'));
|
|
712
|
+
if (this._isBaseCommandClass(groupDef)) {
|
|
713
|
+
groupDef = this._instantiateBaseCommand(groupDef);
|
|
714
|
+
}
|
|
715
|
+
const groupBuilder = this._createBuilderFromData(
|
|
716
|
+
groupDef.data,
|
|
717
|
+
SlashCommandSubcommandGroupBuilder,
|
|
718
|
+
);
|
|
719
|
+
const subGroupList = [];
|
|
720
|
+
const subGroupContents = fs.readdirSync(contentPath, {
|
|
721
|
+
withFileTypes: true,
|
|
722
|
+
});
|
|
723
|
+
for (const subSubItem of subGroupContents) {
|
|
724
|
+
if (
|
|
725
|
+
subSubItem.isFile() &&
|
|
726
|
+
subSubItem.name.endsWith('.js') &&
|
|
727
|
+
!subSubItem.name.startsWith('_')
|
|
728
|
+
) {
|
|
729
|
+
const subSubPath = path.join(contentPath, subSubItem.name);
|
|
730
|
+
let subSubModule = require(subSubPath);
|
|
731
|
+
if (this._isBaseCommandClass(subSubModule)) {
|
|
732
|
+
subSubModule = this._instantiateBaseCommand(subSubModule);
|
|
733
|
+
}
|
|
734
|
+
if (!subSubModule.data) continue;
|
|
735
|
+
const subSubBuilder = this._createBuilderFromData(
|
|
736
|
+
subSubModule.data,
|
|
737
|
+
SlashCommandSubcommandBuilder,
|
|
738
|
+
);
|
|
739
|
+
groupBuilder.addSubcommand(subSubBuilder);
|
|
740
|
+
this.client.commands.set(
|
|
741
|
+
`${mainCommandName} ${groupBuilder.name} ${subSubBuilder.name}`,
|
|
742
|
+
subSubModule,
|
|
743
|
+
);
|
|
744
|
+
if (typeof subSubModule.autocomplete === 'function') {
|
|
745
|
+
this.registerAutocompleteHandler(
|
|
746
|
+
`${mainCommandName} ${groupBuilder.name} ${subSubBuilder.name}`,
|
|
747
|
+
subSubModule.autocomplete,
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
subGroupList.push(subSubBuilder.name);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
mainBuilder.addSubcommandGroup(groupBuilder);
|
|
754
|
+
subcommandsList.push({
|
|
755
|
+
group: groupBuilder.name,
|
|
756
|
+
subcommands: subGroupList,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
commandDataForDeployment.push(mainBuilder.toJSON());
|
|
761
|
+
loadedCommandsSummary.push({
|
|
762
|
+
type: 'group',
|
|
763
|
+
name: mainCommandName,
|
|
764
|
+
subcommands: subcommandsList,
|
|
765
|
+
});
|
|
766
|
+
} else if (
|
|
767
|
+
item.isFile() &&
|
|
768
|
+
item.name.endsWith('.js') &&
|
|
769
|
+
!item.name.startsWith('_')
|
|
770
|
+
) {
|
|
771
|
+
let commandModule = require(itemPath);
|
|
772
|
+
let isClass = false;
|
|
773
|
+
if (this._isBaseCommandClass(commandModule)) {
|
|
774
|
+
commandModule = this._instantiateBaseCommand(commandModule);
|
|
775
|
+
isClass = true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (!isClass && commandModule.subcommand) continue;
|
|
779
|
+
|
|
780
|
+
let summarySlash = null;
|
|
781
|
+
let summaryContext = null;
|
|
782
|
+
|
|
783
|
+
if (commandModule.slashCommand) {
|
|
784
|
+
const builder = commandModule.slashCommand;
|
|
785
|
+
const name = builder.name;
|
|
786
|
+
try {
|
|
787
|
+
const allLocales = this.container.translator.getLocales();
|
|
788
|
+
const nameLocalizations = {};
|
|
789
|
+
const descriptionLocalizations = {};
|
|
790
|
+
if (typeof allLocales.entries === 'function') {
|
|
791
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
792
|
+
const nameKey = `command_${name}_name`;
|
|
793
|
+
const descKey = `command_${name}_desc`;
|
|
794
|
+
if (translations[nameKey])
|
|
795
|
+
nameLocalizations[lang] = translations[nameKey];
|
|
796
|
+
if (translations[descKey])
|
|
797
|
+
descriptionLocalizations[lang] = translations[descKey];
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
for (const lang in allLocales) {
|
|
801
|
+
const translations = allLocales[lang];
|
|
802
|
+
const nameKey = `command_${name}_name`;
|
|
803
|
+
const descKey = `command_${name}_desc`;
|
|
804
|
+
if (translations[nameKey])
|
|
805
|
+
nameLocalizations[lang] = translations[nameKey];
|
|
806
|
+
if (translations[descKey])
|
|
807
|
+
descriptionLocalizations[lang] = translations[descKey];
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (Object.keys(nameLocalizations).length > 0) {
|
|
811
|
+
builder.setNameLocalizations(nameLocalizations);
|
|
812
|
+
}
|
|
813
|
+
if (Object.keys(descriptionLocalizations).length > 0) {
|
|
814
|
+
builder.setDescriptionLocalizations(descriptionLocalizations);
|
|
815
|
+
}
|
|
816
|
+
this._applySubcommandLocalizations(builder, name, allLocales);
|
|
817
|
+
} catch (e) {
|
|
818
|
+
this.logger.warn(
|
|
819
|
+
`Failed to load localizations for command "${name}": ${e.message}`,
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (commandNamesSet.has(name)) {
|
|
824
|
+
this.logger.warn(
|
|
825
|
+
`Duplicate command name detected: "${name}" in ${itemPath}`,
|
|
826
|
+
);
|
|
827
|
+
} else {
|
|
828
|
+
commandNamesSet.add(name);
|
|
829
|
+
|
|
830
|
+
this.client.commands.set(name, commandModule);
|
|
831
|
+
}
|
|
832
|
+
commandDataForDeployment.push(builder.toJSON());
|
|
833
|
+
summarySlash = {
|
|
834
|
+
type: 'single',
|
|
835
|
+
name: name,
|
|
836
|
+
folder: addon.name,
|
|
837
|
+
kind: 'slash',
|
|
838
|
+
};
|
|
839
|
+
if (summarySlash) loadedCommandsSummary.push(summarySlash);
|
|
840
|
+
this.commandCategoryMap.set(name, addon.name);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (commandModule.contextMenuCommand) {
|
|
844
|
+
const builder = commandModule.contextMenuCommand;
|
|
845
|
+
const name = builder.name;
|
|
846
|
+
if (commandNamesSet.has(name) && !commandModule.slashCommand) {
|
|
847
|
+
this.logger.warn(
|
|
848
|
+
`Duplicate command name detected: "${name}" in ${itemPath}`,
|
|
849
|
+
);
|
|
850
|
+
} else {
|
|
851
|
+
if (!commandNamesSet.has(name)) commandNamesSet.add(name);
|
|
852
|
+
|
|
853
|
+
this.client.commands.set(name, commandModule);
|
|
854
|
+
}
|
|
855
|
+
commandDataForDeployment.push(builder.toJSON());
|
|
856
|
+
summaryContext = {
|
|
857
|
+
type: 'single',
|
|
858
|
+
name: name,
|
|
859
|
+
folder: addon.name,
|
|
860
|
+
kind: 'contextMenu',
|
|
861
|
+
};
|
|
862
|
+
if (summaryContext) loadedCommandsSummary.push(summaryContext);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (
|
|
866
|
+
!isClass &&
|
|
867
|
+
!commandModule.slashCommand &&
|
|
868
|
+
!commandModule.contextMenuCommand
|
|
869
|
+
) {
|
|
870
|
+
const summary = this.registerCommand(
|
|
871
|
+
commandModule,
|
|
872
|
+
itemPath,
|
|
873
|
+
commandNamesSet,
|
|
874
|
+
commandDataForDeployment,
|
|
875
|
+
addonPermissionDefaults,
|
|
876
|
+
{ folderName: addon.name },
|
|
877
|
+
);
|
|
878
|
+
if (summary) loadedCommandsSummary.push(summary);
|
|
879
|
+
}
|
|
880
|
+
} else if (item.isDirectory() && !item.name.startsWith('_')) {
|
|
881
|
+
const files = fs
|
|
882
|
+
.readdirSync(itemPath)
|
|
883
|
+
.filter((f) => f.endsWith('.js') && !f.startsWith('_'));
|
|
884
|
+
for (const file of files) {
|
|
885
|
+
const filePath = path.join(itemPath, file);
|
|
886
|
+
let commandModule = require(filePath);
|
|
887
|
+
let isClass = false;
|
|
888
|
+
if (this._isBaseCommandClass(commandModule)) {
|
|
889
|
+
commandModule = this._instantiateBaseCommand(commandModule);
|
|
890
|
+
isClass = true;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (!isClass && commandModule.subcommand) continue;
|
|
894
|
+
|
|
895
|
+
let summarySlash = null;
|
|
896
|
+
let summaryContext = null;
|
|
897
|
+
|
|
898
|
+
if (commandModule.slashCommand) {
|
|
899
|
+
const builder = commandModule.slashCommand;
|
|
900
|
+
const name = builder.name;
|
|
901
|
+
try {
|
|
902
|
+
const allLocales = this.container.translator.getLocales();
|
|
903
|
+
const nameLocalizations = {};
|
|
904
|
+
const descriptionLocalizations = {};
|
|
905
|
+
if (typeof allLocales.entries === 'function') {
|
|
906
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
907
|
+
const nameKey = `command_${name}_name`;
|
|
908
|
+
const descKey = `command_${name}_desc`;
|
|
909
|
+
if (translations[nameKey])
|
|
910
|
+
nameLocalizations[lang] = translations[nameKey];
|
|
911
|
+
if (translations[descKey])
|
|
912
|
+
descriptionLocalizations[lang] = translations[descKey];
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
for (const lang in allLocales) {
|
|
916
|
+
const translations = allLocales[lang];
|
|
917
|
+
const nameKey = `command_${name}_name`;
|
|
918
|
+
const descKey = `command_${name}_desc`;
|
|
919
|
+
if (translations[nameKey])
|
|
920
|
+
nameLocalizations[lang] = translations[nameKey];
|
|
921
|
+
if (translations[descKey])
|
|
922
|
+
descriptionLocalizations[lang] = translations[descKey];
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (Object.keys(nameLocalizations).length > 0) {
|
|
926
|
+
builder.setNameLocalizations(nameLocalizations);
|
|
927
|
+
}
|
|
928
|
+
if (Object.keys(descriptionLocalizations).length > 0) {
|
|
929
|
+
builder.setDescriptionLocalizations(descriptionLocalizations);
|
|
930
|
+
}
|
|
931
|
+
this._applySubcommandLocalizations(builder, name, allLocales);
|
|
932
|
+
} catch (e) {
|
|
933
|
+
this.logger.warn(
|
|
934
|
+
`Failed to load localizations for command "${name}": ${e.message}`,
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
this.commandCategoryMap.set(name, item.name);
|
|
938
|
+
if (commandNamesSet.has(name)) {
|
|
939
|
+
this.logger.warn(
|
|
940
|
+
`Duplicate slash command name detected: "${name}" in ${filePath}`,
|
|
941
|
+
);
|
|
942
|
+
} else {
|
|
943
|
+
commandNamesSet.add(name);
|
|
944
|
+
this.client.commands.set(name, commandModule);
|
|
945
|
+
commandDataForDeployment.push(builder.toJSON());
|
|
946
|
+
summarySlash = {
|
|
947
|
+
type: 'single',
|
|
948
|
+
name: name,
|
|
949
|
+
folder: item.name,
|
|
950
|
+
kind: 'slash',
|
|
951
|
+
};
|
|
952
|
+
if (summarySlash) loadedCommandsSummary.push(summarySlash);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (commandModule.contextMenuCommand) {
|
|
957
|
+
const builder = commandModule.contextMenuCommand;
|
|
958
|
+
const name = builder.name;
|
|
959
|
+
if (!this.client.commands.has(name)) {
|
|
960
|
+
this.client.commands.set(name, commandModule);
|
|
961
|
+
}
|
|
962
|
+
commandDataForDeployment.push(builder.toJSON());
|
|
963
|
+
summaryContext = {
|
|
964
|
+
type: 'single',
|
|
965
|
+
name: name,
|
|
966
|
+
folder: item.name,
|
|
967
|
+
kind: 'contextMenu',
|
|
968
|
+
};
|
|
969
|
+
if (summaryContext) loadedCommandsSummary.push(summaryContext);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (
|
|
973
|
+
!isClass &&
|
|
974
|
+
!commandModule.slashCommand &&
|
|
975
|
+
!commandModule.contextMenuCommand
|
|
976
|
+
) {
|
|
977
|
+
const summary = this.registerCommand(
|
|
978
|
+
commandModule,
|
|
979
|
+
filePath,
|
|
980
|
+
commandNamesSet,
|
|
981
|
+
commandDataForDeployment,
|
|
982
|
+
addonPermissionDefaults,
|
|
983
|
+
{ folderName: item.name },
|
|
984
|
+
);
|
|
985
|
+
if (summary) loadedCommandsSummary.push(summary);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return loadedCommandsSummary;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Apply subcommand localizations
|
|
995
|
+
* @private
|
|
996
|
+
*/
|
|
997
|
+
_applySubcommandLocalizations(commandBuilder, commandName, allLocales) {
|
|
998
|
+
if (Array.isArray(commandBuilder.options)) {
|
|
999
|
+
for (const group of commandBuilder.options) {
|
|
1000
|
+
if (
|
|
1001
|
+
typeof SlashCommandSubcommandGroupBuilder !== 'undefined' &&
|
|
1002
|
+
group instanceof SlashCommandSubcommandGroupBuilder
|
|
1003
|
+
) {
|
|
1004
|
+
const groupName = group.name;
|
|
1005
|
+
|
|
1006
|
+
const groupDescLocalizations = {};
|
|
1007
|
+
if (typeof allLocales.entries === 'function') {
|
|
1008
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
1009
|
+
const groupDescKey = `command_${commandName}_${groupName}_group_desc`;
|
|
1010
|
+
if (translations[groupDescKey])
|
|
1011
|
+
groupDescLocalizations[lang] = translations[groupDescKey];
|
|
1012
|
+
}
|
|
1013
|
+
} else {
|
|
1014
|
+
for (const lang in allLocales) {
|
|
1015
|
+
const translations = allLocales[lang];
|
|
1016
|
+
const groupDescKey = `command_${commandName}_${groupName}_group_desc`;
|
|
1017
|
+
if (translations[groupDescKey])
|
|
1018
|
+
groupDescLocalizations[lang] = translations[groupDescKey];
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (
|
|
1022
|
+
Object.keys(groupDescLocalizations).length > 0 &&
|
|
1023
|
+
typeof group.setDescriptionLocalizations === 'function'
|
|
1024
|
+
) {
|
|
1025
|
+
group.setDescriptionLocalizations(groupDescLocalizations);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (Array.isArray(group.options)) {
|
|
1029
|
+
for (const sub of group.options) {
|
|
1030
|
+
const subName = sub.name;
|
|
1031
|
+
|
|
1032
|
+
const subDescLocalizations = {};
|
|
1033
|
+
if (typeof allLocales.entries === 'function') {
|
|
1034
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
1035
|
+
const subDescKey = `command_${commandName}_${groupName}_${subName}_desc`;
|
|
1036
|
+
if (translations[subDescKey])
|
|
1037
|
+
subDescLocalizations[lang] = translations[subDescKey];
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
for (const lang in allLocales) {
|
|
1041
|
+
const translations = allLocales[lang];
|
|
1042
|
+
const subDescKey = `command_${commandName}_${groupName}_${subName}_desc`;
|
|
1043
|
+
if (translations[subDescKey])
|
|
1044
|
+
subDescLocalizations[lang] = translations[subDescKey];
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (
|
|
1048
|
+
Object.keys(subDescLocalizations).length > 0 &&
|
|
1049
|
+
typeof sub.setDescriptionLocalizations === 'function'
|
|
1050
|
+
) {
|
|
1051
|
+
sub.setDescriptionLocalizations(subDescLocalizations);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
if (Array.isArray(sub.options)) {
|
|
1055
|
+
for (const opt of sub.options) {
|
|
1056
|
+
const optName = opt.name;
|
|
1057
|
+
const optDescLocalizations = {};
|
|
1058
|
+
if (typeof allLocales.entries === 'function') {
|
|
1059
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
1060
|
+
const optDescKey = `command_${commandName}_${groupName}_${subName}_option_${optName}`;
|
|
1061
|
+
if (translations[optDescKey])
|
|
1062
|
+
optDescLocalizations[lang] = translations[optDescKey];
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
for (const lang in allLocales) {
|
|
1066
|
+
const translations = allLocales[lang];
|
|
1067
|
+
const optDescKey = `command_${commandName}_${groupName}_${subName}_option_${optName}`;
|
|
1068
|
+
if (translations[optDescKey])
|
|
1069
|
+
optDescLocalizations[lang] = translations[optDescKey];
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
if (
|
|
1073
|
+
Object.keys(optDescLocalizations).length > 0 &&
|
|
1074
|
+
typeof opt.setDescriptionLocalizations === 'function'
|
|
1075
|
+
) {
|
|
1076
|
+
opt.setDescriptionLocalizations(optDescLocalizations);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
} else if (
|
|
1083
|
+
typeof SlashCommandSubcommandBuilder !== 'undefined' &&
|
|
1084
|
+
group instanceof SlashCommandSubcommandBuilder
|
|
1085
|
+
) {
|
|
1086
|
+
const subName = group.name;
|
|
1087
|
+
const subDescLocalizations = {};
|
|
1088
|
+
if (typeof allLocales.entries === 'function') {
|
|
1089
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
1090
|
+
const subDescKey = `command_${commandName}_${subName}_desc`;
|
|
1091
|
+
if (translations[subDescKey])
|
|
1092
|
+
subDescLocalizations[lang] = translations[subDescKey];
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
for (const lang in allLocales) {
|
|
1096
|
+
const translations = allLocales[lang];
|
|
1097
|
+
const subDescKey = `command_${commandName}_${subName}_desc`;
|
|
1098
|
+
if (translations[subDescKey])
|
|
1099
|
+
subDescLocalizations[lang] = translations[subDescKey];
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
if (
|
|
1103
|
+
Object.keys(subDescLocalizations).length > 0 &&
|
|
1104
|
+
typeof group.setDescriptionLocalizations === 'function'
|
|
1105
|
+
) {
|
|
1106
|
+
group.setDescriptionLocalizations(subDescLocalizations);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (Array.isArray(group.options)) {
|
|
1110
|
+
for (const opt of group.options) {
|
|
1111
|
+
const optName = opt.name;
|
|
1112
|
+
const optDescLocalizations = {};
|
|
1113
|
+
if (typeof allLocales.entries === 'function') {
|
|
1114
|
+
for (const [lang, translations] of allLocales.entries()) {
|
|
1115
|
+
const optDescKey = `command_${commandName}_${subName}_option_${optName}`;
|
|
1116
|
+
if (translations[optDescKey])
|
|
1117
|
+
optDescLocalizations[lang] = translations[optDescKey];
|
|
1118
|
+
}
|
|
1119
|
+
} else {
|
|
1120
|
+
for (const lang in allLocales) {
|
|
1121
|
+
const translations = allLocales[lang];
|
|
1122
|
+
const optDescKey = `command_${commandName}_${subName}_option_${optName}`;
|
|
1123
|
+
if (translations[optDescKey])
|
|
1124
|
+
optDescLocalizations[lang] = translations[optDescKey];
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (
|
|
1128
|
+
Object.keys(optDescLocalizations).length > 0 &&
|
|
1129
|
+
typeof opt.setDescriptionLocalizations === 'function'
|
|
1130
|
+
) {
|
|
1131
|
+
opt.setDescriptionLocalizations(optDescLocalizations);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Log addon summary
|
|
1142
|
+
* @private
|
|
1143
|
+
*/
|
|
1144
|
+
_logAddonSummary(addonSummaries) {
|
|
1145
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬[ Addon(s) Loaded ]▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
1146
|
+
for (const addon of addonSummaries) {
|
|
1147
|
+
this.logger.info(`📦 ${addon.name} (v${addon.version})`);
|
|
1148
|
+
this.logger.info(' ⚙️ Command(s)');
|
|
1149
|
+
if (!addon.commands.length) {
|
|
1150
|
+
this.logger.info(' (no commands registered)');
|
|
1151
|
+
} else {
|
|
1152
|
+
for (const cmd of addon.commands) {
|
|
1153
|
+
if (cmd.type === 'group') {
|
|
1154
|
+
this.logger.info(` └─ /${cmd.name}`);
|
|
1155
|
+
for (const sub of cmd.subcommands) {
|
|
1156
|
+
if (typeof sub === 'string') {
|
|
1157
|
+
this.logger.info(` └─ ${sub}`);
|
|
1158
|
+
} else if (typeof sub === 'object' && sub.group) {
|
|
1159
|
+
this.logger.info(` └─ [${sub.group}]`);
|
|
1160
|
+
for (const subsub of sub.subcommands) {
|
|
1161
|
+
this.logger.info(` └─ ${subsub}`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
} else if (cmd.type === 'single') {
|
|
1166
|
+
let kindLabel = '';
|
|
1167
|
+
if (cmd.kind === 'slash') kindLabel = ' [slash]';
|
|
1168
|
+
else if (cmd.kind === 'contextMenu') kindLabel = ' [contextMenu]';
|
|
1169
|
+
if (cmd.folder) {
|
|
1170
|
+
this.logger.info(
|
|
1171
|
+
` └─ /${cmd.name} (${cmd.folder})${kindLabel}`,
|
|
1172
|
+
);
|
|
1173
|
+
} else {
|
|
1174
|
+
this.logger.info(` └─ /${cmd.name}${kindLabel}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (addon.register?.length) {
|
|
1180
|
+
this.logger.info(' 🧩 Component(s)');
|
|
1181
|
+
for (const reg of addon.register) {
|
|
1182
|
+
this.logger.info(` ${reg}`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (addon.events?.length) {
|
|
1186
|
+
this.logger.info(' 📢 Event(s)');
|
|
1187
|
+
for (const ev of addon.events) {
|
|
1188
|
+
this.logger.info(` └─ ${ev}`);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* Get handler maps for other managers
|
|
1196
|
+
*/
|
|
1197
|
+
getHandlers() {
|
|
1198
|
+
return {
|
|
1199
|
+
buttonHandlers: this.buttonHandlers,
|
|
1200
|
+
modalHandlers: this.modalHandlers,
|
|
1201
|
+
selectMenuHandlers: this.selectMenuHandlers,
|
|
1202
|
+
autocompleteHandlers: this.autocompleteHandlers,
|
|
1203
|
+
commandCategoryMap: this.commandCategoryMap,
|
|
1204
|
+
categoryToFeatureMap: this.categoryToFeatureMap,
|
|
1205
|
+
eventHandlers: this.eventHandlers,
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
964
1208
|
}
|
|
965
1209
|
|
|
966
1210
|
module.exports = AddonManager;
|