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