kythia-core 0.11.0-beta → 0.12.0-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.
Files changed (260) hide show
  1. package/dist/Kythia.d.ts +45 -0
  2. package/dist/Kythia.d.ts.map +1 -0
  3. package/dist/Kythia.js +443 -0
  4. package/dist/Kythia.js.map +1 -0
  5. package/dist/KythiaClient.d.ts +3 -0
  6. package/dist/KythiaClient.d.ts.map +1 -0
  7. package/dist/KythiaClient.js +69 -0
  8. package/dist/KythiaClient.js.map +1 -0
  9. package/dist/cli/Command.d.ts +9 -0
  10. package/dist/cli/Command.d.ts.map +1 -0
  11. package/dist/cli/Command.js +19 -0
  12. package/dist/cli/Command.js.map +1 -0
  13. package/dist/cli/commands/CacheClearCommand.d.ts +8 -0
  14. package/dist/cli/commands/CacheClearCommand.d.ts.map +1 -0
  15. package/dist/cli/commands/CacheClearCommand.js +94 -0
  16. package/dist/cli/commands/CacheClearCommand.js.map +1 -0
  17. package/dist/cli/commands/LangCheckCommand.d.ts +7 -0
  18. package/dist/cli/commands/LangCheckCommand.d.ts.map +1 -0
  19. package/dist/cli/commands/LangCheckCommand.js +345 -0
  20. package/dist/cli/commands/LangCheckCommand.js.map +1 -0
  21. package/dist/cli/commands/LangTranslateCommand.d.ts +8 -0
  22. package/dist/cli/commands/LangTranslateCommand.d.ts.map +1 -0
  23. package/dist/cli/commands/LangTranslateCommand.js +221 -0
  24. package/dist/cli/commands/LangTranslateCommand.js.map +1 -0
  25. package/dist/cli/commands/MakeMigrationCommand.d.ts +7 -0
  26. package/dist/cli/commands/MakeMigrationCommand.d.ts.map +1 -0
  27. package/dist/cli/commands/MakeMigrationCommand.js +55 -0
  28. package/dist/cli/commands/MakeMigrationCommand.js.map +1 -0
  29. package/dist/cli/commands/MakeModelCommand.d.ts +7 -0
  30. package/dist/cli/commands/MakeModelCommand.d.ts.map +1 -0
  31. package/dist/cli/commands/MakeModelCommand.js +56 -0
  32. package/dist/cli/commands/MakeModelCommand.js.map +1 -0
  33. package/dist/cli/commands/MigrateCommand.d.ts +14 -0
  34. package/dist/cli/commands/MigrateCommand.d.ts.map +1 -0
  35. package/dist/cli/commands/MigrateCommand.js +190 -0
  36. package/dist/cli/commands/MigrateCommand.js.map +1 -0
  37. package/dist/cli/commands/NamespaceCommand.d.ts +7 -0
  38. package/dist/cli/commands/NamespaceCommand.d.ts.map +1 -0
  39. package/dist/cli/commands/NamespaceCommand.js +92 -0
  40. package/dist/cli/commands/NamespaceCommand.js.map +1 -0
  41. package/dist/cli/commands/StructureCommand.d.ts +7 -0
  42. package/dist/cli/commands/StructureCommand.d.ts.map +1 -0
  43. package/dist/cli/commands/StructureCommand.js +51 -0
  44. package/dist/cli/commands/StructureCommand.js.map +1 -0
  45. package/dist/cli/commands/UpversionCommand.d.ts +7 -0
  46. package/dist/cli/commands/UpversionCommand.d.ts.map +1 -0
  47. package/dist/cli/commands/UpversionCommand.js +68 -0
  48. package/dist/cli/commands/UpversionCommand.js.map +1 -0
  49. package/dist/cli/index.d.ts +3 -0
  50. package/dist/cli/index.d.ts.map +1 -0
  51. package/dist/cli/index.js +44 -0
  52. package/dist/cli/index.js.map +1 -0
  53. package/dist/cli/utils/db.d.ts +9 -0
  54. package/dist/cli/utils/db.d.ts.map +1 -0
  55. package/dist/cli/utils/db.js +90 -0
  56. package/dist/cli/utils/db.js.map +1 -0
  57. package/dist/database/KythiaMigrator.d.ts +4 -0
  58. package/dist/database/KythiaMigrator.d.ts.map +1 -0
  59. package/dist/database/KythiaMigrator.js +94 -0
  60. package/dist/database/KythiaMigrator.js.map +1 -0
  61. package/dist/database/KythiaModel.d.ts +83 -0
  62. package/dist/database/KythiaModel.d.ts.map +1 -0
  63. package/dist/database/KythiaModel.js +1121 -0
  64. package/dist/database/KythiaModel.js.map +1 -0
  65. package/dist/database/KythiaSequelize.d.ts +4 -0
  66. package/dist/database/KythiaSequelize.d.ts.map +1 -0
  67. package/dist/database/KythiaSequelize.js +99 -0
  68. package/dist/database/KythiaSequelize.js.map +1 -0
  69. package/dist/database/KythiaStorage.d.ts +21 -0
  70. package/dist/database/KythiaStorage.d.ts.map +1 -0
  71. package/dist/database/KythiaStorage.js +80 -0
  72. package/dist/database/KythiaStorage.js.map +1 -0
  73. package/dist/database/ModelLoader.d.ts +4 -0
  74. package/dist/database/ModelLoader.d.ts.map +1 -0
  75. package/dist/database/ModelLoader.js +54 -0
  76. package/dist/database/ModelLoader.js.map +1 -0
  77. package/dist/index.d.ts +10 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +36 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/lang/en.json +85 -0
  82. package/dist/managers/AddonManager.d.ts +45 -0
  83. package/dist/managers/AddonManager.d.ts.map +1 -0
  84. package/dist/managers/AddonManager.js +932 -0
  85. package/dist/managers/AddonManager.js.map +1 -0
  86. package/dist/managers/EventManager.d.ts +19 -0
  87. package/dist/managers/EventManager.d.ts.map +1 -0
  88. package/dist/managers/EventManager.js +55 -0
  89. package/dist/managers/EventManager.js.map +1 -0
  90. package/dist/managers/InteractionManager.d.ts +41 -0
  91. package/dist/managers/InteractionManager.d.ts.map +1 -0
  92. package/dist/managers/InteractionManager.js +441 -0
  93. package/dist/managers/InteractionManager.js.map +1 -0
  94. package/dist/managers/MiddlewareManager.d.ts +14 -0
  95. package/dist/managers/MiddlewareManager.d.ts.map +1 -0
  96. package/dist/managers/MiddlewareManager.js +75 -0
  97. package/dist/managers/MiddlewareManager.js.map +1 -0
  98. package/dist/managers/ShutdownManager.d.ts +22 -0
  99. package/dist/managers/ShutdownManager.d.ts.map +1 -0
  100. package/dist/managers/ShutdownManager.js +151 -0
  101. package/dist/managers/ShutdownManager.js.map +1 -0
  102. package/dist/managers/TranslatorManager.d.ts +19 -0
  103. package/dist/managers/TranslatorManager.d.ts.map +1 -0
  104. package/dist/managers/TranslatorManager.js +118 -0
  105. package/dist/managers/TranslatorManager.js.map +1 -0
  106. package/dist/middlewares/botPermissions.d.ts +4 -0
  107. package/dist/middlewares/botPermissions.d.ts.map +1 -0
  108. package/dist/middlewares/botPermissions.js +28 -0
  109. package/dist/middlewares/botPermissions.js.map +1 -0
  110. package/dist/middlewares/cooldown.d.ts +4 -0
  111. package/dist/middlewares/cooldown.d.ts.map +1 -0
  112. package/dist/middlewares/cooldown.js +42 -0
  113. package/dist/middlewares/cooldown.js.map +1 -0
  114. package/dist/middlewares/isInMainGuild.d.ts +4 -0
  115. package/dist/middlewares/isInMainGuild.d.ts.map +1 -0
  116. package/dist/middlewares/isInMainGuild.js +52 -0
  117. package/dist/middlewares/isInMainGuild.js.map +1 -0
  118. package/dist/middlewares/ownerOnly.d.ts +4 -0
  119. package/dist/middlewares/ownerOnly.d.ts.map +1 -0
  120. package/dist/middlewares/ownerOnly.js +24 -0
  121. package/dist/middlewares/ownerOnly.js.map +1 -0
  122. package/dist/middlewares/teamOnly.d.ts +4 -0
  123. package/dist/middlewares/teamOnly.d.ts.map +1 -0
  124. package/dist/middlewares/teamOnly.js +26 -0
  125. package/dist/middlewares/teamOnly.js.map +1 -0
  126. package/dist/middlewares/userPermissions.d.ts +4 -0
  127. package/dist/middlewares/userPermissions.d.ts.map +1 -0
  128. package/dist/middlewares/userPermissions.js +28 -0
  129. package/dist/middlewares/userPermissions.js.map +1 -0
  130. package/dist/middlewares/voteLocked.d.ts +4 -0
  131. package/dist/middlewares/voteLocked.d.ts.map +1 -0
  132. package/dist/middlewares/voteLocked.js +50 -0
  133. package/dist/middlewares/voteLocked.js.map +1 -0
  134. package/dist/structures/BaseCommand.d.ts +23 -0
  135. package/dist/structures/BaseCommand.d.ts.map +1 -0
  136. package/dist/structures/BaseCommand.js +42 -0
  137. package/dist/structures/BaseCommand.js.map +1 -0
  138. package/dist/types/AddonManager.d.ts +58 -0
  139. package/dist/types/AddonManager.d.ts.map +1 -0
  140. package/dist/types/AddonManager.js +3 -0
  141. package/dist/types/AddonManager.js.map +1 -0
  142. package/dist/types/DiscordHelpers.d.ts +7 -0
  143. package/dist/types/DiscordHelpers.d.ts.map +1 -0
  144. package/dist/types/DiscordHelpers.js +3 -0
  145. package/dist/types/DiscordHelpers.js.map +1 -0
  146. package/dist/types/EventManager.d.ts +10 -0
  147. package/dist/types/EventManager.d.ts.map +1 -0
  148. package/dist/types/EventManager.js +3 -0
  149. package/dist/types/EventManager.js.map +1 -0
  150. package/dist/types/InteractionManager.d.ts +35 -0
  151. package/dist/types/InteractionManager.d.ts.map +1 -0
  152. package/dist/types/InteractionManager.js +3 -0
  153. package/dist/types/InteractionManager.js.map +1 -0
  154. package/dist/types/KythiaClient.d.ts +9 -0
  155. package/dist/types/KythiaClient.d.ts.map +1 -0
  156. package/dist/types/KythiaClient.js +3 -0
  157. package/dist/types/KythiaClient.js.map +1 -0
  158. package/dist/types/KythiaConfig.d.ts +291 -0
  159. package/dist/types/KythiaConfig.d.ts.map +1 -0
  160. package/dist/types/KythiaConfig.js +3 -0
  161. package/dist/types/KythiaConfig.js.map +1 -0
  162. package/dist/types/KythiaContainer.d.ts +38 -0
  163. package/dist/types/KythiaContainer.d.ts.map +1 -0
  164. package/dist/types/KythiaContainer.js +3 -0
  165. package/dist/types/KythiaContainer.js.map +1 -0
  166. package/dist/types/KythiaLogger.d.ts +5 -0
  167. package/dist/types/KythiaLogger.d.ts.map +1 -0
  168. package/dist/types/KythiaLogger.js +3 -0
  169. package/dist/types/KythiaLogger.js.map +1 -0
  170. package/dist/types/KythiaMigrator.d.ts +9 -0
  171. package/dist/types/KythiaMigrator.d.ts.map +1 -0
  172. package/dist/types/KythiaMigrator.js +3 -0
  173. package/dist/types/KythiaMigrator.js.map +1 -0
  174. package/dist/types/KythiaModel.d.ts +31 -0
  175. package/dist/types/KythiaModel.d.ts.map +1 -0
  176. package/dist/types/KythiaModel.js +3 -0
  177. package/dist/types/KythiaModel.js.map +1 -0
  178. package/dist/types/KythiaOptions.d.ts +13 -0
  179. package/dist/types/KythiaOptions.d.ts.map +1 -0
  180. package/dist/types/KythiaOptions.js +3 -0
  181. package/dist/types/KythiaOptions.js.map +1 -0
  182. package/dist/types/KythiaSequelize.d.ts +13 -0
  183. package/dist/types/KythiaSequelize.d.ts.map +1 -0
  184. package/dist/types/KythiaSequelize.js +3 -0
  185. package/dist/types/KythiaSequelize.js.map +1 -0
  186. package/dist/types/KythiaStorage.d.ts +22 -0
  187. package/dist/types/KythiaStorage.d.ts.map +1 -0
  188. package/dist/types/KythiaStorage.js +3 -0
  189. package/dist/types/KythiaStorage.js.map +1 -0
  190. package/dist/types/MiddlewareManager.d.ts +14 -0
  191. package/dist/types/MiddlewareManager.d.ts.map +1 -0
  192. package/dist/types/MiddlewareManager.js +3 -0
  193. package/dist/types/MiddlewareManager.js.map +1 -0
  194. package/dist/types/ModelLoader.d.ts +8 -0
  195. package/dist/types/ModelLoader.d.ts.map +1 -0
  196. package/dist/types/ModelLoader.js +3 -0
  197. package/dist/types/ModelLoader.js.map +1 -0
  198. package/dist/types/ShutdownManager.d.ts +15 -0
  199. package/dist/types/ShutdownManager.d.ts.map +1 -0
  200. package/dist/types/ShutdownManager.js +3 -0
  201. package/dist/types/ShutdownManager.js.map +1 -0
  202. package/dist/types/TranslatorManager.d.ts +16 -0
  203. package/dist/types/TranslatorManager.d.ts.map +1 -0
  204. package/dist/types/TranslatorManager.js +3 -0
  205. package/dist/types/TranslatorManager.js.map +1 -0
  206. package/dist/types/index.d.ts +13 -0
  207. package/dist/types/index.d.ts.map +1 -0
  208. package/dist/types/index.js +29 -0
  209. package/dist/types/index.js.map +1 -0
  210. package/dist/utils/color.d.ts +15 -0
  211. package/dist/utils/color.d.ts.map +1 -0
  212. package/dist/utils/color.js +156 -0
  213. package/dist/utils/color.js.map +1 -0
  214. package/dist/utils/discord.d.ts +8 -0
  215. package/dist/utils/discord.d.ts.map +1 -0
  216. package/dist/utils/discord.js +53 -0
  217. package/dist/utils/discord.js.map +1 -0
  218. package/dist/utils/formatter.d.ts +3 -0
  219. package/dist/utils/formatter.d.ts.map +1 -0
  220. package/dist/utils/formatter.js +89 -0
  221. package/dist/utils/formatter.js.map +1 -0
  222. package/dist/utils/index.d.ts +12 -0
  223. package/dist/utils/index.d.ts.map +1 -0
  224. package/dist/utils/index.js +54 -0
  225. package/dist/utils/index.js.map +1 -0
  226. package/dist/utils/logger.d.ts +5 -0
  227. package/dist/utils/logger.d.ts.map +1 -0
  228. package/dist/utils/logger.js +150 -0
  229. package/dist/utils/logger.js.map +1 -0
  230. package/package.json +28 -6
  231. package/src/lang/en.json +85 -0
  232. package/changelog.md +0 -53
  233. package/index.js +0 -15
  234. package/src/Kythia.js +0 -556
  235. package/src/KythiaClient.js +0 -94
  236. package/src/cli/Command.js +0 -68
  237. package/src/cli/commands/CacheClearCommand.js +0 -136
  238. package/src/cli/commands/LangCheckCommand.js +0 -367
  239. package/src/cli/commands/LangTranslateCommand.js +0 -336
  240. package/src/cli/commands/MakeMigrationCommand.js +0 -82
  241. package/src/cli/commands/MakeModelCommand.js +0 -81
  242. package/src/cli/commands/MigrateCommand.js +0 -259
  243. package/src/cli/commands/NamespaceCommand.js +0 -112
  244. package/src/cli/commands/StructureCommand.js +0 -70
  245. package/src/cli/commands/UpversionCommand.js +0 -94
  246. package/src/cli/index.js +0 -69
  247. package/src/cli/utils/db.js +0 -117
  248. package/src/database/KythiaMigrator.js +0 -116
  249. package/src/database/KythiaModel.js +0 -1557
  250. package/src/database/KythiaSequelize.js +0 -128
  251. package/src/database/KythiaStorage.js +0 -117
  252. package/src/database/ModelLoader.js +0 -79
  253. package/src/managers/AddonManager.js +0 -1219
  254. package/src/managers/EventManager.js +0 -104
  255. package/src/managers/InteractionManager.js +0 -815
  256. package/src/managers/ShutdownManager.js +0 -218
  257. package/src/structures/BaseCommand.js +0 -53
  258. package/src/utils/color.js +0 -180
  259. package/src/utils/formatter.js +0 -99
  260. package/src/utils/index.js +0 -4
@@ -1,815 +0,0 @@
1
- /**
2
- * 🎯 Interaction Manager
3
- *
4
- * @file src/managers/InteractionManager.js
5
- * @copyright © 2025 kenndeclouv
6
- * @assistant chaa & graa
7
- * @version 0.11.0-beta
8
- *
9
- * @description
10
- * Handles all Discord interaction events including slash commands, buttons, modals,
11
- * autocomplete, and context menu commands. Manages permissions, cooldowns, and error handling.
12
- */
13
-
14
- const {
15
- Events,
16
- Collection,
17
- ButtonStyle,
18
- MessageFlags,
19
- EmbedBuilder,
20
- ButtonBuilder,
21
- WebhookClient,
22
- SeparatorBuilder,
23
- ActionRowBuilder,
24
- ContainerBuilder,
25
- TextDisplayBuilder,
26
- SeparatorSpacingSize,
27
- } = require('discord.js');
28
- const convertColor = require('../utils/color');
29
- const Sentry = require('@sentry/node');
30
-
31
- class InteractionManager {
32
- /**
33
- * 🏗️ InteractionManager Constructor
34
- * @param {Object} client - Discord client instance
35
- * @param {Object} container - Dependency container
36
- * @param {Object} handlers - Handler maps from AddonManager
37
- */
38
- constructor({ client, container, handlers }) {
39
- this.client = client;
40
- this.container = container;
41
- this.buttonHandlers = handlers.buttonHandlers;
42
- this.modalHandlers = handlers.modalHandlers;
43
- this.selectMenuHandlers = handlers.selectMenuHandlers;
44
- this.autocompleteHandlers = handlers.autocompleteHandlers;
45
- this.commandCategoryMap = handlers.commandCategoryMap;
46
- this.categoryToFeatureMap = handlers.categoryToFeatureMap;
47
-
48
- this.kythiaConfig = this.container.kythiaConfig;
49
- this.models = this.container.models;
50
- this.helpers = this.container.helpers;
51
-
52
- this.logger = this.container.logger;
53
- this.t = this.container.t;
54
-
55
- this.ServerSetting = this.models.ServerSetting;
56
- this.KythiaVoter = this.models.KythiaVoter;
57
- this.isTeam = this.helpers.discord.isTeam;
58
- this.isOwner = this.helpers.discord.isOwner;
59
- }
60
-
61
- /**
62
- * 🛎️ Initialize Interaction Handler
63
- * Sets up the main Discord interaction handler for commands, autocomplete, buttons, and modals.
64
- */
65
- initialize() {
66
- function formatPerms(permsArray) {
67
- return permsArray
68
- .map((perm) => perm.replace(/([A-Z])/g, ' $1').trim())
69
- .join(', ');
70
- }
71
-
72
- this.client.on(Events.InteractionCreate, async (interaction) => {
73
- try {
74
- if (interaction.isChatInputCommand()) {
75
- await this._handleChatInputCommand(interaction, formatPerms);
76
- } else if (interaction.isAutocomplete()) {
77
- await this._handleAutocomplete(interaction);
78
- } else if (interaction.isButton()) {
79
- await this._handleButton(interaction);
80
- } else if (interaction.isModalSubmit()) {
81
- await this._handleModalSubmit(interaction);
82
- } else if (interaction.isAnySelectMenu()) {
83
- await this._handleSelectMenu(interaction);
84
- } else if (
85
- interaction.isUserContextMenuCommand() ||
86
- interaction.isMessageContextMenuCommand()
87
- ) {
88
- await this._handleContextMenuCommand(interaction, formatPerms);
89
- }
90
- } catch (error) {
91
- await this._handleInteractionError(interaction, error);
92
- }
93
- });
94
-
95
- this.client.on(Events.AutoModerationActionExecution, async (execution) => {
96
- try {
97
- await this._handleAutoModerationAction(execution);
98
- } catch (err) {
99
- this.logger.error(
100
- `[AutoMod Logger] Error during execution for ${execution.guild.name}:`,
101
- err,
102
- );
103
- }
104
- });
105
- }
106
-
107
- /**
108
- * Handle chat input commands
109
- * @private
110
- */
111
- async _handleChatInputCommand(interaction, formatPerms) {
112
- let commandKey = interaction.commandName;
113
- const group = interaction.options.getSubcommandGroup(false);
114
- const subcommand = interaction.options.getSubcommand(false);
115
-
116
- if (group) commandKey = `${commandKey} ${group} ${subcommand}`;
117
- else if (subcommand) commandKey = `${commandKey} ${subcommand}`;
118
-
119
- let command = this.client.commands.get(commandKey);
120
-
121
- if (!command && (subcommand || group)) {
122
- command = this.client.commands.get(interaction.commandName);
123
- }
124
- if (!command) {
125
- this.logger.error(`Command not found for key: ${commandKey}`);
126
- return interaction.reply({
127
- content: await this.t(interaction, 'common.error.command.not.found'),
128
- flags: MessageFlags.Ephemeral,
129
- });
130
- }
131
-
132
- if (interaction.inGuild()) {
133
- const category = this.commandCategoryMap.get(interaction.commandName);
134
- const featureFlag = this.categoryToFeatureMap.get(category);
135
-
136
- if (featureFlag && !this.isOwner(interaction.user.id)) {
137
- const settings = await this.ServerSetting.getCache({
138
- guildId: interaction.guild.id,
139
- });
140
-
141
- if (
142
- settings &&
143
- Object.hasOwn(settings, featureFlag) &&
144
- settings[featureFlag] === false
145
- ) {
146
- const featureName =
147
- category.charAt(0).toUpperCase() + category.slice(1);
148
- const reply = await this.t(
149
- interaction,
150
- 'common.error.feature.disabled',
151
- { feature: featureName },
152
- );
153
- return interaction.reply({ content: reply });
154
- }
155
- }
156
- }
157
-
158
- if (command.guildOnly && !interaction.inGuild()) {
159
- return interaction.reply({
160
- content: await this.t(interaction, 'common.error.guild.only'),
161
- flags: MessageFlags.Ephemeral,
162
- });
163
- }
164
- if (command.ownerOnly && !this.isOwner(interaction.user.id)) {
165
- return interaction.reply({
166
- content: await this.t(interaction, 'common.error.not.owner'),
167
- flags: MessageFlags.Ephemeral,
168
- });
169
- }
170
- if (command.teamOnly && !this.isOwner(interaction.user.id)) {
171
- const isTeamMember = await this.isTeam(interaction.user);
172
- if (!isTeamMember)
173
- return interaction.reply({
174
- content: await this.t(interaction, 'common.error.not.team'),
175
- flags: MessageFlags.Ephemeral,
176
- });
177
- }
178
- if (command.permissions && interaction.inGuild()) {
179
- const missingPerms = interaction.member.permissions.missing(
180
- command.permissions,
181
- );
182
- if (missingPerms.length > 0)
183
- return interaction.reply({
184
- content: await this.t(
185
- interaction,
186
- 'common.error.user.missing.perms',
187
- { perms: formatPerms(missingPerms) },
188
- ),
189
- flags: MessageFlags.Ephemeral,
190
- });
191
- }
192
- if (command.botPermissions && interaction.inGuild()) {
193
- const missingPerms = interaction.guild.members.me.permissions.missing(
194
- command.botPermissions,
195
- );
196
- if (missingPerms.length > 0)
197
- return interaction.reply({
198
- content: await this.t(interaction, 'common.error.bot.missing.perms', {
199
- perms: formatPerms(missingPerms),
200
- }),
201
- flags: MessageFlags.Ephemeral,
202
- });
203
- }
204
-
205
- if (command.voteLocked && !this.isOwner(interaction.user.id)) {
206
- const voter = await this.KythiaVoter.getCache({
207
- userId: interaction.user.id,
208
- });
209
- const twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000);
210
-
211
- if (!voter || voter.votedAt < twelveHoursAgo) {
212
- const container = new ContainerBuilder().setAccentColor(
213
- convertColor(this.kythiaConfig.bot.color, {
214
- from: 'hex',
215
- to: 'decimal',
216
- }),
217
- );
218
- container.addTextDisplayComponents(
219
- new TextDisplayBuilder().setContent(
220
- await this.t(interaction, 'common.error.vote.locked.text'),
221
- ),
222
- );
223
- container.addSeparatorComponents(
224
- new SeparatorBuilder()
225
- .setSpacing(SeparatorSpacingSize.Small)
226
- .setDivider(true),
227
- );
228
- container.addActionRowComponents(
229
- new ActionRowBuilder().addComponents(
230
- new ButtonBuilder()
231
- .setLabel(
232
- await this.t(interaction, 'common.error.vote.locked.button', {
233
- username: interaction.client.user.username,
234
- }),
235
- )
236
- .setStyle(ButtonStyle.Link)
237
- .setURL(
238
- `https://top.gg/bot/${this.kythiaConfig.bot.clientId}/vote`,
239
- ),
240
- ),
241
- );
242
- container.addSeparatorComponents(
243
- new SeparatorBuilder()
244
- .setSpacing(SeparatorSpacingSize.Small)
245
- .setDivider(true),
246
- );
247
- container.addTextDisplayComponents(
248
- new TextDisplayBuilder().setContent(
249
- await this.t(interaction, 'common.container.footer', {
250
- username: interaction.client.user.username,
251
- }),
252
- ),
253
- );
254
- return interaction.reply({
255
- components: [container],
256
- flags: MessageFlags.Ephemeral | MessageFlags.IsComponentsV2,
257
- });
258
- }
259
- }
260
-
261
- const cooldownDuration =
262
- command.cooldown ?? this.kythiaConfig.bot.globalCommandCooldown ?? 0;
263
-
264
- if (cooldownDuration > 0 && !this.isOwner(interaction.user.id)) {
265
- const { cooldowns } = this.client;
266
-
267
- if (!cooldowns.has(command.name)) {
268
- cooldowns.set(command.name, new Collection());
269
- }
270
-
271
- const now = Date.now();
272
- const timestamps = cooldowns.get(command.name);
273
- const cooldownAmount = cooldownDuration * 1000;
274
-
275
- if (timestamps.has(interaction.user.id)) {
276
- const expirationTime =
277
- timestamps.get(interaction.user.id) + cooldownAmount;
278
-
279
- if (now < expirationTime) {
280
- const timeLeft = (expirationTime - now) / 1000;
281
- const reply = await this.t(interaction, 'common.error.cooldown', {
282
- time: timeLeft.toFixed(1),
283
- });
284
- return interaction.reply({
285
- content: reply,
286
- flags: MessageFlags.Ephemeral,
287
- });
288
- }
289
- }
290
-
291
- timestamps.set(interaction.user.id, now);
292
- setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
293
- }
294
-
295
- if (typeof command.execute === 'function') {
296
- if (!interaction.logger) {
297
- interaction.logger = this.logger;
298
- }
299
-
300
- if (this.container && !this.container.logger) {
301
- this.container.logger = this.logger;
302
- }
303
- if (command.execute.length === 2) {
304
- await command.execute(interaction, this.container);
305
- } else {
306
- await command.execute(interaction);
307
- }
308
- } else {
309
- this.logger.error(
310
- "Command doesn't have a valid 'execute' function:",
311
- command.name || commandKey,
312
- );
313
- return interaction.reply({
314
- content: await this.t(
315
- interaction,
316
- 'common.error.command.execution.invalid',
317
- ),
318
- flags: MessageFlags.Ephemeral,
319
- });
320
- }
321
- }
322
-
323
- /**
324
- * Handle autocomplete interactions
325
- * @private
326
- */
327
- async _handleAutocomplete(interaction) {
328
- let commandKey = interaction.commandName;
329
- const group = interaction.options.getSubcommandGroup(false);
330
- const subcommand = interaction.options.getSubcommand(false);
331
-
332
- if (group) commandKey = `${commandKey} ${group} ${subcommand}`;
333
- else if (subcommand) commandKey = `${commandKey} ${subcommand}`;
334
-
335
- let handler = this.autocompleteHandlers.get(commandKey);
336
-
337
- if (!handler && (subcommand || group)) {
338
- handler = this.autocompleteHandlers.get(interaction.commandName);
339
- }
340
-
341
- if (handler) {
342
- try {
343
- await handler(interaction, this.container);
344
- } catch (err) {
345
- this.logger.error(
346
- `Error in autocomplete handler for ${commandKey}:`,
347
- err,
348
- );
349
- try {
350
- await interaction.respond([]);
351
- } catch (e) {
352
- this.logger.error(e);
353
- }
354
- }
355
- } else {
356
- try {
357
- await interaction.respond([]);
358
- } catch (e) {
359
- this.logger.error(e);
360
- }
361
- }
362
- }
363
-
364
- /**
365
- * Handle button interactions
366
- * @private
367
- */
368
- async _handleButton(interaction) {
369
- const customIdPrefix = interaction.customId.includes('|')
370
- ? interaction.customId.split('|')[0]
371
- : interaction.customId.split(':')[0];
372
-
373
- const handler = this.buttonHandlers.get(customIdPrefix);
374
-
375
- if (handler) {
376
- if (
377
- typeof handler === 'object' &&
378
- typeof handler.execute === 'function'
379
- ) {
380
- await handler.execute(interaction, this.container);
381
- } else if (typeof handler === 'function') {
382
- if (handler.length === 2) {
383
- await handler(interaction, this.container);
384
- } else {
385
- await handler(interaction);
386
- }
387
- } else {
388
- this.logger.error(
389
- `Handler for button ${customIdPrefix} has an invalid format`,
390
- );
391
- }
392
- }
393
- }
394
-
395
- /**
396
- * Handle modal submit interactions
397
- * @private
398
- */
399
- async _handleModalSubmit(interaction) {
400
- const customIdPrefix = interaction.customId.includes('|')
401
- ? interaction.customId.split('|')[0]
402
- : interaction.customId.split(':')[0];
403
-
404
- this.logger.info(
405
- `Modal submit - customId: ${interaction.customId}, prefix: ${customIdPrefix}`,
406
- );
407
-
408
- const handler = this.modalHandlers.get(customIdPrefix);
409
- this.logger.info(`Modal handler found: ${!!handler}`);
410
-
411
- if (handler) {
412
- if (
413
- typeof handler === 'object' &&
414
- typeof handler.execute === 'function'
415
- ) {
416
- await handler.execute(interaction, this.container);
417
- } else if (typeof handler === 'function') {
418
- if (handler.length === 2) {
419
- await handler(interaction, this.container);
420
- } else {
421
- await handler(interaction);
422
- }
423
- } else {
424
- this.logger.error(
425
- `Handler untuk modal ${customIdPrefix} formatnya salah (bukan fungsi atau { execute: ... })`,
426
- );
427
- }
428
- }
429
- }
430
-
431
- /**
432
- * Handle select menu interactions
433
- * @private
434
- */
435
- async _handleSelectMenu(interaction) {
436
- const customIdPrefix = interaction.customId.includes('|')
437
- ? interaction.customId.split('|')[0]
438
- : interaction.customId.split(':')[0];
439
-
440
- this.logger.info(
441
- `Select menu submit - customId: ${interaction.customId}, prefix: ${customIdPrefix}`,
442
- );
443
-
444
- const handler = this.selectMenuHandlers.get(customIdPrefix);
445
- this.logger.info(`Select menu handler found: ${!!handler}`);
446
-
447
- if (handler) {
448
- if (
449
- typeof handler === 'object' &&
450
- typeof handler.execute === 'function'
451
- ) {
452
- await handler.execute(interaction, this.container);
453
- } else if (typeof handler === 'function') {
454
- if (handler.length === 2) {
455
- await handler(interaction, this.container);
456
- } else {
457
- await handler(interaction);
458
- }
459
- } else {
460
- this.logger.error(
461
- `Handler untuk select menu ${customIdPrefix} formatnya salah`,
462
- );
463
- }
464
- }
465
- }
466
-
467
- /**
468
- * Handle context menu commands
469
- * @private
470
- */
471
- async _handleContextMenuCommand(interaction, formatPerms) {
472
- const command = this.client.commands.get(interaction.commandName);
473
- if (!command) return;
474
-
475
- if (command.guildOnly && !interaction.inGuild()) {
476
- return interaction.reply({
477
- content: await this.t(interaction, 'common.error.guild.only'),
478
- flags: MessageFlags.Ephemeral,
479
- });
480
- }
481
- if (command.ownerOnly && !this.isOwner(interaction.user.id)) {
482
- return interaction.reply({
483
- content: await this.t(interaction, 'common.error.not.owner'),
484
- flags: MessageFlags.Ephemeral,
485
- });
486
- }
487
- if (command.teamOnly && !this.isOwner(interaction.user.id)) {
488
- const isTeamMember = await this.isTeam(interaction.user);
489
- if (!isTeamMember)
490
- return interaction.reply({
491
- content: await this.t(interaction, 'common.error.not.team'),
492
- flags: MessageFlags.Ephemeral,
493
- });
494
- }
495
- if (command.permissions && interaction.inGuild()) {
496
- const missingPerms = interaction.member.permissions.missing(
497
- command.permissions,
498
- );
499
- if (missingPerms.length > 0)
500
- return interaction.reply({
501
- content: await this.t(
502
- interaction,
503
- 'common.error.user.missing.perms',
504
- { perms: formatPerms(missingPerms) },
505
- ),
506
- flags: MessageFlags.Ephemeral,
507
- });
508
- }
509
- if (command.botPermissions && interaction.inGuild()) {
510
- const missingPerms = interaction.guild.members.me.permissions.missing(
511
- command.botPermissions,
512
- );
513
- if (missingPerms.length > 0)
514
- return interaction.reply({
515
- content: await this.t(interaction, 'common.error.bot.missing.perms', {
516
- perms: formatPerms(missingPerms),
517
- }),
518
- flags: MessageFlags.Ephemeral,
519
- });
520
- }
521
- if (command.isInMainGuild && !this.isOwner(interaction.user.id)) {
522
- const mainGuild = this.client.guilds.cache.get(
523
- this.kythiaConfig.bot.mainGuildId,
524
- );
525
- if (!mainGuild) {
526
- this.logger.error(
527
- `❌ [isInMainGuild Check] Error: Bot is not a member of the main guild specified in config: ${this.kythiaConfig.bot.mainGuildId}`,
528
- );
529
- }
530
- try {
531
- await mainGuild.members.fetch(interaction.user.id);
532
- } catch (error) {
533
- const container = new ContainerBuilder().setAccentColor(
534
- convertColor(this.kythiaConfig.bot.color, {
535
- from: 'hex',
536
- to: 'decimal',
537
- }),
538
- );
539
- container.addTextDisplayComponents(
540
- new TextDisplayBuilder().setContent(
541
- await this.t(interaction, 'common.error.not.in.main.guild.text', {
542
- name: mainGuild.name,
543
- }),
544
- ),
545
- );
546
- container.addSeparatorComponents(
547
- new SeparatorBuilder()
548
- .setSpacing(SeparatorSpacingSize.Small)
549
- .setDivider(true),
550
- );
551
- container.addActionRowComponents(
552
- new ActionRowBuilder().addComponents(
553
- new ButtonBuilder()
554
- .setLabel(
555
- await this.t(
556
- interaction,
557
- 'common.error.not.in.main.guild.button.join',
558
- ),
559
- )
560
- .setStyle(ButtonStyle.Link)
561
- .setURL(this.kythiaConfig.settings.supportServer),
562
- ),
563
- );
564
- container.addTextDisplayComponents(
565
- new TextDisplayBuilder().setContent(
566
- await this.t(interaction, 'common.container.footer', {
567
- username: interaction.client.user.username,
568
- }),
569
- ),
570
- );
571
- this.logger.error(error);
572
- return interaction.reply({
573
- components: [container],
574
- flags: MessageFlags.IsPersistent | MessageFlags.IsComponentsV2,
575
- });
576
- }
577
- }
578
- if (command.voteLocked && !this.isOwner(interaction.user.id)) {
579
- const voter = await this.KythiaVoter.getCache({
580
- userId: interaction.user.id,
581
- });
582
- const twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000);
583
-
584
- if (!voter || voter.votedAt < twelveHoursAgo) {
585
- const container = new ContainerBuilder().setAccentColor(
586
- convertColor(this.kythiaConfig.bot.color, {
587
- from: 'hex',
588
- to: 'decimal',
589
- }),
590
- );
591
- container.addTextDisplayComponents(
592
- new TextDisplayBuilder().setContent(
593
- await this.t(interaction, 'common.error.vote.locked.text'),
594
- ),
595
- );
596
- container.addSeparatorComponents(
597
- new SeparatorBuilder()
598
- .setSpacing(SeparatorSpacingSize.Small)
599
- .setDivider(true),
600
- );
601
- container.addActionRowComponents(
602
- new ActionRowBuilder().addComponents(
603
- new ButtonBuilder()
604
- .setLabel(
605
- await this.t(interaction, 'common.error.vote.locked.button', {
606
- username: interaction.client.user.username,
607
- }),
608
- )
609
- .setStyle(ButtonStyle.Link)
610
- .setURL(
611
- `https://top.gg/bot/${this.kythiaConfig.bot.clientId}/vote`,
612
- ),
613
- ),
614
- );
615
- container.addSeparatorComponents(
616
- new SeparatorBuilder()
617
- .setSpacing(SeparatorSpacingSize.Small)
618
- .setDivider(true),
619
- );
620
- container.addTextDisplayComponents(
621
- new TextDisplayBuilder().setContent(
622
- await this.t(interaction, 'common.container.footer', {
623
- username: interaction.client.user.username,
624
- }),
625
- ),
626
- );
627
- return interaction.reply({
628
- components: [container],
629
- flags: MessageFlags.Ephemeral | MessageFlags.IsComponentsV2,
630
- });
631
- }
632
- }
633
-
634
- if (!interaction.logger) {
635
- interaction.logger = this.logger;
636
- }
637
- if (this.container && !this.container.logger) {
638
- this.container.logger = this.logger;
639
- }
640
- await command.execute(interaction, this.container);
641
- }
642
-
643
- /**
644
- * Handle AutoModeration action execution
645
- * @private
646
- */
647
- async _handleAutoModerationAction(execution) {
648
- const guildId = execution.guild.id;
649
- const ruleName = execution.ruleTriggerType.toString();
650
-
651
- const settings = await this.ServerSetting.getCache({ guildId: guildId });
652
- const locale = execution.guild.preferredLocale;
653
-
654
- if (!settings || !settings.modLogChannelId) {
655
- return;
656
- }
657
-
658
- const logChannelId = settings.modLogChannelId;
659
- const logChannel = await execution.guild.channels
660
- .fetch(logChannelId)
661
- .catch(() => null);
662
-
663
- if (logChannel) {
664
- const embed = new EmbedBuilder()
665
- .setColor('Red')
666
- .setDescription(
667
- await this.t(
668
- null,
669
- 'common.automod',
670
- {
671
- ruleName: ruleName,
672
- },
673
- locale,
674
- ),
675
- )
676
- .addFields(
677
- {
678
- name: await this.t(null, 'common.automod.field.user', {}, locale),
679
- value: `${execution.user.tag} (${execution.userId})`,
680
- inline: true,
681
- },
682
- {
683
- name: await this.t(
684
- null,
685
- 'common.automod.field.rule.trigger',
686
- {},
687
- locale,
688
- ),
689
- value: `\`${ruleName}\``,
690
- inline: true,
691
- },
692
- )
693
- .setFooter({
694
- text: await this.t(
695
- null,
696
- 'common.embed.footer',
697
- {
698
- username: execution.guild.client.user.username,
699
- },
700
- locale,
701
- ),
702
- })
703
- .setTimestamp();
704
-
705
- await logChannel.send({ embeds: [embed] });
706
- }
707
- }
708
-
709
- /**
710
- * Handle interaction errors
711
- * @private
712
- */
713
- async _handleInteractionError(interaction, error) {
714
- this.logger.error(
715
- `Error in interaction handler for ${interaction.user.tag}:`,
716
- error,
717
- );
718
-
719
- if (this.kythiaConfig.sentry?.dsn) {
720
- Sentry.withScope((scope) => {
721
- scope.setUser({
722
- id: interaction.user.id,
723
- username: interaction.user.tag,
724
- });
725
- scope.setTag('command', interaction.commandName);
726
- if (interaction.guild) {
727
- scope.setContext('guild', {
728
- id: interaction.guild.id,
729
- name: interaction.guild.name,
730
- });
731
- }
732
- Sentry.captureException(error);
733
- });
734
- }
735
-
736
- const ownerFirstId = this.kythiaConfig.owner.ids.split(',')[0].trim();
737
- const components = [
738
- new ContainerBuilder()
739
- .setAccentColor(convertColor('Red', { from: 'discord', to: 'decimal' }))
740
- .addTextDisplayComponents(
741
- new TextDisplayBuilder().setContent(
742
- await this.t(interaction, 'common.error.generic'),
743
- ),
744
- )
745
- .addSeparatorComponents(
746
- new SeparatorBuilder()
747
- .setSpacing(SeparatorSpacingSize.Small)
748
- .setDivider(true),
749
- )
750
- .addActionRowComponents(
751
- new ActionRowBuilder().addComponents(
752
- new ButtonBuilder()
753
- .setStyle(ButtonStyle.Link)
754
- .setLabel(
755
- await this.t(
756
- interaction,
757
- 'common.error.button.join.support.server',
758
- ),
759
- )
760
- .setURL(this.kythiaConfig.settings.supportServer),
761
- new ButtonBuilder()
762
- .setStyle(ButtonStyle.Link)
763
- .setLabel(
764
- await this.t(interaction, 'common.error.button.contact.owner'),
765
- )
766
- .setURL(`https://discord.com/users/${ownerFirstId}`),
767
- ),
768
- ),
769
- ];
770
- try {
771
- if (interaction.replied || interaction.deferred) {
772
- await interaction.followUp({
773
- components,
774
- flags: MessageFlags.Ephemeral | MessageFlags.IsComponentsV2,
775
- });
776
- } else {
777
- await interaction.reply({
778
- components,
779
- flags: MessageFlags.Ephemeral | MessageFlags.IsComponentsV2,
780
- });
781
- }
782
- } catch (e) {
783
- this.logger.error('Failed to send interaction error message:', e);
784
- }
785
-
786
- try {
787
- if (
788
- this.kythiaConfig.api?.webhookErrorLogs &&
789
- this.kythiaConfig.settings &&
790
- this.kythiaConfig.settings.webhookErrorLogs === true
791
- ) {
792
- const webhookClient = new WebhookClient({
793
- url: this.kythiaConfig.api.webhookErrorLogs,
794
- });
795
- const errorEmbed = new EmbedBuilder()
796
- .setColor('Red')
797
- .setDescription(
798
- `## ❌ Error at ${interaction.user.tag}\n` +
799
- `\`\`\`${error.stack}\`\`\``,
800
- )
801
- .setFooter({
802
- text: interaction.guild
803
- ? `Error from server ${interaction.guild.name}`
804
- : 'Error from DM',
805
- })
806
- .setTimestamp();
807
- await webhookClient.send({ embeds: [errorEmbed] });
808
- }
809
- } catch (webhookErr) {
810
- this.logger.error('Error sending interaction error webhook:', webhookErr);
811
- }
812
- }
813
- }
814
-
815
- module.exports = InteractionManager;