kythia-core 0.9.3-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/LICENSE +409 -0
- package/README.md +307 -0
- package/index.js +17 -0
- package/package.json +46 -0
- package/src/Kythia.js +430 -0
- package/src/KythiaClient.js +53 -0
- package/src/database/KythiaModel.js +948 -0
- package/src/database/KythiaORM.js +481 -0
- package/src/database/KythiaSequelize.js +94 -0
- package/src/managers/AddonManager.js +954 -0
- package/src/managers/EventManager.js +99 -0
- package/src/managers/InteractionManager.js +553 -0
- package/src/managers/ShutdownManager.js +197 -0
- package/src/structures/BaseCommand.js +49 -0
- package/src/utils/color.js +176 -0
- package/src/utils/formatter.js +99 -0
- package/src/utils/index.js +4 -0
package/src/Kythia.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🤖 Main Kythia Entrypoint
|
|
3
|
+
*
|
|
4
|
+
* @file src/Kythia.js
|
|
5
|
+
* @copyright © 2025 kenndeclouv
|
|
6
|
+
* @assistant chaa & graa
|
|
7
|
+
* @version 0.9.3-beta
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* This file contains the main Bot class - acting as an orchestrator (CEO) that
|
|
11
|
+
* initializes and coordinates specialized managers for different responsibilities.
|
|
12
|
+
*
|
|
13
|
+
* ✨ Core Features:
|
|
14
|
+
* - Orchestrates AddonManager, InteractionManager, EventManager, and ShutdownManager
|
|
15
|
+
* - REST API setup for command deployment
|
|
16
|
+
* - Integration with client and database
|
|
17
|
+
* - Manages dependencies through container pattern
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { REST, Routes, Collection } = require('discord.js');
|
|
21
|
+
const KythiaORM = require('./database/KythiaORM');
|
|
22
|
+
const KythiaClient = require('./KythiaClient');
|
|
23
|
+
const Sentry = require('@sentry/node');
|
|
24
|
+
const figlet = require('figlet');
|
|
25
|
+
|
|
26
|
+
const InteractionManager = require('./managers/InteractionManager');
|
|
27
|
+
const ShutdownManager = require('./managers/ShutdownManager');
|
|
28
|
+
const AddonManager = require('./managers/AddonManager');
|
|
29
|
+
const EventManager = require('./managers/EventManager');
|
|
30
|
+
|
|
31
|
+
class Kythia {
|
|
32
|
+
/**
|
|
33
|
+
* 🏗️ Kythia Constructor
|
|
34
|
+
* Initializes the Discord client, REST API, and dependency container.
|
|
35
|
+
* Sets up manager instances (but doesn't start them yet).
|
|
36
|
+
*/
|
|
37
|
+
constructor({ config, logger, redis, sequelize, translator, models, helpers, utils, appRoot }) {
|
|
38
|
+
const missingDeps = [];
|
|
39
|
+
if (!config) missingDeps.push('config');
|
|
40
|
+
if (!logger) missingDeps.push('logger');
|
|
41
|
+
if (!translator) {
|
|
42
|
+
missingDeps.push('translator');
|
|
43
|
+
} else {
|
|
44
|
+
if (!translator.t) missingDeps.push('translator.t');
|
|
45
|
+
if (!translator.loadLocales) missingDeps.push('translator.loadLocales');
|
|
46
|
+
}
|
|
47
|
+
if (missingDeps.length > 0) {
|
|
48
|
+
console.error(`FATAL: Missing required dependencies: ${missingDeps.join(', ')}.`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
this.kythiaConfig = config;
|
|
52
|
+
this.appRoot = appRoot || process.cwd();
|
|
53
|
+
|
|
54
|
+
this.client = KythiaClient();
|
|
55
|
+
this.client.commands = new Collection();
|
|
56
|
+
this.rest = new REST({ version: '10' }).setToken(this.kythiaConfig.bot.token);
|
|
57
|
+
|
|
58
|
+
this.models = models;
|
|
59
|
+
this.helpers = helpers;
|
|
60
|
+
this.utils = utils;
|
|
61
|
+
|
|
62
|
+
this.redis = redis;
|
|
63
|
+
this.sequelize = sequelize;
|
|
64
|
+
|
|
65
|
+
this.logger = logger;
|
|
66
|
+
this.translator = translator;
|
|
67
|
+
|
|
68
|
+
this.container = {
|
|
69
|
+
client: this.client,
|
|
70
|
+
sequelize: this.sequelize,
|
|
71
|
+
logger: this.logger,
|
|
72
|
+
t: this.translator.t,
|
|
73
|
+
redis: this.redis,
|
|
74
|
+
kythiaConfig: this.kythiaConfig,
|
|
75
|
+
translator: this.translator,
|
|
76
|
+
|
|
77
|
+
models: this.models,
|
|
78
|
+
helpers: this.helpers,
|
|
79
|
+
appRoot: this.appRoot,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this.client.container = this.container;
|
|
83
|
+
this.client.cooldowns = new Collection();
|
|
84
|
+
|
|
85
|
+
this.dbReadyHooks = [];
|
|
86
|
+
this.clientReadyHooks = [];
|
|
87
|
+
|
|
88
|
+
this.addonManager = null;
|
|
89
|
+
this.interactionManager = null;
|
|
90
|
+
this.eventManager = null;
|
|
91
|
+
this.shutdownManager = null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 🔍 Check Required Config
|
|
96
|
+
* Checks if all required configurations are set.
|
|
97
|
+
* Throws an error if any required config is missing.
|
|
98
|
+
*/
|
|
99
|
+
_checkRequiredConfig() {
|
|
100
|
+
const requiredConfig = [
|
|
101
|
+
['bot', 'token'],
|
|
102
|
+
['bot', 'clientId'],
|
|
103
|
+
['bot', 'clientSecret'],
|
|
104
|
+
['db', 'driver'],
|
|
105
|
+
['db', 'host'],
|
|
106
|
+
['db', 'port'],
|
|
107
|
+
['db', 'name'],
|
|
108
|
+
['db', 'user'],
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const missingConfigs = [];
|
|
112
|
+
|
|
113
|
+
for (const pathArr of requiredConfig) {
|
|
114
|
+
let value = this.kythiaConfig;
|
|
115
|
+
for (const key of pathArr) {
|
|
116
|
+
value = value?.[key];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (value === undefined || value === null || value === '') {
|
|
120
|
+
missingConfigs.push(pathArr.join('.'));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (missingConfigs.length > 0) {
|
|
125
|
+
this.logger.error('❌ Required configurations are not set:');
|
|
126
|
+
for (const missing of missingConfigs) {
|
|
127
|
+
this.logger.error(` - ${missing}`);
|
|
128
|
+
}
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.logger.info('✔️ All required configurations are set');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 🔘 Register Button Handler
|
|
137
|
+
* Delegates to AddonManager
|
|
138
|
+
* @param {string} customId - The customId of the button
|
|
139
|
+
* @param {Function} handler - The handler function to execute
|
|
140
|
+
*/
|
|
141
|
+
registerButtonHandler(customId, handler) {
|
|
142
|
+
if (this.addonManager) {
|
|
143
|
+
this.addonManager.registerButtonHandler(customId, handler);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 📝 Register Modal Handler
|
|
149
|
+
* Delegates to AddonManager
|
|
150
|
+
* @param {string} customIdPrefix - The prefix of the modal customId
|
|
151
|
+
* @param {Function} handler - The handler function to execute
|
|
152
|
+
*/
|
|
153
|
+
registerModalHandler(customIdPrefix, handler) {
|
|
154
|
+
if (this.addonManager) {
|
|
155
|
+
this.addonManager.registerModalHandler(customIdPrefix, handler);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 🛡️ Validate License (Stub)
|
|
161
|
+
* Placeholder for license validation logic for addons.
|
|
162
|
+
* @param {string} licenseKey - The license key to validate
|
|
163
|
+
* @param {string} addonName - The name of the addon
|
|
164
|
+
* @returns {Promise<boolean>} Always returns true (stub)
|
|
165
|
+
*/
|
|
166
|
+
async _validateLicense(licenseKey, addonName) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 🚀 Deploy Commands to Discord
|
|
172
|
+
* Deploys all registered slash commands to Discord using the REST API.
|
|
173
|
+
* @param {Array} commands - Array of command data to deploy
|
|
174
|
+
*/
|
|
175
|
+
async _deployCommands(commands) {
|
|
176
|
+
if (!commands || commands.length === 0) {
|
|
177
|
+
this.logger.info('No commands to deploy.');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const { slash, user, message } = this._getCommandCounts(commands);
|
|
182
|
+
const clientId = this.kythiaConfig.bot.clientId;
|
|
183
|
+
const devGuildId = this.kythiaConfig.bot.devGuildId;
|
|
184
|
+
|
|
185
|
+
let deployType = '';
|
|
186
|
+
if (this.kythiaConfig.env == 'dev' || this.kythiaConfig.env == 'development') {
|
|
187
|
+
if (!devGuildId) {
|
|
188
|
+
this.logger.warn('⚠️ devGuildId not set in config. Skipping guild command deployment.');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
this.logger.info(`🟠 Deploying to GUILD ${devGuildId}...`);
|
|
192
|
+
await this.rest.put(Routes.applicationGuildCommands(clientId, devGuildId), { body: commands });
|
|
193
|
+
this.logger.info('✅ Guild commands deployed instantly!');
|
|
194
|
+
deployType = `Guild (${devGuildId})`;
|
|
195
|
+
} else {
|
|
196
|
+
this.logger.info(`🟢 Deploying globally...`);
|
|
197
|
+
await this.rest.put(Routes.applicationCommands(clientId), { body: commands });
|
|
198
|
+
this.logger.info('✅ Global commands deployed successfully!');
|
|
199
|
+
if (devGuildId) {
|
|
200
|
+
this.logger.info(`🧹 Clearing old commands from dev guild: ${devGuildId}...`);
|
|
201
|
+
try {
|
|
202
|
+
await this.rest.put(Routes.applicationGuildCommands(clientId, devGuildId), { body: [] });
|
|
203
|
+
this.logger.info('✅ Dev guild commands cleared successfully.');
|
|
204
|
+
} catch (err) {
|
|
205
|
+
this.logger.warn(`⚠️ Could not clear dev guild commands (maybe it was already clean): ${err.message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
deployType = 'Global';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.logger.info(`⭕ All Slash Commands: ${commands.length}`);
|
|
212
|
+
this.logger.info(`⭕ Top Level Slash Commands: ${slash}`);
|
|
213
|
+
this.logger.info(`⭕ User Context Menu: ${user}`);
|
|
214
|
+
this.logger.info(`⭕ Message Context Menu: ${message}`);
|
|
215
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
216
|
+
} catch (err) {
|
|
217
|
+
this.logger.error('❌ Failed to deploy slash commands:', err);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 🧮 Count command types from JSON array
|
|
223
|
+
* @param {Array} commandJsonArray - Array command data to be deployed
|
|
224
|
+
* @returns {object} - Object containing counts { slash, user, message }
|
|
225
|
+
* @private
|
|
226
|
+
*/
|
|
227
|
+
_getCommandCounts(commandJsonArray) {
|
|
228
|
+
const counts = { slash: 0, user: 0, message: 0 };
|
|
229
|
+
|
|
230
|
+
if (!Array.isArray(commandJsonArray)) {
|
|
231
|
+
this.logger.warn('commandJsonArray is not iterable. Returning zero counts.');
|
|
232
|
+
return counts;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const cmd of commandJsonArray) {
|
|
236
|
+
switch (cmd?.type) {
|
|
237
|
+
case 1:
|
|
238
|
+
case undefined:
|
|
239
|
+
counts.slash++;
|
|
240
|
+
break;
|
|
241
|
+
case 2:
|
|
242
|
+
counts.user++;
|
|
243
|
+
break;
|
|
244
|
+
case 3:
|
|
245
|
+
counts.message++;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return counts;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Adds a callback to be executed when the database is ready.
|
|
254
|
+
* The callback will be executed after all database models have been synchronized.
|
|
255
|
+
* @param {function} callback - Callback to be executed when the database is ready
|
|
256
|
+
*/
|
|
257
|
+
addDbReadyHook(callback) {
|
|
258
|
+
this.dbReadyHooks.push(callback);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Adds a callback to be executed when the client is ready.
|
|
263
|
+
* @param {function} callback - Callback to be executed when the client is ready
|
|
264
|
+
*/
|
|
265
|
+
addClientReadyHook(callback) {
|
|
266
|
+
this.clientReadyHooks.push(callback);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 🌸 Start the Kythia Bot
|
|
271
|
+
* Main orchestration method that:
|
|
272
|
+
* 1. Initializes Redis cache
|
|
273
|
+
* 2. Creates and starts all managers
|
|
274
|
+
* 3. Loads addons via AddonManager
|
|
275
|
+
* 4. Initializes database
|
|
276
|
+
* 5. Sets up interaction and event handlers
|
|
277
|
+
* 6. Deploys commands
|
|
278
|
+
* 7. Logs in to Discord
|
|
279
|
+
*/
|
|
280
|
+
async start() {
|
|
281
|
+
const version = require('../package.json').version;
|
|
282
|
+
const clc = require('cli-color');
|
|
283
|
+
const figletText = (text, opts) =>
|
|
284
|
+
new Promise((resolve, reject) => {
|
|
285
|
+
figlet.text(text, opts, (err, data) => {
|
|
286
|
+
if (err) reject(err);
|
|
287
|
+
else resolve(data);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const data = await figletText('KYTHIA', {
|
|
293
|
+
font: 'ANSI Shadow',
|
|
294
|
+
horizontalLayout: 'full',
|
|
295
|
+
verticalLayout: 'full',
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const infoLines = [
|
|
299
|
+
clc.cyan('Created by kenndeclouv'),
|
|
300
|
+
clc.cyan('Discord Support: ') + clc.underline('https://dsc.gg/kythia'),
|
|
301
|
+
clc.cyan('Official Documentation: ') + clc.underline('https://kythia.my.id/commands'),
|
|
302
|
+
'',
|
|
303
|
+
clc.cyanBright(`Kythia version: ${version}`),
|
|
304
|
+
'',
|
|
305
|
+
clc.yellowBright('Respect my work by not removing the credit'),
|
|
306
|
+
];
|
|
307
|
+
|
|
308
|
+
const rawInfoLines = infoLines.map((line) => clc.strip(line));
|
|
309
|
+
const infoMaxLen = Math.max(...rawInfoLines.map((l) => l.length));
|
|
310
|
+
const pad = 8;
|
|
311
|
+
const borderWidth = infoMaxLen + pad * 2;
|
|
312
|
+
const borderChar = clc.cyanBright('═');
|
|
313
|
+
const sideChar = clc.cyanBright('║');
|
|
314
|
+
const topBorder = clc.cyanBright('╔' + borderChar.repeat(borderWidth) + '╗');
|
|
315
|
+
const bottomBorder = clc.cyanBright('╚' + borderChar.repeat(borderWidth) + '╝');
|
|
316
|
+
const emptyLine = sideChar + ' '.repeat(borderWidth) + sideChar;
|
|
317
|
+
|
|
318
|
+
const figletLines = data.split('\n');
|
|
319
|
+
const centeredFigletInBorder = figletLines
|
|
320
|
+
.map((line) => {
|
|
321
|
+
const rawLen = clc.strip(line).length;
|
|
322
|
+
const spaces = ' '.repeat(Math.max(0, Math.floor((borderWidth - rawLen) / 2)));
|
|
323
|
+
return sideChar + spaces + clc.cyanBright(line) + ' '.repeat(borderWidth - spaces.length - rawLen) + sideChar;
|
|
324
|
+
})
|
|
325
|
+
.join('\n');
|
|
326
|
+
|
|
327
|
+
const centeredInfo = infoLines
|
|
328
|
+
.map((line, idx) => {
|
|
329
|
+
const raw = rawInfoLines[idx];
|
|
330
|
+
const spaces = ' '.repeat(Math.floor((borderWidth - raw.length) / 2));
|
|
331
|
+
return sideChar + spaces + line + ' '.repeat(borderWidth - spaces.length - raw.length) + sideChar;
|
|
332
|
+
})
|
|
333
|
+
.join('\n');
|
|
334
|
+
|
|
335
|
+
console.log('\n' + topBorder);
|
|
336
|
+
console.log(emptyLine);
|
|
337
|
+
console.log(centeredFigletInBorder);
|
|
338
|
+
console.log(emptyLine);
|
|
339
|
+
console.log(centeredInfo);
|
|
340
|
+
console.log(emptyLine);
|
|
341
|
+
console.log(bottomBorder + '\n');
|
|
342
|
+
} catch (err) {
|
|
343
|
+
this.logger.error('❌ Failed to render figlet banner:', err);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this.logger.info('🚀 Starting kythia...');
|
|
347
|
+
|
|
348
|
+
if (this.kythiaConfig.sentry.dsn) {
|
|
349
|
+
Sentry.init({
|
|
350
|
+
dsn: this.kythiaConfig.sentry.dsn,
|
|
351
|
+
tracesSampleRate: 1.0,
|
|
352
|
+
profilesSampleRate: 1.0,
|
|
353
|
+
});
|
|
354
|
+
this.logger.info('✔️ Sentry Error Tracking is ACTIVE');
|
|
355
|
+
} else {
|
|
356
|
+
this.logger.warn('🟠 Sentry DSN not found in config. Error tracking is INACTIVE.');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this._checkRequiredConfig();
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const shouldDeploy = process.argv.includes('--deploy');
|
|
363
|
+
|
|
364
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬[ Load Locales & Fonts ]▬▬▬▬▬▬▬▬▬▬▬');
|
|
365
|
+
this.translator.loadLocales();
|
|
366
|
+
if (this.helpers && this.helpers.fonts && typeof this.helpers.fonts.loadFonts === 'function') {
|
|
367
|
+
this.helpers.fonts.loadFonts({ logger: this.logger });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Initialize Cache ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
371
|
+
|
|
372
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Kythia Addons ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
373
|
+
this.addonManager = new AddonManager({ client: this.client, container: this.container });
|
|
374
|
+
const allCommands = await this.addonManager.loadAddons(this);
|
|
375
|
+
|
|
376
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Load KythiaORM ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
377
|
+
const sequelize = await KythiaORM({
|
|
378
|
+
kythiaInstance: this,
|
|
379
|
+
sequelize: this.sequelize,
|
|
380
|
+
KythiaModel: this.dbDependencies.KythiaModel,
|
|
381
|
+
logger: this.dbDependencies.logger,
|
|
382
|
+
config: this.dbDependencies.config,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
this.logger.info('🔄 Hydrating container with initialized models...');
|
|
386
|
+
this.container.models = sequelize.models;
|
|
387
|
+
|
|
388
|
+
const handlers = this.addonManager.getHandlers();
|
|
389
|
+
this.eventManager = new EventManager({ client: this.client, container: this.container, eventHandlers: handlers.eventHandlers });
|
|
390
|
+
this.eventManager.initialize();
|
|
391
|
+
|
|
392
|
+
this.interactionManager = new InteractionManager({ client: this.client, container: this.container, handlers: handlers });
|
|
393
|
+
this.interactionManager.initialize();
|
|
394
|
+
|
|
395
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬[ Deploy Commands ]▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
396
|
+
if (shouldDeploy) {
|
|
397
|
+
await this._deployCommands(allCommands);
|
|
398
|
+
} else {
|
|
399
|
+
this.logger.info('⏭️ Skipping command deployment. Use --deploy flag to force update.');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
this.shutdownManager = new ShutdownManager({ client: this.client, container: this.container });
|
|
403
|
+
this.shutdownManager.initialize();
|
|
404
|
+
|
|
405
|
+
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬[ Systems Initializing ]▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
406
|
+
|
|
407
|
+
this.client.once('clientReady', async (c) => {
|
|
408
|
+
this.logger.info(`🌸 Logged in as ${this.client.user.tag}`);
|
|
409
|
+
this.logger.info(`🚀 Executing ${this.clientReadyHooks.length} client-ready hooks...`);
|
|
410
|
+
for (const hook of this.clientReadyHooks) {
|
|
411
|
+
try {
|
|
412
|
+
await hook(c);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
this.logger.error('Failed to execute a client-ready hook:', error);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
await this.client.login(this.kythiaConfig.bot.token);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
this.logger.error('❌ Kythia initialization failed:', error);
|
|
422
|
+
if (this.kythiaConfig.sentry.dsn) {
|
|
423
|
+
Sentry.captureException(error);
|
|
424
|
+
}
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
module.exports = Kythia;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const { Client, GatewayIntentBits, Partials, Options } = require('discord.js');
|
|
2
|
+
|
|
3
|
+
module.exports = function kythiaClient() {
|
|
4
|
+
const client = new Client({
|
|
5
|
+
intents: [
|
|
6
|
+
GatewayIntentBits.Guilds,
|
|
7
|
+
GatewayIntentBits.GuildMessages,
|
|
8
|
+
GatewayIntentBits.MessageContent,
|
|
9
|
+
GatewayIntentBits.GuildMembers,
|
|
10
|
+
GatewayIntentBits.GuildModeration,
|
|
11
|
+
GatewayIntentBits.GuildInvites,
|
|
12
|
+
GatewayIntentBits.GuildVoiceStates,
|
|
13
|
+
GatewayIntentBits.AutoModerationExecution,
|
|
14
|
+
GatewayIntentBits.DirectMessages,
|
|
15
|
+
GatewayIntentBits.DirectMessageReactions,
|
|
16
|
+
GatewayIntentBits.DirectMessageTyping,
|
|
17
|
+
GatewayIntentBits.GuildExpressions,
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
partials: [Partials.Message, Partials.Channel, Partials.Reaction, Partials.User, Partials.GuildMember],
|
|
21
|
+
|
|
22
|
+
makeCache: Options.cacheWithLimits({
|
|
23
|
+
MessageManager: 25,
|
|
24
|
+
PresenceManager: 0,
|
|
25
|
+
GuildMemberManager: {
|
|
26
|
+
max: 100,
|
|
27
|
+
keepOverLimit: (member) =>
|
|
28
|
+
(client.user && member.id === client.user.id) ||
|
|
29
|
+
(member.guild && member.id === member.guild.ownerId) ||
|
|
30
|
+
(member.voice && member.voice.channelId !== null),
|
|
31
|
+
},
|
|
32
|
+
ThreadManager: 10,
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
sweepers: {
|
|
36
|
+
...Options.DefaultSweeperSettings,
|
|
37
|
+
messages: {
|
|
38
|
+
interval: 3600,
|
|
39
|
+
lifetime: 1800,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
threads: {
|
|
43
|
+
interval: 3600,
|
|
44
|
+
lifetime: 1800,
|
|
45
|
+
},
|
|
46
|
+
users: {
|
|
47
|
+
interval: 3600,
|
|
48
|
+
filter: () => (user) => user && !user.bot,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return client;
|
|
53
|
+
};
|