@kotori-bot/loader 1.4.2 → 1.5.0-beta.1
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/lib/{loader.d.ts → class/loader.d.ts} +8 -5
- package/lib/{loader.js → class/loader.js} +64 -57
- package/lib/{runner.d.ts → class/runner.d.ts} +9 -3
- package/lib/{runner.js → class/runner.js} +103 -57
- package/lib/{consts.d.ts → constants.d.ts} +3 -0
- package/lib/constants.js +14 -0
- package/lib/decorators/index.d.ts +5 -0
- package/lib/decorators/index.js +24 -0
- package/lib/decorators/utils.d.ts +35 -0
- package/lib/decorators/utils.js +83 -0
- package/lib/index.d.ts +4 -2
- package/lib/index.js +5 -3
- package/lib/service/adapter.d.ts +15 -0
- package/lib/service/adapter.js +48 -0
- package/lib/service/file.d.ts +1 -1
- package/lib/service/file.js +4 -3
- package/lib/service/server.d.ts +53 -16
- package/lib/service/server.js +74 -25
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +17 -0
- package/lib/types/server.d.ts +27 -0
- package/lib/types/server.js +2 -0
- package/package.json +4 -2
- package/lib/consts.js +0 -11
- /package/lib/{log.d.ts → utils/log.d.ts} +0 -0
- /package/lib/{log.js → utils/log.js} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Container, Symbols } from '@kotori-bot/core';
|
|
1
|
+
import { Container, Symbols, formatFactory } from '@kotori-bot/core';
|
|
2
2
|
import Logger from '@kotori-bot/logger';
|
|
3
3
|
import Runner from './runner';
|
|
4
|
-
import Server from '
|
|
5
|
-
import type Database from '
|
|
6
|
-
import File from '
|
|
4
|
+
import Server from '../service/server';
|
|
5
|
+
import type Database from '../service/database';
|
|
6
|
+
import File from '../service/file';
|
|
7
7
|
declare module '@kotori-bot/core' {
|
|
8
8
|
interface Context {
|
|
9
9
|
readonly baseDir: Runner['baseDir'];
|
|
@@ -15,6 +15,8 @@ declare module '@kotori-bot/core' {
|
|
|
15
15
|
server: Server;
|
|
16
16
|
db: Database;
|
|
17
17
|
file: File;
|
|
18
|
+
format: ReturnType<typeof formatFactory>;
|
|
19
|
+
locale: Context['i18n']['locale'];
|
|
18
20
|
}
|
|
19
21
|
interface GlobalConfig {
|
|
20
22
|
dirs: string[];
|
|
@@ -22,11 +24,12 @@ declare module '@kotori-bot/core' {
|
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
export declare class Loader extends Container {
|
|
25
|
-
private ctx;
|
|
27
|
+
private readonly ctx;
|
|
26
28
|
private loadCount;
|
|
27
29
|
constructor(options?: {
|
|
28
30
|
dir?: string;
|
|
29
31
|
mode?: string;
|
|
32
|
+
level?: number;
|
|
30
33
|
});
|
|
31
34
|
run(): void;
|
|
32
35
|
private handleError;
|
|
@@ -32,47 +32,44 @@ exports.Loader = void 0;
|
|
|
32
32
|
* @Blog: https://hotaru.icu
|
|
33
33
|
* @Date: 2023-06-24 15:12:55
|
|
34
34
|
* @LastEditors: Hotaru biyuehuya@gmail.com
|
|
35
|
-
* @LastEditTime: 2024-
|
|
35
|
+
* @LastEditTime: 2024-05-26 16:45:40
|
|
36
36
|
*/
|
|
37
37
|
const core_1 = require("@kotori-bot/core");
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const logger_1 =
|
|
38
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
39
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
40
|
+
const logger_1 = __importStar(require("@kotori-bot/logger"));
|
|
41
41
|
const runner_1 = __importStar(require("./runner"));
|
|
42
|
-
const log_1 = __importDefault(require("
|
|
43
|
-
const
|
|
44
|
-
const server_1 = __importDefault(require("
|
|
45
|
-
const file_1 = __importDefault(require("
|
|
46
|
-
function
|
|
42
|
+
const log_1 = __importDefault(require("../utils/log"));
|
|
43
|
+
const constants_1 = require("../constants");
|
|
44
|
+
const server_1 = __importDefault(require("../service/server"));
|
|
45
|
+
const file_1 = __importDefault(require("../service/file"));
|
|
46
|
+
function getBaseDir(file, dir) {
|
|
47
47
|
const handle = (root) => {
|
|
48
48
|
const baseDir = {
|
|
49
49
|
root,
|
|
50
|
-
modules:
|
|
51
|
-
data:
|
|
52
|
-
logs:
|
|
50
|
+
modules: node_path_1.default.join(root, 'modules'),
|
|
51
|
+
data: node_path_1.default.join(root, 'data'),
|
|
52
|
+
logs: node_path_1.default.join(root, 'logs')
|
|
53
53
|
};
|
|
54
54
|
Object.values(baseDir).forEach((val) => {
|
|
55
|
-
if (!
|
|
56
|
-
|
|
55
|
+
if (!node_fs_1.default.existsSync(val))
|
|
56
|
+
node_fs_1.default.mkdirSync(val);
|
|
57
57
|
});
|
|
58
58
|
return baseDir;
|
|
59
59
|
};
|
|
60
|
-
const options = {
|
|
61
|
-
mode: file === consts_1.DEV_CONFIG_NAME ? 'dev' : 'build'
|
|
62
|
-
};
|
|
63
60
|
if (dir)
|
|
64
|
-
return
|
|
65
|
-
let root =
|
|
61
|
+
return handle(node_path_1.default.resolve(dir));
|
|
62
|
+
let root = node_path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
|
|
66
63
|
let count = 0;
|
|
67
|
-
while (!
|
|
64
|
+
while (!node_fs_1.default.existsSync(node_path_1.default.join(root, file))) {
|
|
68
65
|
if (count > 5) {
|
|
69
66
|
logger_1.default.fatal(`cannot find file ${file} `);
|
|
70
67
|
process.exit();
|
|
71
68
|
}
|
|
72
|
-
root =
|
|
69
|
+
root = node_path_1.default.join(root, '..');
|
|
73
70
|
count += 1;
|
|
74
71
|
}
|
|
75
|
-
return
|
|
72
|
+
return handle(root);
|
|
76
73
|
}
|
|
77
74
|
/* eslint consistent-return: 0 */
|
|
78
75
|
function getCoreConfig(file, baseDir) {
|
|
@@ -91,7 +88,7 @@ function getCoreConfig(file, baseDir) {
|
|
|
91
88
|
.default(core_1.DEFAULT_CORE_CONFIG.plugin)
|
|
92
89
|
})
|
|
93
90
|
.default({ global: Object.assign(core_1.DEFAULT_CORE_CONFIG.global), plugin: core_1.DEFAULT_CORE_CONFIG.plugin })
|
|
94
|
-
.parse((0, core_1.loadConfig)(
|
|
91
|
+
.parse((0, core_1.loadConfig)(node_path_1.default.join(baseDir.root, file), 'yaml'));
|
|
95
92
|
return core_1.Tsu.Object({
|
|
96
93
|
adapter: core_1.Tsu.Object({})
|
|
97
94
|
.index(core_1.Tsu.Object({
|
|
@@ -115,13 +112,27 @@ class Loader extends core_1.Container {
|
|
|
115
112
|
loadCount = 0;
|
|
116
113
|
constructor(options) {
|
|
117
114
|
super();
|
|
118
|
-
const file = options && options.mode
|
|
119
|
-
const runnerConfig =
|
|
115
|
+
const file = options && options.mode?.startsWith(constants_1.DEV_MODE) ? constants_1.DEV_CONFIG_NAME : constants_1.BUILD_CONFIG_NAME;
|
|
116
|
+
const runnerConfig = {
|
|
117
|
+
baseDir: getBaseDir(file, options?.dir),
|
|
118
|
+
options: { mode: (options?.mode || constants_1.BUILD_MODE) },
|
|
119
|
+
level: options?.level || options?.mode?.startsWith(constants_1.DEV_MODE) ? logger_1.LoggerLevel.DEBUG : logger_1.LoggerLevel.INFO
|
|
120
|
+
};
|
|
120
121
|
const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
|
|
121
122
|
ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
|
|
122
123
|
ctx.mixin('runner', ['baseDir', 'options']);
|
|
123
124
|
core_1.Container.setInstance(ctx);
|
|
125
|
+
ctx.provide('loader-tools', { format: (0, core_1.formatFactory)(ctx.i18n), locale: ctx.i18n.locale.bind(ctx.i18n) });
|
|
126
|
+
ctx.mixin('loader-tools', ['locale', 'format']);
|
|
127
|
+
ctx.i18n.use(node_path_1.default.resolve(__dirname, '../../locales'));
|
|
124
128
|
this.ctx = core_1.Container.getInstance();
|
|
129
|
+
this.ctx.logger.trace(`options:`, options);
|
|
130
|
+
this.ctx.logger.trace(`runnerConfig:`, runnerConfig);
|
|
131
|
+
this.ctx.logger.trace(`baseDir:`, this.ctx.baseDir);
|
|
132
|
+
this.ctx.logger.trace(`options:`, this.ctx.options);
|
|
133
|
+
this.ctx.logger.trace(`config:`, this.ctx.config);
|
|
134
|
+
this.ctx.logger.trace(`where:`, __dirname, __filename);
|
|
135
|
+
this.ctx.logger.trace(`running:`, process.cwd());
|
|
125
136
|
}
|
|
126
137
|
run() {
|
|
127
138
|
(0, log_1.default)(this.ctx.pkg, this.ctx);
|
|
@@ -153,42 +164,39 @@ class Loader extends core_1.Container {
|
|
|
153
164
|
process.on('uncaughtExceptionMonitor', (err) => this.handleError(err, 'sync'));
|
|
154
165
|
process.on('unhandledRejection', (err) => this.handleError(err, 'async'));
|
|
155
166
|
process.on('SIGINT', () => process.exit());
|
|
156
|
-
this.ctx.logger.debug('
|
|
167
|
+
this.ctx.logger.debug(this.ctx.locale('loader.debug.info'));
|
|
157
168
|
}
|
|
158
169
|
listenMessage() {
|
|
159
170
|
this.ctx.on('connect', (data) => {
|
|
160
|
-
const { type, mode, normal, address, adapter } = data;
|
|
171
|
+
const { type, mode, normal, address: addr, adapter } = data;
|
|
161
172
|
let msg;
|
|
162
173
|
if (type === 'connect') {
|
|
163
174
|
switch (mode) {
|
|
164
175
|
case 'ws':
|
|
165
|
-
msg =
|
|
176
|
+
msg = this.ctx.format(`loader.bots.${normal ? 'connect' : 'reconnect'}`, [addr]);
|
|
166
177
|
break;
|
|
167
178
|
case 'ws-reverse':
|
|
168
|
-
msg = `
|
|
179
|
+
msg = this.ctx.format(`loader.bots.${normal ? 'start' : 'restart'}`, [addr]);
|
|
169
180
|
break;
|
|
170
181
|
default:
|
|
171
|
-
msg =
|
|
182
|
+
msg = this.ctx.format('loader.bots.ready', [addr]);
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
185
|
else {
|
|
175
186
|
switch (mode) {
|
|
176
187
|
case 'ws':
|
|
177
|
-
msg = `disconnect
|
|
188
|
+
msg = this.ctx.format(`loader.bots.disconnect${normal ? '' : '.error'}`, [addr]);
|
|
178
189
|
break;
|
|
179
190
|
case 'ws-reverse':
|
|
180
|
-
msg = `
|
|
191
|
+
msg = this.ctx.format(`loader.bots.stop${normal ? '' : '.error'}`, [addr]);
|
|
181
192
|
break;
|
|
182
193
|
default:
|
|
183
|
-
msg =
|
|
194
|
+
msg = this.ctx.format('loader.bots.dispose', [addr]);
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
197
|
adapter.ctx.logger[normal ? 'info' : 'warn'](msg);
|
|
187
198
|
});
|
|
188
|
-
this.ctx.on('status', (
|
|
189
|
-
const { status, adapter } = data;
|
|
190
|
-
adapter.ctx.logger.info(status);
|
|
191
|
-
});
|
|
199
|
+
this.ctx.on('status', ({ status, adapter }) => adapter.ctx.logger.info(status));
|
|
192
200
|
this.ctx.on('ready_module', (data) => {
|
|
193
201
|
if (typeof data.instance !== 'object')
|
|
194
202
|
return;
|
|
@@ -199,16 +207,17 @@ class Loader extends core_1.Container {
|
|
|
199
207
|
return;
|
|
200
208
|
this.loadCount += 1;
|
|
201
209
|
const { name, version, author, peerDependencies } = pkg[0].pkg;
|
|
202
|
-
this.ctx.logger.info(
|
|
210
|
+
this.ctx.logger.info(this.ctx.format('loader.modules.load', [name, version, Array.isArray(author) ? author.join(',') : author]));
|
|
203
211
|
const requiredVersion = peerDependencies['kotori-bot'];
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
if (requiredVersion.includes('workspace') ||
|
|
213
|
+
constants_1.SUPPORTS_VERSION.exec(requiredVersion) ||
|
|
214
|
+
requiredVersion.includes(this.ctx.pkg.version))
|
|
215
|
+
return;
|
|
216
|
+
if (constants_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
|
|
217
|
+
this.ctx.logger.warn(this.ctx.format('loader.modules.incomplete', [requiredVersion]));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.ctx.logger.error(this.ctx.format('loader.modules.unsupported', [requiredVersion]));
|
|
212
221
|
}
|
|
213
222
|
});
|
|
214
223
|
}
|
|
@@ -219,7 +228,7 @@ class Loader extends core_1.Container {
|
|
|
219
228
|
loadAllModules() {
|
|
220
229
|
this.ctx.get('runner').loadAll();
|
|
221
230
|
const failLoadCount = this.ctx.get('runner')[core_1.Symbols.modules].size - this.loadCount;
|
|
222
|
-
this.ctx.logger.info(
|
|
231
|
+
this.ctx.logger.info(this.ctx.format(`loader.modules.all${failLoadCount > 0 ? '.fail' : ''}`, [this.loadCount, failLoadCount]));
|
|
223
232
|
this.loadAllAdapter();
|
|
224
233
|
this.ctx.emit('ready');
|
|
225
234
|
}
|
|
@@ -228,13 +237,11 @@ class Loader extends core_1.Container {
|
|
|
228
237
|
Object.keys(this.ctx.config.adapter).forEach((botName) => {
|
|
229
238
|
const botConfig = this.ctx.config.adapter[botName];
|
|
230
239
|
const array = adapters.get(botConfig.extends);
|
|
231
|
-
if (!array)
|
|
232
|
-
this.ctx.logger.warn(
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
240
|
+
if (!array)
|
|
241
|
+
return this.ctx.logger.warn(this.ctx.format('loader.adapters.notfound', [botConfig.extends, botName]));
|
|
235
242
|
const result = array[1]?.parseSafe(botConfig);
|
|
236
243
|
if (result && !result.value)
|
|
237
|
-
throw new core_1.ModuleError(
|
|
244
|
+
throw new core_1.ModuleError(this.ctx.format('error.module.config_bot', [botName, result.error.message]));
|
|
238
245
|
const bot = new array[0](this.ctx.extends({}, `${botConfig.extends}/${botName}`), result ? result.data : botConfig, botName);
|
|
239
246
|
this.ctx.on('ready', () => bot.start());
|
|
240
247
|
this.ctx.on('dispose', () => bot.stop());
|
|
@@ -243,16 +250,16 @@ class Loader extends core_1.Container {
|
|
|
243
250
|
async checkUpdate() {
|
|
244
251
|
const { version } = this.ctx.pkg;
|
|
245
252
|
const res = await this.ctx.http
|
|
246
|
-
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/
|
|
247
|
-
.catch(() => this.ctx.logger.error('
|
|
253
|
+
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* GLOBAL.UPDATE */)
|
|
254
|
+
.catch(() => this.ctx.logger.error(this.ctx.locale('loader.tips.update.failed')));
|
|
248
255
|
if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
|
|
249
|
-
this.ctx.logger.warn(
|
|
256
|
+
this.ctx.logger.warn(this.ctx.locale('loader.tips.update.failed'));
|
|
250
257
|
}
|
|
251
258
|
else if (version === res.version) {
|
|
252
|
-
this.ctx.logger.info('
|
|
259
|
+
this.ctx.logger.info(this.ctx.locale('loader.tips.update.latest'));
|
|
253
260
|
}
|
|
254
261
|
else {
|
|
255
|
-
this.ctx.logger.warn(
|
|
262
|
+
this.ctx.logger.warn(this.ctx.format('loader.tips.update.available', [version, res.version, "https://github.com/kotorijs/kotori" /* GLOBAL.REPO */]));
|
|
256
263
|
}
|
|
257
264
|
}
|
|
258
265
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Context, LocaleType, ModuleConfig, Symbols } from '@kotori-bot/core';
|
|
2
|
+
import { BUILD_MODE, DEV_MODE, DEV_SOURCE_MODE } from '../constants';
|
|
3
|
+
import './loader';
|
|
2
4
|
interface BaseDir {
|
|
3
5
|
root: string;
|
|
4
6
|
modules: string;
|
|
@@ -6,11 +8,12 @@ interface BaseDir {
|
|
|
6
8
|
logs: string;
|
|
7
9
|
}
|
|
8
10
|
interface Options {
|
|
9
|
-
mode:
|
|
11
|
+
mode: typeof BUILD_MODE | typeof DEV_MODE | typeof DEV_SOURCE_MODE;
|
|
10
12
|
}
|
|
11
13
|
interface RunnerConfig {
|
|
12
14
|
baseDir: BaseDir;
|
|
13
15
|
options: Options;
|
|
16
|
+
level: number;
|
|
14
17
|
}
|
|
15
18
|
interface ModulePackage {
|
|
16
19
|
name: string;
|
|
@@ -40,13 +43,16 @@ export declare const localeTypeSchema: import("@kotori-bot/core").UnionParser<[i
|
|
|
40
43
|
export declare class Runner {
|
|
41
44
|
readonly baseDir: BaseDir;
|
|
42
45
|
readonly options: Options;
|
|
43
|
-
private ctx;
|
|
44
|
-
private isDev;
|
|
46
|
+
private readonly ctx;
|
|
47
|
+
private readonly isDev;
|
|
48
|
+
private readonly isSourceDev;
|
|
45
49
|
readonly [Symbols.modules]: Map<string, [ModuleMeta, ModuleConfig]>;
|
|
46
50
|
constructor(ctx: Context, config: RunnerConfig);
|
|
47
51
|
private getDirFiles;
|
|
48
52
|
private getModuleRootDir;
|
|
53
|
+
private checkModuleFiles;
|
|
49
54
|
private getModuleList;
|
|
55
|
+
private loadLang;
|
|
50
56
|
private loadEx;
|
|
51
57
|
private unloadEx;
|
|
52
58
|
loadAll(): void;
|
|
@@ -27,12 +27,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.Runner = exports.localeTypeSchema = void 0;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
30
|
+
const node_fs_1 = __importStar(require("node:fs"));
|
|
31
|
+
const node_path_1 = __importStar(require("node:path"));
|
|
32
32
|
const core_1 = require("@kotori-bot/core");
|
|
33
33
|
const logger_1 = require("@kotori-bot/logger");
|
|
34
|
-
const
|
|
35
|
-
const logger_2 = __importDefault(require("
|
|
34
|
+
const constants_1 = require("../constants");
|
|
35
|
+
const logger_2 = __importDefault(require("../utils/logger"));
|
|
36
|
+
require("./loader");
|
|
36
37
|
exports.localeTypeSchema = core_1.Tsu.Union([
|
|
37
38
|
core_1.Tsu.Union([core_1.Tsu.Literal('en_US'), core_1.Tsu.Literal('ja_JP')]),
|
|
38
39
|
core_1.Tsu.Union([core_1.Tsu.Literal('zh_TW'), core_1.Tsu.Any()])
|
|
@@ -75,18 +76,37 @@ class Runner {
|
|
|
75
76
|
options;
|
|
76
77
|
ctx;
|
|
77
78
|
isDev;
|
|
79
|
+
isSourceDev;
|
|
78
80
|
[core_1.Symbols.modules] = new Map();
|
|
79
81
|
constructor(ctx, config) {
|
|
80
82
|
this.ctx = ctx;
|
|
81
83
|
/* handle config */
|
|
82
84
|
this.baseDir = config.baseDir;
|
|
83
85
|
this.options = config.options;
|
|
84
|
-
this.isDev = this.options.mode
|
|
86
|
+
this.isDev = this.options.mode.startsWith(constants_1.DEV_MODE);
|
|
87
|
+
this.isSourceDev = this.options.mode === constants_1.DEV_SOURCE_MODE;
|
|
85
88
|
const loggerOptions = {
|
|
86
|
-
level: this.
|
|
89
|
+
level: this.ctx.config.global.level ?? config.level,
|
|
87
90
|
label: [],
|
|
88
91
|
transports: [
|
|
89
|
-
new logger_1.ConsoleTransport(
|
|
92
|
+
new logger_1.ConsoleTransport({
|
|
93
|
+
template: '%time% %type% %label%%msg%',
|
|
94
|
+
label: '[%name%] ',
|
|
95
|
+
labelColor: 'cyan',
|
|
96
|
+
time: 'M/D H:m:s',
|
|
97
|
+
timeColor: 'blue',
|
|
98
|
+
pidColor: 'bold',
|
|
99
|
+
useColor: true,
|
|
100
|
+
detail: {
|
|
101
|
+
FATAL: ['FATAL', 'redBright', 'redBright'],
|
|
102
|
+
ERROR: ['ERROR', 'red', 'red'],
|
|
103
|
+
WARN: ['WARN', 'yellowBright', 'yellowBright'],
|
|
104
|
+
INFO: ['INFO', 'green'],
|
|
105
|
+
DEBUG: ['DEBUG', 'magenta', 'magentaBright'],
|
|
106
|
+
TRACE: ['TRACE', 'gray', 'gray']
|
|
107
|
+
},
|
|
108
|
+
indent: 2
|
|
109
|
+
}),
|
|
90
110
|
new logger_1.FileTransport({ dir: this.baseDir.logs, filter: (data) => data.level >= logger_1.LoggerLevel.WARN })
|
|
91
111
|
]
|
|
92
112
|
};
|
|
@@ -94,90 +114,116 @@ class Runner {
|
|
|
94
114
|
ctx.inject('logger');
|
|
95
115
|
}
|
|
96
116
|
getDirFiles(rootDir) {
|
|
97
|
-
const files =
|
|
117
|
+
const files = node_fs_1.default.readdirSync(rootDir);
|
|
98
118
|
const list = [];
|
|
99
119
|
files.forEach((fileName) => {
|
|
100
|
-
const file =
|
|
101
|
-
if (
|
|
120
|
+
const file = node_path_1.default.join(rootDir, fileName);
|
|
121
|
+
if (node_fs_1.default.statSync(file).isDirectory()) {
|
|
102
122
|
list.push(...this.getDirFiles(file));
|
|
103
123
|
}
|
|
104
|
-
if (
|
|
124
|
+
if (node_path_1.default.parse(file).ext !== (this.isSourceDev ? constants_1.DEV_FILE : constants_1.BUILD_FILE))
|
|
105
125
|
return;
|
|
106
|
-
list.push(
|
|
126
|
+
list.push(node_path_1.default.resolve(file));
|
|
107
127
|
});
|
|
108
128
|
return list;
|
|
109
129
|
}
|
|
110
130
|
getModuleRootDir() {
|
|
111
131
|
const moduleRootDir = [];
|
|
112
132
|
[
|
|
113
|
-
...this.ctx.config.global.dirs.map((dir) =>
|
|
133
|
+
...this.ctx.config.global.dirs.map((dir) => node_path_1.default.resolve(this.ctx.baseDir.root, dir)),
|
|
114
134
|
this.ctx.baseDir.modules
|
|
115
135
|
].forEach((dir) => {
|
|
116
|
-
if (
|
|
136
|
+
if (node_fs_1.default.existsSync(dir) && node_fs_1.default.statSync(dir).isDirectory())
|
|
117
137
|
moduleRootDir.push(dir);
|
|
118
138
|
});
|
|
119
139
|
return moduleRootDir;
|
|
120
140
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
async checkModuleFiles(rootDir, filename) {
|
|
142
|
+
const dir = node_path_1.default.join(rootDir, filename);
|
|
143
|
+
if (!node_fs_1.default.statSync(dir).isDirectory())
|
|
144
|
+
return;
|
|
145
|
+
if (rootDir !== this.ctx.baseDir.modules && !filename.startsWith(core_1.PLUGIN_PREFIX))
|
|
146
|
+
return;
|
|
147
|
+
const packagePath = node_path_1.default.join(dir, 'package.json');
|
|
148
|
+
let pkg;
|
|
149
|
+
if (!node_fs_1.default.existsSync(packagePath))
|
|
150
|
+
return;
|
|
151
|
+
try {
|
|
152
|
+
pkg = JSON.parse(node_fs_1.default.readFileSync(packagePath).toString());
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
throw new core_1.DevError(this.ctx.format('error.dev.package.illegal', [packagePath]));
|
|
156
|
+
}
|
|
157
|
+
const result = modulePackageSchema.parseSafe(pkg);
|
|
158
|
+
if (!result.value) {
|
|
159
|
+
if (rootDir !== this.ctx.baseDir.modules)
|
|
131
160
|
return;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const files = fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
|
|
151
|
-
this[core_1.Symbols.modules].set(pkg.name, [
|
|
152
|
-
{ pkg, files, main },
|
|
153
|
-
this.ctx.config.plugin[(0, core_1.stringRightSplit)(pkg.name, core_1.PLUGIN_PREFIX)] || {}
|
|
154
|
-
]);
|
|
161
|
+
throw new core_1.DevError(this.ctx.format('error.dev.package.missing', [packagePath, result.error.message]));
|
|
162
|
+
}
|
|
163
|
+
pkg = result.data;
|
|
164
|
+
const devMode = this.isSourceDev && (0, node_fs_1.existsSync)(node_path_1.default.resolve(dir, constants_1.DEV_IMPORT));
|
|
165
|
+
const main = node_path_1.default.resolve(dir, devMode ? constants_1.DEV_IMPORT : pkg.main);
|
|
166
|
+
if (!node_fs_1.default.existsSync(main))
|
|
167
|
+
throw new core_1.DevError(this.ctx.format('error.dev.main_file', [main]));
|
|
168
|
+
const dirs = node_path_1.default.join(dir, devMode ? constants_1.DEV_CODE_DIRS : node_path_1.default.dirname(pkg.main));
|
|
169
|
+
const files = node_fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
|
|
170
|
+
this[core_1.Symbols.modules].set(pkg.name, [
|
|
171
|
+
{ pkg, files, main },
|
|
172
|
+
this.ctx.config.plugin[(0, core_1.stringRightSplit)(pkg.name, core_1.PLUGIN_PREFIX)] || {}
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
getModuleList(rootDir) {
|
|
176
|
+
this.ctx.logger.trace('load dirs:', rootDir);
|
|
177
|
+
node_fs_1.default.readdirSync(rootDir).forEach(async (filename) => {
|
|
178
|
+
await this.checkModuleFiles(rootDir, filename);
|
|
155
179
|
});
|
|
156
180
|
}
|
|
157
|
-
|
|
181
|
+
loadLang(lang) {
|
|
182
|
+
if (lang)
|
|
183
|
+
this.ctx.i18n.use((0, node_path_1.resolve)(...(Array.isArray(lang) ? lang : [lang])));
|
|
184
|
+
}
|
|
185
|
+
loadEx(instance, origin) {
|
|
186
|
+
this.ctx.logger.trace('module:', instance, origin);
|
|
187
|
+
const parsed = (schema) => {
|
|
188
|
+
const result = schema.parseSafe(config);
|
|
189
|
+
if (!result.value)
|
|
190
|
+
throw new core_1.ModuleError(this.ctx.format('error.module.config', [pkg.name, result.error.message]));
|
|
191
|
+
return result.data;
|
|
192
|
+
};
|
|
158
193
|
const { main, pkg } = instance;
|
|
159
194
|
/* eslint-disable-next-line import/no-dynamic-require, global-require, @typescript-eslint/no-var-requires */
|
|
160
195
|
let obj = require(main);
|
|
161
|
-
let
|
|
196
|
+
let config = origin;
|
|
162
197
|
const adapterName = pkg.name.split(core_1.ADAPTER_PREFIX)[1];
|
|
163
198
|
if (core_1.Adapter.isPrototypeOf.call(core_1.Adapter, obj.default) &&
|
|
164
199
|
adapterName &&
|
|
165
200
|
(!obj.config || obj.config instanceof core_1.Parser)) {
|
|
201
|
+
/* Adapter Class */
|
|
166
202
|
this.ctx[core_1.Symbols.adapter].set(adapterName, [obj.default, obj.config]);
|
|
167
203
|
obj = {};
|
|
168
204
|
}
|
|
169
205
|
else if (core_1.Service.isPrototypeOf.call(core_1.Service, obj.default)) {
|
|
206
|
+
/* Service Class */
|
|
170
207
|
obj = {};
|
|
171
208
|
}
|
|
172
209
|
else if (obj.config instanceof core_1.Parser) {
|
|
173
|
-
|
|
174
|
-
if (!result.value)
|
|
175
|
-
throw new core_1.ModuleError(`Config format of module ${pkg.name} is error: ${result.error.message}`);
|
|
176
|
-
handle = result.data;
|
|
210
|
+
config = parsed(obj.config);
|
|
177
211
|
}
|
|
178
212
|
if (obj.lang)
|
|
179
|
-
this.
|
|
180
|
-
|
|
213
|
+
this.loadLang(obj.lang);
|
|
214
|
+
if (obj.default) {
|
|
215
|
+
if (obj.default.lang)
|
|
216
|
+
this.loadLang(obj.default.lang);
|
|
217
|
+
if (obj.default.config instanceof core_1.Parser)
|
|
218
|
+
config = parsed(obj.default.config);
|
|
219
|
+
}
|
|
220
|
+
else if (obj.Main) {
|
|
221
|
+
if (obj.Main.lang)
|
|
222
|
+
this.loadLang(obj.Main.lang);
|
|
223
|
+
if (obj.Main.config instanceof core_1.Parser)
|
|
224
|
+
config = parsed(obj.Main.config);
|
|
225
|
+
}
|
|
226
|
+
this.ctx.load({ name: pkg.name, ...obj, config });
|
|
181
227
|
}
|
|
182
228
|
unloadEx(instance) {
|
|
183
229
|
instance.files.forEach((file) => delete require.cache[require.resolve(file)]);
|
|
@@ -194,8 +240,8 @@ class Runner {
|
|
|
194
240
|
this.watcher();
|
|
195
241
|
}
|
|
196
242
|
watcher() {
|
|
197
|
-
this[core_1.Symbols.modules].forEach((data) => data[0].files.forEach((file) =>
|
|
198
|
-
this.ctx.logger.debug(
|
|
243
|
+
this[core_1.Symbols.modules].forEach((data) => data[0].files.forEach((file) => node_fs_1.default.watchFile(file, async () => {
|
|
244
|
+
this.ctx.logger.debug(this.ctx.format('loader.debug.reload', [data[0].pkg.name]));
|
|
199
245
|
this.unloadEx(data[0]);
|
|
200
246
|
this.loadEx(...data);
|
|
201
247
|
})));
|
|
@@ -6,3 +6,6 @@ export declare const BUILD_CONFIG_NAME = "kotori.yml";
|
|
|
6
6
|
export declare const DEV_CONFIG_NAME = "kotori.dev.yml";
|
|
7
7
|
export declare const SUPPORTS_VERSION: RegExp;
|
|
8
8
|
export declare const SUPPORTS_HALF_VERSION: RegExp;
|
|
9
|
+
export declare const BUILD_MODE: "build";
|
|
10
|
+
export declare const DEV_MODE: "dev";
|
|
11
|
+
export declare const DEV_SOURCE_MODE: "dev-source";
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEV_SOURCE_MODE = exports.DEV_MODE = exports.BUILD_MODE = exports.SUPPORTS_HALF_VERSION = exports.SUPPORTS_VERSION = exports.DEV_CONFIG_NAME = exports.BUILD_CONFIG_NAME = exports.DEV_IMPORT = exports.DEV_CODE_DIRS = exports.BUILD_FILE = exports.DEV_FILE = void 0;
|
|
4
|
+
exports.DEV_FILE = '.ts';
|
|
5
|
+
exports.BUILD_FILE = '.js';
|
|
6
|
+
exports.DEV_CODE_DIRS = './src/';
|
|
7
|
+
exports.DEV_IMPORT = `${exports.DEV_CODE_DIRS}index.ts`;
|
|
8
|
+
exports.BUILD_CONFIG_NAME = 'kotori.yml';
|
|
9
|
+
exports.DEV_CONFIG_NAME = 'kotori.dev.yml';
|
|
10
|
+
exports.SUPPORTS_VERSION = /(1\.1\.0)|(1\.2\.0)|(1\.3\.(.*))|(1\.4\.(.*))/;
|
|
11
|
+
exports.SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
|
|
12
|
+
exports.BUILD_MODE = 'build';
|
|
13
|
+
exports.DEV_MODE = 'dev';
|
|
14
|
+
exports.DEV_SOURCE_MODE = 'dev-source';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.plugins = void 0;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const core_1 = require("@kotori-bot/core");
|
|
10
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
11
|
+
function plugins(plugin) {
|
|
12
|
+
let pkgName;
|
|
13
|
+
if (!Array.isArray(plugin) && typeof plugin === 'object') {
|
|
14
|
+
pkgName = plugin.name;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
pkgName = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(...(Array.isArray(plugin) ? plugin : [plugin]), 'package.json')).toString()).name;
|
|
18
|
+
}
|
|
19
|
+
const pluginName = pkgName.split(core_1.PLUGIN_PREFIX)[1] ?? pkgName;
|
|
20
|
+
const ctx = core_1.Container.getInstance().extends(undefined, pluginName);
|
|
21
|
+
return new utils_1.default(ctx);
|
|
22
|
+
}
|
|
23
|
+
exports.plugins = plugins;
|
|
24
|
+
exports.default = plugins;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Context, EventsList, CommandAccess, MessageScope } from '@kotori-bot/core';
|
|
2
|
+
export declare class Decorators {
|
|
3
|
+
private readonly ctx;
|
|
4
|
+
private isCreated;
|
|
5
|
+
private pkgName;
|
|
6
|
+
private object?;
|
|
7
|
+
private register;
|
|
8
|
+
constructor(ctx: Context);
|
|
9
|
+
readonly import: (Target: object) => void;
|
|
10
|
+
readonly lang: <T extends object>(target: T, property: keyof T) => void;
|
|
11
|
+
readonly inject: <T extends object>(target: T, property: keyof T) => void;
|
|
12
|
+
readonly schema: <T extends object>(Target: T, property: keyof T) => void;
|
|
13
|
+
on<T extends keyof EventsList>(meta: {
|
|
14
|
+
type: T;
|
|
15
|
+
}): (...args: any[]) => void;
|
|
16
|
+
once<T extends keyof EventsList>(meta: {
|
|
17
|
+
type: T;
|
|
18
|
+
}): (...args: any[]) => void;
|
|
19
|
+
midware(meta?: {
|
|
20
|
+
priority: number;
|
|
21
|
+
}): (...args: any[]) => void;
|
|
22
|
+
command(meta: {
|
|
23
|
+
template: string;
|
|
24
|
+
alias?: string[];
|
|
25
|
+
description?: string;
|
|
26
|
+
help?: string;
|
|
27
|
+
scope?: MessageScope | 'all';
|
|
28
|
+
access?: CommandAccess;
|
|
29
|
+
options?: [string, string][];
|
|
30
|
+
}): (...args: any[]) => void;
|
|
31
|
+
regexp(meta: {
|
|
32
|
+
match: RegExp;
|
|
33
|
+
}): (...args: any[]) => void;
|
|
34
|
+
}
|
|
35
|
+
export default Decorators;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Decorators = void 0;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const core_1 = require("@kotori-bot/core");
|
|
6
|
+
class Decorators {
|
|
7
|
+
ctx;
|
|
8
|
+
isCreated = false;
|
|
9
|
+
pkgName;
|
|
10
|
+
object;
|
|
11
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
|
+
register(callback) {
|
|
13
|
+
return (...args) => this.ctx.parent.once('ready_module', (data) => {
|
|
14
|
+
if (data.instance.name === this.pkgName) {
|
|
15
|
+
callback(...args);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
this.register(callback);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
22
|
+
constructor(ctx) {
|
|
23
|
+
this.ctx = ctx;
|
|
24
|
+
this.pkgName = `${core_1.PLUGIN_PREFIX}${ctx.identity}`;
|
|
25
|
+
}
|
|
26
|
+
import = (Target) => {
|
|
27
|
+
this.register(() => {
|
|
28
|
+
if (!this.isCreated)
|
|
29
|
+
this.schema(Target, undefined);
|
|
30
|
+
})();
|
|
31
|
+
};
|
|
32
|
+
lang = (target, property) => {
|
|
33
|
+
this.register(() => {
|
|
34
|
+
const lang = target[property];
|
|
35
|
+
this.ctx.parent.i18n.use((0, node_path_1.resolve)(...(Array.isArray(lang) ? lang : [lang])));
|
|
36
|
+
})();
|
|
37
|
+
};
|
|
38
|
+
inject = (target, property) => {
|
|
39
|
+
this.register(() => {
|
|
40
|
+
const inject = target[property];
|
|
41
|
+
inject.forEach((identity) => this.ctx[core_1.Tokens.container].forEach((service, name) => {
|
|
42
|
+
if (!(service instanceof core_1.Service) || service.identity !== identity)
|
|
43
|
+
return;
|
|
44
|
+
this.ctx.inject(name);
|
|
45
|
+
}));
|
|
46
|
+
})();
|
|
47
|
+
};
|
|
48
|
+
schema = (Target, property) => {
|
|
49
|
+
this.register(() => {
|
|
50
|
+
let config = (this.ctx[core_1.Symbols.modules].get(this.pkgName) ?? [])[1];
|
|
51
|
+
const result = Target[property].parseSafe(config);
|
|
52
|
+
if (!result.value)
|
|
53
|
+
throw new core_1.ModuleError(`Config format of module ${this.pkgName} is error: ${result.error.message}`);
|
|
54
|
+
config = result.data;
|
|
55
|
+
if (this.isCreated)
|
|
56
|
+
return;
|
|
57
|
+
this.isCreated = true;
|
|
58
|
+
this.object = new Target(this.ctx, config);
|
|
59
|
+
})();
|
|
60
|
+
};
|
|
61
|
+
on(meta) {
|
|
62
|
+
return this.register((target, property) => this.ctx.on(meta.type, (...args) => target[property].bind(this.object)(...args)));
|
|
63
|
+
}
|
|
64
|
+
once(meta) {
|
|
65
|
+
return this.register((target, property) => this.ctx.once(meta.type, (...args) => target[property].bind(this.object)(...args)));
|
|
66
|
+
}
|
|
67
|
+
midware(meta) {
|
|
68
|
+
return this.register((target, property) => this.ctx.midware((next, session) => target[property].bind(this.object)(next, session), meta?.priority));
|
|
69
|
+
}
|
|
70
|
+
command(meta) {
|
|
71
|
+
return this.register((target, property) => {
|
|
72
|
+
const command = this.ctx
|
|
73
|
+
.command(meta.template, meta)
|
|
74
|
+
.action((data, session) => target[property].bind(this.object)(data, session));
|
|
75
|
+
meta.options?.forEach(([name, template]) => command.option(name, template));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
regexp(meta) {
|
|
79
|
+
return this.register((target, property) => this.ctx.regexp(meta.match, (match, session) => target[property].bind(this.object)(match, session)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.Decorators = Decorators;
|
|
83
|
+
exports.default = Decorators;
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @Blog: https://hotaru.icu
|
|
5
5
|
* @Date: 2023-10-29 16:20:51
|
|
6
6
|
* @LastEditors: Hotaru biyuehuya@gmail.com
|
|
7
|
-
* @LastEditTime: 2024-
|
|
7
|
+
* @LastEditTime: 2024-05-26 15:36:01
|
|
8
8
|
*/
|
|
9
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
|
@@ -21,6 +21,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
21
21
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
__exportStar(require("./loader"), exports);
|
|
25
|
-
__exportStar(require("./
|
|
24
|
+
__exportStar(require("./class/loader"), exports);
|
|
25
|
+
__exportStar(require("./constants"), exports);
|
|
26
|
+
__exportStar(require("./decorators"), exports);
|
|
27
|
+
__exportStar(require("./types"), exports);
|
|
26
28
|
__exportStar(require("@kotori-bot/logger"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Api, Adapter as OriginAdapter } from '@kotori-bot/core';
|
|
2
|
+
import { WsRouteHandler } from '../types/server';
|
|
3
|
+
export declare namespace Adapter {
|
|
4
|
+
abstract class WebSocket<T extends Api = Api> extends OriginAdapter<T> {
|
|
5
|
+
private isSetup;
|
|
6
|
+
private destroyFn?;
|
|
7
|
+
protected destroy(): void;
|
|
8
|
+
protected setup(): void;
|
|
9
|
+
abstract handle<T extends object>(data: T): void;
|
|
10
|
+
connection?: (ws: Parameters<WsRouteHandler>[0], req: Parameters<WsRouteHandler>[1]) => void;
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export default Adapter;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Adapter = void 0;
|
|
4
|
+
const core_1 = require("@kotori-bot/core");
|
|
5
|
+
/* eslint-disable-next-line @typescript-eslint/no-namespace */
|
|
6
|
+
var Adapter;
|
|
7
|
+
(function (Adapter) {
|
|
8
|
+
class WebSocket extends core_1.Adapter {
|
|
9
|
+
isSetup = false;
|
|
10
|
+
destroyFn;
|
|
11
|
+
destroy() {
|
|
12
|
+
if (!this.destroyFn)
|
|
13
|
+
return;
|
|
14
|
+
this.destroyFn();
|
|
15
|
+
this.isSetup = false;
|
|
16
|
+
}
|
|
17
|
+
setup() {
|
|
18
|
+
if (this.isSetup)
|
|
19
|
+
return;
|
|
20
|
+
this.ctx.inject('server');
|
|
21
|
+
this.destroyFn = this.ctx.server.wss(`/adapter/${this.identity}`, (ws, req) => {
|
|
22
|
+
if (this.connection)
|
|
23
|
+
this.connection(ws, req);
|
|
24
|
+
ws.on('message', (raw) => {
|
|
25
|
+
let data;
|
|
26
|
+
try {
|
|
27
|
+
data = JSON.parse(raw.toString());
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
this.ctx.logger.error(`Data parse error: ${e instanceof Error ? e.message : e}`);
|
|
31
|
+
}
|
|
32
|
+
if (data)
|
|
33
|
+
this.handle(data);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
this.isSetup = true;
|
|
37
|
+
}
|
|
38
|
+
connection;
|
|
39
|
+
start() {
|
|
40
|
+
this.setup();
|
|
41
|
+
}
|
|
42
|
+
stop() {
|
|
43
|
+
this.destroy();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
Adapter.WebSocket = WebSocket;
|
|
47
|
+
})(Adapter || (exports.Adapter = Adapter = {}));
|
|
48
|
+
exports.default = Adapter;
|
package/lib/service/file.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare class File extends Service {
|
|
|
3
3
|
constructor(ctx: Context);
|
|
4
4
|
getDir(): string;
|
|
5
5
|
getFile(filename: string): string;
|
|
6
|
-
load(filename: string, type?: Parameters<typeof loadConfig>[1], init?: Parameters<typeof loadConfig>[2]):
|
|
6
|
+
load<T = Parameters<typeof saveConfig>[1]>(filename: string, type?: Parameters<typeof loadConfig>[1], init?: Parameters<typeof loadConfig>[2]): T;
|
|
7
7
|
save(filename: string, data: Parameters<typeof saveConfig>[1], type?: Parameters<typeof saveConfig>[2]): void;
|
|
8
8
|
create(filename: string, data?: Parameters<typeof createConfig>[1], type?: Parameters<typeof createConfig>[2]): void;
|
|
9
9
|
}
|
package/lib/service/file.js
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.File = void 0;
|
|
4
4
|
const core_1 = require("@kotori-bot/core");
|
|
5
|
-
const
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
6
|
class File extends core_1.Service {
|
|
7
7
|
constructor(ctx) {
|
|
8
8
|
super(ctx, {}, 'file');
|
|
9
9
|
}
|
|
10
10
|
getDir() {
|
|
11
|
-
return (0,
|
|
11
|
+
return (0, node_path_1.join)(this.ctx.baseDir.data, ...(this.ctx.identity ? this.ctx.identity.split('/') : []));
|
|
12
12
|
}
|
|
13
13
|
getFile(filename) {
|
|
14
|
-
return (0,
|
|
14
|
+
return (0, node_path_1.join)(this.getDir(), filename);
|
|
15
15
|
}
|
|
16
|
+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
16
17
|
load(filename, type, init) {
|
|
17
18
|
return (0, core_1.loadConfig)(this.getFile(filename), type, init);
|
|
18
19
|
}
|
package/lib/service/server.d.ts
CHANGED
|
@@ -1,26 +1,63 @@
|
|
|
1
|
-
/// <reference types="
|
|
2
|
-
/// <reference types="
|
|
3
|
-
/// <reference types="serve-static" />
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
4
3
|
import { Context, Service } from '@kotori-bot/core';
|
|
5
|
-
import
|
|
4
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import { HttpRouteHandler, HttpRoutes, WsRouteHandler } from '../types/server';
|
|
6
6
|
interface ServerConfig {
|
|
7
7
|
port: number;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
interface BodyParserOptions {
|
|
10
|
+
inflate?: boolean | undefined;
|
|
11
|
+
limit?: number | string | undefined;
|
|
12
|
+
type?: string | string[] | ((req: IncomingMessage) => any) | undefined;
|
|
13
|
+
verify?(req: IncomingMessage, res: ServerResponse, buf: Buffer, encoding: string): void;
|
|
14
|
+
}
|
|
15
|
+
interface UrlencodedOptions extends BodyParserOptions {
|
|
16
|
+
extended?: boolean | undefined;
|
|
17
|
+
parameterLimit?: number | undefined;
|
|
18
|
+
}
|
|
19
|
+
interface ServeStaticOptions<R extends ServerResponse = ServerResponse> {
|
|
20
|
+
acceptRanges?: boolean | undefined;
|
|
21
|
+
cacheControl?: boolean | undefined;
|
|
22
|
+
dotfiles?: string | undefined;
|
|
23
|
+
etag?: boolean | undefined;
|
|
24
|
+
extensions?: string[] | false | undefined;
|
|
25
|
+
fallthrough?: boolean | undefined;
|
|
26
|
+
immutable?: boolean | undefined;
|
|
27
|
+
index?: boolean | string | string[] | undefined;
|
|
28
|
+
lastModified?: boolean | undefined;
|
|
29
|
+
maxAge?: number | string | undefined;
|
|
30
|
+
redirect?: boolean | undefined;
|
|
31
|
+
setHeaders?: ((res: R, path: string, stat: any) => any) | undefined;
|
|
32
|
+
}
|
|
33
|
+
interface RouterOptions {
|
|
34
|
+
caseSensitive?: boolean | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* @default false
|
|
37
|
+
* @since 4.5.0
|
|
38
|
+
*/
|
|
39
|
+
mergeParams?: boolean | undefined;
|
|
40
|
+
strict?: boolean | undefined;
|
|
41
|
+
}
|
|
42
|
+
export declare class Server extends Service<ServerConfig> implements HttpRoutes {
|
|
10
43
|
private app;
|
|
11
|
-
private server
|
|
44
|
+
private server;
|
|
45
|
+
private wsServer;
|
|
46
|
+
private wsRoutes;
|
|
12
47
|
constructor(ctx: Context, config: ServerConfig);
|
|
13
48
|
start(): void;
|
|
14
49
|
stop(): void;
|
|
15
|
-
get:
|
|
16
|
-
post:
|
|
17
|
-
patch:
|
|
18
|
-
put:
|
|
19
|
-
delete:
|
|
20
|
-
all:
|
|
21
|
-
use:
|
|
22
|
-
router:
|
|
23
|
-
json: (options?:
|
|
24
|
-
static:
|
|
50
|
+
get<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
51
|
+
post<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
52
|
+
patch<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
53
|
+
put<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
54
|
+
delete<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
55
|
+
all<P extends string>(path: P, ...callback: HttpRouteHandler<P>[]): void;
|
|
56
|
+
use<P extends string>(path: P | HttpRouteHandler | HttpRoutes, ...callback: (HttpRouteHandler<P> | HttpRoutes)[]): void;
|
|
57
|
+
router: (options?: RouterOptions) => HttpRoutes;
|
|
58
|
+
json: (options?: BodyParserOptions) => HttpRouteHandler;
|
|
59
|
+
static: (root: string, options?: ServeStaticOptions) => HttpRouteHandler;
|
|
60
|
+
urlencoded: (options?: UrlencodedOptions) => HttpRouteHandler;
|
|
61
|
+
wss<P extends string>(path: P, callback: WsRouteHandler<P>): () => boolean;
|
|
25
62
|
}
|
|
26
63
|
export default Server;
|
package/lib/service/server.js
CHANGED
|
@@ -5,14 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Server = void 0;
|
|
7
7
|
const core_1 = require("@kotori-bot/core");
|
|
8
|
+
const node_http_1 = require("node:http");
|
|
9
|
+
const path_to_regexp_1 = require("path-to-regexp");
|
|
8
10
|
const express_1 = __importDefault(require("express"));
|
|
11
|
+
const ws_1 = __importDefault(require("ws"));
|
|
9
12
|
class Server extends core_1.Service {
|
|
10
13
|
app;
|
|
11
14
|
server;
|
|
15
|
+
wsServer;
|
|
16
|
+
wsRoutes = new Map();
|
|
12
17
|
constructor(ctx, config) {
|
|
13
18
|
super(ctx, config, 'server');
|
|
14
19
|
this.app = (0, express_1.default)();
|
|
15
|
-
this.app.use(
|
|
20
|
+
this.app.use(express_1.default.json());
|
|
21
|
+
this.app.use('/', (req, res, next) => {
|
|
16
22
|
let isWebui = false;
|
|
17
23
|
ctx[core_1.Symbols.modules].forEach((module) => {
|
|
18
24
|
if (isWebui)
|
|
@@ -20,41 +26,84 @@ class Server extends core_1.Service {
|
|
|
20
26
|
if (module[0].pkg.name === '@kotori-bot/kotori-plugin-webui')
|
|
21
27
|
isWebui = true;
|
|
22
28
|
});
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
if (isWebui || req.url !== '/') {
|
|
30
|
+
next();
|
|
31
|
+
return;
|
|
26
32
|
}
|
|
27
|
-
|
|
33
|
+
res.setHeader('Content-type', 'text/html');
|
|
34
|
+
res.send(/* html */ `<h1>Welcome to kotori!</h1>`);
|
|
35
|
+
});
|
|
36
|
+
this.server = (0, node_http_1.createServer)(this.app);
|
|
37
|
+
this.wsServer = new ws_1.default.Server({ noServer: true });
|
|
38
|
+
this.server.on('upgrade', (req, socket, head) => {
|
|
39
|
+
this.wsServer.handleUpgrade(req, socket, head, (ws) => {
|
|
40
|
+
this.wsServer.emit('connection', ws, req);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
this.wsServer.on('connection', (ws, req) => {
|
|
44
|
+
let triggered = false;
|
|
45
|
+
/* eslint-disable no-restricted-syntax,no-continue */
|
|
46
|
+
for (const [template, list] of this.wsRoutes.entries()) {
|
|
47
|
+
if (!req.url)
|
|
48
|
+
continue;
|
|
49
|
+
const result = (0, path_to_regexp_1.match)(template, { decode: decodeURIComponent })(req.url);
|
|
50
|
+
if (!result)
|
|
51
|
+
continue;
|
|
52
|
+
if (!triggered)
|
|
53
|
+
triggered = true;
|
|
54
|
+
list.forEach((callback) => {
|
|
55
|
+
callback(ws, Object.assign(req, { params: result.params }));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/* eslint-enable no-restricted-syntax,no-continue */
|
|
59
|
+
if (!triggered)
|
|
60
|
+
ws.close(1002);
|
|
28
61
|
});
|
|
29
|
-
this.get = this.app.get.bind(this.app);
|
|
30
|
-
this.post = this.app.post.bind(this.app);
|
|
31
|
-
this.patch = this.app.patch.bind(this.app);
|
|
32
|
-
this.put = this.app.put.bind(this.app);
|
|
33
|
-
this.delete = this.app.delete.bind(this.app);
|
|
34
|
-
this.use = this.app.use.bind(this.app);
|
|
35
|
-
this.all = this.app.all.bind(this.app);
|
|
36
62
|
}
|
|
37
63
|
start() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
this.server.listen(this.config.port, () => {
|
|
65
|
+
this.ctx.logger.label('server').info(`http server start at http://127.0.0.1:${this.config.port}`);
|
|
66
|
+
this.ctx.logger.label('server').info(`websocket server start at ws://127.0.0.1:${this.config.port}`);
|
|
67
|
+
});
|
|
42
68
|
}
|
|
43
69
|
stop() {
|
|
44
|
-
|
|
45
|
-
return;
|
|
70
|
+
this.wsServer.close();
|
|
46
71
|
this.server.close();
|
|
47
72
|
}
|
|
48
|
-
get
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
73
|
+
get(path, ...callback) {
|
|
74
|
+
this.app.get(path, ...callback);
|
|
75
|
+
}
|
|
76
|
+
post(path, ...callback) {
|
|
77
|
+
this.app.post(path, ...callback);
|
|
78
|
+
}
|
|
79
|
+
patch(path, ...callback) {
|
|
80
|
+
this.app.patch(path, ...callback);
|
|
81
|
+
}
|
|
82
|
+
put(path, ...callback) {
|
|
83
|
+
this.app.put(path, ...callback);
|
|
84
|
+
}
|
|
85
|
+
delete(path, ...callback) {
|
|
86
|
+
this.app.delete(path, ...callback);
|
|
87
|
+
}
|
|
88
|
+
all(path, ...callback) {
|
|
89
|
+
this.app.all(path, ...callback);
|
|
90
|
+
}
|
|
91
|
+
use(path, ...callback) {
|
|
92
|
+
if (typeof path === 'string')
|
|
93
|
+
this.app.use(path, ...callback);
|
|
94
|
+
else
|
|
95
|
+
this.app.use('/', path, ...callback);
|
|
96
|
+
}
|
|
55
97
|
router = express_1.default.Router;
|
|
56
98
|
json = express_1.default.json;
|
|
57
99
|
static = express_1.default.static;
|
|
100
|
+
urlencoded = express_1.default.urlencoded;
|
|
101
|
+
wss(path, callback) {
|
|
102
|
+
const list = this.wsRoutes.get(path) || new Set();
|
|
103
|
+
list.add(callback);
|
|
104
|
+
this.wsRoutes.set(path, list);
|
|
105
|
+
return () => list.delete(callback);
|
|
106
|
+
}
|
|
58
107
|
}
|
|
59
108
|
exports.Server = Server;
|
|
60
109
|
exports.default = Server;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './server';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./server"), exports);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { NextFunction, Request, Response } from 'express';
|
|
3
|
+
import { IncomingMessage } from 'node:http';
|
|
4
|
+
import Ws from 'ws';
|
|
5
|
+
type RemoveTail<S extends string, Tail extends string> = S extends `${infer P}${Tail}` ? P : S;
|
|
6
|
+
type GetRouteParameter<S extends string> = RemoveTail<RemoveTail<RemoveTail<S, `/${string}`>, `-${string}`>, `.${string}`>;
|
|
7
|
+
interface ParamsDictionary {
|
|
8
|
+
[key: string]: string;
|
|
9
|
+
}
|
|
10
|
+
type RouteParameters<Route extends string> = string extends Route ? ParamsDictionary : Route extends `${string}(${string}` ? ParamsDictionary : Route extends `${string}:${infer Rest}` ? (GetRouteParameter<Rest> extends never ? ParamsDictionary : GetRouteParameter<Rest> extends `${infer ParamName}?` ? {
|
|
11
|
+
[P in ParamName]?: string;
|
|
12
|
+
} : {
|
|
13
|
+
[P in GetRouteParameter<Rest>]: string;
|
|
14
|
+
}) & (Rest extends `${GetRouteParameter<Rest>}${infer Next}` ? RouteParameters<Next> : unknown) : object;
|
|
15
|
+
export type HttpRouteHandler<P extends string = string> = (req: Request<RouteParameters<P>>, res: Response, next: NextFunction) => unknown;
|
|
16
|
+
type KeyList = 'get' | 'post' | 'patch' | 'put' | 'delete' | 'all';
|
|
17
|
+
export type WsRouteHandler<P extends string = ''> = (ws: Ws, req: IncomingMessage & {
|
|
18
|
+
params: RouteParameters<P>;
|
|
19
|
+
}) => void;
|
|
20
|
+
type HttpRoutesReflect = {
|
|
21
|
+
[K in KeyList]: <P extends string>(path: P, ...callback: HttpRouteHandler<P>[]) => unknown;
|
|
22
|
+
};
|
|
23
|
+
export interface HttpRoutes extends HttpRoutesReflect {
|
|
24
|
+
use<P extends string>(path: P, ...callback: (HttpRouteHandler<P> | HttpRoutes)[]): unknown;
|
|
25
|
+
use(...callback: (HttpRouteHandler | HttpRoutes)[]): unknown;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kotori-bot/loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0-beta.1",
|
|
4
4
|
"description": "Loader For KotoriBot",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -24,9 +24,11 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://kotori.js.org",
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@types/ws": "^8.5.8",
|
|
27
28
|
"express": "^4.18.2",
|
|
28
29
|
"knex": "^3.1.0",
|
|
29
|
-
"
|
|
30
|
+
"ws": "^8.14.2",
|
|
31
|
+
"@kotori-bot/core": "^1.4.0",
|
|
30
32
|
"@kotori-bot/logger": "^1.2.0"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
package/lib/consts.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SUPPORTS_HALF_VERSION = exports.SUPPORTS_VERSION = exports.DEV_CONFIG_NAME = exports.BUILD_CONFIG_NAME = exports.DEV_IMPORT = exports.DEV_CODE_DIRS = exports.BUILD_FILE = exports.DEV_FILE = void 0;
|
|
4
|
-
exports.DEV_FILE = '.ts';
|
|
5
|
-
exports.BUILD_FILE = '.js';
|
|
6
|
-
exports.DEV_CODE_DIRS = './src/';
|
|
7
|
-
exports.DEV_IMPORT = `${exports.DEV_CODE_DIRS}index.ts`;
|
|
8
|
-
exports.BUILD_CONFIG_NAME = 'kotori.yml';
|
|
9
|
-
exports.DEV_CONFIG_NAME = 'kotori.dev.yml';
|
|
10
|
-
exports.SUPPORTS_VERSION = /(1\.1\.0)|(1\.2\.0)|(1\.3\.(.*?))/;
|
|
11
|
-
exports.SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
|
|
File without changes
|
|
File without changes
|