kythia-core 0.10.1-beta → 0.11.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -1
- package/package.json +11 -1
- package/src/Kythia.js +9 -7
- package/src/KythiaClient.js +1 -2
- package/src/cli/Command.js +68 -0
- package/src/cli/commands/CacheClearCommand.js +136 -0
- package/src/cli/commands/LangCheckCommand.js +396 -0
- package/src/cli/commands/LangTranslateCommand.js +336 -0
- package/src/cli/commands/MakeMigrationCommand.js +82 -0
- package/src/cli/commands/MakeModelCommand.js +81 -0
- package/src/cli/commands/MigrateCommand.js +259 -0
- package/src/cli/commands/NamespaceCommand.js +112 -0
- package/src/cli/commands/StructureCommand.js +70 -0
- package/src/cli/commands/UpversionCommand.js +94 -0
- package/src/cli/index.js +69 -0
- package/src/cli/utils/db.js +117 -0
- package/src/database/KythiaMigrator.js +1 -1
- package/src/database/KythiaModel.js +76 -48
- package/src/database/KythiaSequelize.js +1 -1
- package/src/database/KythiaStorage.js +1 -1
- package/src/database/ModelLoader.js +1 -1
- package/src/managers/AddonManager.js +10 -1
- package/src/managers/EventManager.js +1 -1
- package/src/managers/InteractionManager.js +56 -2
- package/src/managers/ShutdownManager.js +1 -1
- package/.husky/pre-commit +0 -4
- package/biome.json +0 -40
- package/bun.lock +0 -445
package/README.md
CHANGED
|
@@ -305,6 +305,92 @@ The core abandons traditional `sync()` operations in favor of a robust, **Larave
|
|
|
305
305
|
|
|
306
306
|
-----
|
|
307
307
|
|
|
308
|
+
## �️ CLI Tools & Commands
|
|
309
|
+
|
|
310
|
+
Kythia Core comes with a powerful CLI to streamline development, database management, and localization tasks.
|
|
311
|
+
|
|
312
|
+
Run `npx kythia --help` to see all available commands.
|
|
313
|
+
|
|
314
|
+
### Database Management
|
|
315
|
+
|
|
316
|
+
#### `migrate`
|
|
317
|
+
Run pending database migrations.
|
|
318
|
+
```bash
|
|
319
|
+
npx kythia migrate
|
|
320
|
+
```
|
|
321
|
+
**Options:**
|
|
322
|
+
* `-f, --fresh`: **[DANGER]** Wipe the entire database (drop all tables) and re-run all migrations from scratch.
|
|
323
|
+
* `-r, --rollback`: Rollback the last *batch* of migrations.
|
|
324
|
+
|
|
325
|
+
#### `make:migration`
|
|
326
|
+
Create a new timestamped migration file in an addon.
|
|
327
|
+
```bash
|
|
328
|
+
npx kythia make:migration --name create_users_table --addon core
|
|
329
|
+
```
|
|
330
|
+
**Options:**
|
|
331
|
+
* `--name <string>`: Name of the migration (snake_case recommended).
|
|
332
|
+
* `--addon <string>`: Target addon name (must exist in `addons/`).
|
|
333
|
+
|
|
334
|
+
#### `make:model`
|
|
335
|
+
Scaffold a new Sequelize model file in an addon.
|
|
336
|
+
```bash
|
|
337
|
+
npx kythia make:model --name User --addon core
|
|
338
|
+
```
|
|
339
|
+
**Options:**
|
|
340
|
+
* `--name <string>`: Name of the model (PascalCase recommended).
|
|
341
|
+
* `--addon <string>`: Target addon name.
|
|
342
|
+
|
|
343
|
+
#### `cache:clear`
|
|
344
|
+
Flush Redis cache. Supports multi-instance selection if `REDIS_URLS` is configured.
|
|
345
|
+
```bash
|
|
346
|
+
npx kythia cache:clear
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Localization (i18n)
|
|
350
|
+
|
|
351
|
+
#### `lang:check`
|
|
352
|
+
Lint translation key usage in your code against your language files.
|
|
353
|
+
```bash
|
|
354
|
+
npx kythia lang:check
|
|
355
|
+
```
|
|
356
|
+
* **Static Analysis:** Uses AST parsing to find `t('key')` calls.
|
|
357
|
+
* **Validation:** Reports missing keys in JSON files.
|
|
358
|
+
* **Unused Keys:** Reports keys defined in `en.json` but never used in code.
|
|
359
|
+
|
|
360
|
+
#### `lang:translate`
|
|
361
|
+
Auto-translate your `en.json` to a target language using Google Gemini AI.
|
|
362
|
+
```bash
|
|
363
|
+
npx kythia lang:translate --target ja
|
|
364
|
+
```
|
|
365
|
+
**Options:**
|
|
366
|
+
* `--target <lang>`: Target language code (default: `ja`).
|
|
367
|
+
* **Requires:** `GEMINI_API_KEYS` in `.env`.
|
|
368
|
+
|
|
369
|
+
### Development Utilities
|
|
370
|
+
|
|
371
|
+
#### `dev:namespace`
|
|
372
|
+
Automatically add or update JSDoc `@namespace` headers in all project files.
|
|
373
|
+
```bash
|
|
374
|
+
npx kythia dev:namespace
|
|
375
|
+
```
|
|
376
|
+
Useful for maintaining consistent file documentation and ownership headers.
|
|
377
|
+
|
|
378
|
+
#### `gen:structure`
|
|
379
|
+
Generate a markdown tree representation of your project structure.
|
|
380
|
+
```bash
|
|
381
|
+
npx kythia gen:structure
|
|
382
|
+
```
|
|
383
|
+
Outputs to `temp/structure.md`. Great for documentation or sharing context with AI.
|
|
384
|
+
|
|
385
|
+
#### `version:up`
|
|
386
|
+
Synchronize JSDoc `@version` tags across the project with `package.json`.
|
|
387
|
+
```bash
|
|
388
|
+
npx kythia version:up
|
|
389
|
+
```
|
|
390
|
+
Run this after bumping your package version to keep file headers in sync.
|
|
391
|
+
|
|
392
|
+
-----
|
|
393
|
+
|
|
308
394
|
## 🔗 Peer Dependencies
|
|
309
395
|
|
|
310
396
|
* **`discord.js`:** This is a `peerDependency`. `kythia-core` requires `discord.js` to function, but it expects the *consuming application* (your main bot) to provide it by listing `discord.js` in its own `dependencies`. This prevents version conflicts and issues with `instanceof` checks, especially common when using `npm link` during development. Ensure your main bot project has `discord.js` installed.
|
|
@@ -313,4 +399,4 @@ The core abandons traditional `sync()` operations in favor of a robust, **Larave
|
|
|
313
399
|
|
|
314
400
|
## 📜 License
|
|
315
401
|
|
|
316
|
-
This project is licensed under the CC BY NC 4.0 License - see the [LICENSE](
|
|
402
|
+
This project is licensed under the CC BY NC 4.0 License - see the [LICENSE](./LICENSE) file for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kythia-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1-beta",
|
|
4
4
|
"description": "Core library for the Kythia main Discord bot: extensible, modular, and scalable foundation for commands, components, and event management.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"biome check --write --no-errors-on-unmatched"
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"kythia": "./src/cli/index.js"
|
|
18
|
+
},
|
|
16
19
|
"keywords": [
|
|
17
20
|
"kythia",
|
|
18
21
|
"discord",
|
|
@@ -32,15 +35,22 @@
|
|
|
32
35
|
"license": "CC BY NC 4.0",
|
|
33
36
|
"type": "commonjs",
|
|
34
37
|
"dependencies": {
|
|
38
|
+
"@babel/parser": "^7.28.5",
|
|
39
|
+
"@babel/traverse": "^7.28.5",
|
|
40
|
+
"@dotenvx/dotenvx": "^1.51.1",
|
|
41
|
+
"@google/genai": "^1.30.0",
|
|
35
42
|
"@sentry/node": "^10.10.0",
|
|
36
43
|
"async-exit-hook": "^2.0.1",
|
|
37
44
|
"async-mutex": "^0.5.0",
|
|
38
45
|
"cli-color": "^2.0.4",
|
|
46
|
+
"commander": "^14.0.2",
|
|
39
47
|
"dotenv": "^16.6.1",
|
|
40
48
|
"figlet": "^1.9.3",
|
|
49
|
+
"glob": "^13.0.0",
|
|
41
50
|
"ioredis": "^5.7.0",
|
|
42
51
|
"json-stable-stringify": "^1.3.0",
|
|
43
52
|
"lru-cache": "^11.2.2",
|
|
53
|
+
"picocolors": "^1.1.1",
|
|
44
54
|
"sequelize": "^6.37.7",
|
|
45
55
|
"umzug": "^3.8.2"
|
|
46
56
|
},
|
package/src/Kythia.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/Kythia.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.11.1-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* The heart of the application lifecycle. This class acts as the central
|
|
@@ -29,7 +29,9 @@ const AddonManager = require('./managers/AddonManager');
|
|
|
29
29
|
const EventManager = require('./managers/EventManager');
|
|
30
30
|
|
|
31
31
|
const KythiaMigrator = require('./database/KythiaMigrator');
|
|
32
|
-
const ModelLoader = require('./database/ModelLoader');
|
|
32
|
+
// const ModelLoader = require('./database/ModelLoader');
|
|
33
|
+
const bootModels = require('./database/ModelLoader');
|
|
34
|
+
const KythiaModel = require('./database/KythiaModel');
|
|
33
35
|
|
|
34
36
|
class Kythia {
|
|
35
37
|
/**
|
|
@@ -380,7 +382,7 @@ class Kythia {
|
|
|
380
382
|
clc.cyan('Created by kenndeclouv'),
|
|
381
383
|
clc.cyan('Discord Support: ') + clc.underline('https://dsc.gg/kythia'),
|
|
382
384
|
clc.cyan('Official Documentation: ') +
|
|
383
|
-
clc.underline('https://kythia.
|
|
385
|
+
clc.underline('https://docs.kythia.me'),
|
|
384
386
|
'',
|
|
385
387
|
clc.cyanBright(`Kythia version: ${version}`),
|
|
386
388
|
'',
|
|
@@ -482,17 +484,17 @@ class Kythia {
|
|
|
482
484
|
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Connect Database ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
483
485
|
await this.sequelize.authenticate();
|
|
484
486
|
|
|
485
|
-
this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Run Migrations ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
|
|
486
487
|
await KythiaMigrator({
|
|
487
488
|
sequelize: this.sequelize,
|
|
488
489
|
container: this.container,
|
|
489
490
|
logger: this.logger,
|
|
490
491
|
});
|
|
491
492
|
|
|
492
|
-
this.
|
|
493
|
-
await ModelLoader(this, this.sequelize);
|
|
493
|
+
await bootModels(this, this.sequelize);
|
|
494
494
|
|
|
495
|
-
this.logger.info('
|
|
495
|
+
this.logger.info('🪝 Attaching Cache Hooks...');
|
|
496
|
+
|
|
497
|
+
KythiaModel.attachHooksToAllModels(this.sequelize, this.client);
|
|
496
498
|
|
|
497
499
|
const handlers = this.addonManager.getHandlers();
|
|
498
500
|
this.eventManager = new EventManager({
|
package/src/KythiaClient.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file src/client/kythiaClient.js
|
|
5
5
|
* @copyright © 2025 kenndeclouv
|
|
6
6
|
* @assistant chaa & graa
|
|
7
|
-
* @version 0.
|
|
7
|
+
* @version 0.11.1-beta
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Factory function that initializes the Discord.js Client with high-performance
|
|
@@ -49,7 +49,6 @@ module.exports = function kythiaClient() {
|
|
|
49
49
|
|
|
50
50
|
ThreadManager: {
|
|
51
51
|
maxSize: 25,
|
|
52
|
-
keepOverLimit: (thread) => thread.isActive(),
|
|
53
52
|
},
|
|
54
53
|
|
|
55
54
|
GuildMemberManager: {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🏗️ Abstract Base Command
|
|
3
|
+
*
|
|
4
|
+
* @file src/cli/Command.js
|
|
5
|
+
* @copyright © 2025 kenndeclouv
|
|
6
|
+
* @assistant chaa & graa
|
|
7
|
+
* @version 0.11.1-beta
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* The base class for all Kythia CLI commands. It enforces a standard structure
|
|
11
|
+
* (signature, description, handle) and manages the integration with Commander.js.
|
|
12
|
+
*
|
|
13
|
+
* ✨ Core Features:
|
|
14
|
+
* - Standardized Interface: Enforces `handle()` implementation.
|
|
15
|
+
* - Flexible Parsing: Handles both Options and Arguments intelligently.
|
|
16
|
+
* - Auto Registration: Simplifies wiring commands to the main program.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
class Command {
|
|
20
|
+
/**
|
|
21
|
+
* The name and signature of the console command.
|
|
22
|
+
* @type {string}
|
|
23
|
+
*/
|
|
24
|
+
signature = '';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The console command description.
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
description = '';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Execute the console command.
|
|
34
|
+
* @param {Object} options - The options object from Commander
|
|
35
|
+
* @param {...any} args - Positional arguments (if any)
|
|
36
|
+
*/
|
|
37
|
+
async handle(_options, ..._args) {
|
|
38
|
+
throw new Error('Command must implement handle method');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register the command with the program.
|
|
43
|
+
* @param {import('commander').Command} program
|
|
44
|
+
*/
|
|
45
|
+
register(program) {
|
|
46
|
+
const cmd = program
|
|
47
|
+
.command(this.signature)
|
|
48
|
+
.description(this.description)
|
|
49
|
+
.action((...args) => {
|
|
50
|
+
const _commandObj = args.pop();
|
|
51
|
+
const opts = args.pop() || {};
|
|
52
|
+
|
|
53
|
+
this.handle(opts, ...args);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.configure(cmd);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Configure additional options or arguments.
|
|
61
|
+
* @param {import('commander').Command} cmd
|
|
62
|
+
*/
|
|
63
|
+
configure(_cmd) {
|
|
64
|
+
// Override to add options
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = Command;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🧹 Redis Cache Flusher
|
|
3
|
+
*
|
|
4
|
+
* @file src/cli/commands/CacheClearCommand.js
|
|
5
|
+
* @copyright © 2025 kenndeclouv
|
|
6
|
+
* @assistant chaa & graa
|
|
7
|
+
* @version 0.11.1-beta
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* Interactive utility to flush Redis cache. Supports intelligent handling of
|
|
11
|
+
* multiple Redis instances defined in environment variables.
|
|
12
|
+
*
|
|
13
|
+
* ✨ Core Features:
|
|
14
|
+
* - Multi-Target: Detects and lists all Redis URLs from config.
|
|
15
|
+
* - Safety First: Requires explicit confirmation before flushing.
|
|
16
|
+
* - Target Selection: Allows flushing specific instances or all at once.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const Command = require('../Command');
|
|
20
|
+
const pc = require('picocolors');
|
|
21
|
+
const readline = require('node:readline');
|
|
22
|
+
const Redis = require('ioredis');
|
|
23
|
+
require('@dotenvx/dotenvx/config');
|
|
24
|
+
|
|
25
|
+
function askQuestion(query) {
|
|
26
|
+
const rl = readline.createInterface({
|
|
27
|
+
input: process.stdin,
|
|
28
|
+
output: process.stdout,
|
|
29
|
+
});
|
|
30
|
+
return new Promise((resolve) =>
|
|
31
|
+
rl.question(query, (ans) => {
|
|
32
|
+
rl.close();
|
|
33
|
+
resolve(ans);
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class CacheClearCommand extends Command {
|
|
39
|
+
signature = 'cache:clear';
|
|
40
|
+
description = 'Flush Redis cache (supports multi-instance selection)';
|
|
41
|
+
|
|
42
|
+
async handle() {
|
|
43
|
+
console.log(pc.dim('🔍 Detecting Redis configuration...'));
|
|
44
|
+
|
|
45
|
+
const redisUrlsRaw = process.env.REDIS_URLS;
|
|
46
|
+
|
|
47
|
+
if (!redisUrlsRaw) {
|
|
48
|
+
console.error(pc.red('❌ REDIS_URLS not found in .env'));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const urls = redisUrlsRaw
|
|
53
|
+
.split(',')
|
|
54
|
+
.map((u) => u.trim())
|
|
55
|
+
.filter((u) => u.length > 0);
|
|
56
|
+
|
|
57
|
+
if (urls.length === 0) {
|
|
58
|
+
console.error(pc.red('❌ No valid Redis URLs found.'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let targets = [];
|
|
63
|
+
|
|
64
|
+
if (urls.length === 1) {
|
|
65
|
+
targets = [urls[0]];
|
|
66
|
+
console.log(pc.cyan(`🎯 Target: ${targets[0]}`));
|
|
67
|
+
} else {
|
|
68
|
+
console.log(
|
|
69
|
+
pc.yellow(`⚠️ Multiple Redis instances detected (${urls.length}):`),
|
|
70
|
+
);
|
|
71
|
+
console.log(
|
|
72
|
+
pc.dim('---------------------------------------------------'),
|
|
73
|
+
);
|
|
74
|
+
console.log(`0. ${pc.bgRed(pc.white(' FLUSH ALL INSTANCES '))}`);
|
|
75
|
+
urls.forEach((url, index) => {
|
|
76
|
+
const maskedUrl = url.replace(/:([^@]+)@/, ':****@');
|
|
77
|
+
console.log(`${index + 1}. ${maskedUrl}`);
|
|
78
|
+
});
|
|
79
|
+
console.log(
|
|
80
|
+
pc.dim('---------------------------------------------------'),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const answer = await askQuestion(
|
|
84
|
+
pc.cyan('Select target to flush (number): '),
|
|
85
|
+
);
|
|
86
|
+
const choice = parseInt(answer, 10);
|
|
87
|
+
|
|
88
|
+
if (Number.isNaN(choice) || choice < 0 || choice > urls.length) {
|
|
89
|
+
console.error(pc.red('❌ Invalid selection.'));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (choice === 0) {
|
|
94
|
+
targets = urls;
|
|
95
|
+
} else {
|
|
96
|
+
targets = [urls[choice - 1]];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const confirm = await askQuestion(
|
|
101
|
+
pc.bgRed(pc.white(' DANGER ')) +
|
|
102
|
+
pc.yellow(
|
|
103
|
+
` Are you sure you want to FLUSHALL ${targets.length} instance(s)? (y/n): `,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
|
|
108
|
+
console.log(pc.cyan('🛡️ Operation cancelled.'));
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('');
|
|
113
|
+
for (const url of targets) {
|
|
114
|
+
const masked = url.replace(/:([^@]+)@/, ':****@');
|
|
115
|
+
try {
|
|
116
|
+
console.log(pc.dim(`🔌 Connecting to ${masked}...`));
|
|
117
|
+
const redis = new Redis(url, {
|
|
118
|
+
maxRetriesPerRequest: 1,
|
|
119
|
+
retryStrategy: null,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await redis.flushall();
|
|
123
|
+
console.log(pc.green(`✅ FLUSHALL Success: ${masked}`));
|
|
124
|
+
|
|
125
|
+
redis.quit();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(pc.red(`❌ Failed to flush ${masked}: ${err.message}`));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(pc.green('\n✨ Cache clearing process completed.'));
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = CacheClearCommand;
|