@kotori-bot/loader 1.4.2 → 1.5.0
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/class/loader.d.ts +41 -0
- package/lib/class/loader.js +268 -0
- package/lib/class/runner.d.ts +60 -0
- package/lib/class/runner.js +233 -0
- package/lib/constants.d.ts +11 -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 +3 -2
- package/lib/index.js +4 -3
- package/lib/loader.js +7 -7
- package/lib/runner.js +5 -5
- package/lib/service/file.d.ts +1 -1
- package/lib/service/file.js +1 -0
- package/lib/service/server.d.ts +5 -0
- package/lib/service/server.js +41 -7
- package/lib/utils/log.d.ts +3 -0
- package/lib/utils/log.js +20 -0
- package/package.json +4 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Container, Symbols } from '@kotori-bot/core';
|
|
2
|
+
import Logger from '@kotori-bot/logger';
|
|
3
|
+
import Runner from './runner';
|
|
4
|
+
import Server from '../service/server';
|
|
5
|
+
import type Database from '../service/database';
|
|
6
|
+
import File from '../service/file';
|
|
7
|
+
declare module '@kotori-bot/core' {
|
|
8
|
+
interface Context {
|
|
9
|
+
readonly baseDir: Runner['baseDir'];
|
|
10
|
+
readonly options: Runner['options'];
|
|
11
|
+
readonly [Symbols.modules]: Runner[typeof Symbols.modules];
|
|
12
|
+
loadAll(): void;
|
|
13
|
+
watcher(): void;
|
|
14
|
+
logger: Logger;
|
|
15
|
+
server: Server;
|
|
16
|
+
db: Database;
|
|
17
|
+
file: File;
|
|
18
|
+
}
|
|
19
|
+
interface GlobalConfig {
|
|
20
|
+
dirs: string[];
|
|
21
|
+
port: number;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export declare class Loader extends Container {
|
|
25
|
+
private readonly ctx;
|
|
26
|
+
private loadCount;
|
|
27
|
+
constructor(options?: {
|
|
28
|
+
dir?: string;
|
|
29
|
+
mode?: string;
|
|
30
|
+
level?: number;
|
|
31
|
+
});
|
|
32
|
+
run(): void;
|
|
33
|
+
private handleError;
|
|
34
|
+
private catchError;
|
|
35
|
+
private listenMessage;
|
|
36
|
+
private setPreService;
|
|
37
|
+
private loadAllModules;
|
|
38
|
+
private loadAllAdapter;
|
|
39
|
+
private checkUpdate;
|
|
40
|
+
}
|
|
41
|
+
export default Loader;
|
|
@@ -0,0 +1,268 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Loader = void 0;
|
|
30
|
+
/*
|
|
31
|
+
* @Author: hotaru biyuehuya@gmail.com
|
|
32
|
+
* @Blog: https://hotaru.icu
|
|
33
|
+
* @Date: 2023-06-24 15:12:55
|
|
34
|
+
* @LastEditors: Hotaru biyuehuya@gmail.com
|
|
35
|
+
* @LastEditTime: 2024-05-03 22:05:36
|
|
36
|
+
*/
|
|
37
|
+
const core_1 = require("@kotori-bot/core");
|
|
38
|
+
const path_1 = __importDefault(require("path"));
|
|
39
|
+
const fs_1 = __importDefault(require("fs"));
|
|
40
|
+
const logger_1 = __importStar(require("@kotori-bot/logger"));
|
|
41
|
+
const runner_1 = __importStar(require("./runner"));
|
|
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
|
+
const handle = (root) => {
|
|
48
|
+
const baseDir = {
|
|
49
|
+
root,
|
|
50
|
+
modules: path_1.default.join(root, 'modules'),
|
|
51
|
+
data: path_1.default.join(root, 'data'),
|
|
52
|
+
logs: path_1.default.join(root, 'logs')
|
|
53
|
+
};
|
|
54
|
+
Object.values(baseDir).forEach((val) => {
|
|
55
|
+
if (!fs_1.default.existsSync(val))
|
|
56
|
+
fs_1.default.mkdirSync(val);
|
|
57
|
+
});
|
|
58
|
+
return baseDir;
|
|
59
|
+
};
|
|
60
|
+
if (dir)
|
|
61
|
+
return handle(path_1.default.resolve(dir));
|
|
62
|
+
let root = path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
|
|
63
|
+
let count = 0;
|
|
64
|
+
while (!fs_1.default.existsSync(path_1.default.join(root, file))) {
|
|
65
|
+
if (count > 5) {
|
|
66
|
+
logger_1.default.fatal(`cannot find file ${file} `);
|
|
67
|
+
process.exit();
|
|
68
|
+
}
|
|
69
|
+
root = path_1.default.join(root, '..');
|
|
70
|
+
count += 1;
|
|
71
|
+
}
|
|
72
|
+
return handle(root);
|
|
73
|
+
}
|
|
74
|
+
/* eslint consistent-return: 0 */
|
|
75
|
+
function getCoreConfig(file, baseDir) {
|
|
76
|
+
try {
|
|
77
|
+
const result1 = core_1.Tsu.Object({
|
|
78
|
+
global: core_1.Tsu.Object({
|
|
79
|
+
dirs: core_1.Tsu.Array(core_1.Tsu.String()).default([]),
|
|
80
|
+
port: core_1.Tsu.Number().default(core_1.DEFAULT_PORT),
|
|
81
|
+
lang: runner_1.localeTypeSchema.default(core_1.DEFAULT_CORE_CONFIG.global.lang),
|
|
82
|
+
'command-prefix': core_1.Tsu.String().default(core_1.DEFAULT_CORE_CONFIG.global['command-prefix'])
|
|
83
|
+
}),
|
|
84
|
+
plugin: core_1.Tsu.Object({})
|
|
85
|
+
.index(core_1.Tsu.Object({
|
|
86
|
+
filter: core_1.Tsu.Object({}).default({})
|
|
87
|
+
}).default({ filter: {} }))
|
|
88
|
+
.default(core_1.DEFAULT_CORE_CONFIG.plugin)
|
|
89
|
+
})
|
|
90
|
+
.default({ global: Object.assign(core_1.DEFAULT_CORE_CONFIG.global), plugin: core_1.DEFAULT_CORE_CONFIG.plugin })
|
|
91
|
+
.parse((0, core_1.loadConfig)(path_1.default.join(baseDir.root, file), 'yaml'));
|
|
92
|
+
return core_1.Tsu.Object({
|
|
93
|
+
adapter: core_1.Tsu.Object({})
|
|
94
|
+
.index(core_1.Tsu.Object({
|
|
95
|
+
extends: core_1.Tsu.String(),
|
|
96
|
+
master: core_1.Tsu.Union([core_1.Tsu.Number(), core_1.Tsu.String()]),
|
|
97
|
+
lang: runner_1.localeTypeSchema.default(result1.global.lang),
|
|
98
|
+
'command-prefix': core_1.Tsu.String().default(result1.global['command-prefix'])
|
|
99
|
+
}))
|
|
100
|
+
.default(core_1.DEFAULT_CORE_CONFIG.adapter)
|
|
101
|
+
}).parse(result1);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (!(err instanceof core_1.TsuError))
|
|
105
|
+
throw err;
|
|
106
|
+
logger_1.default.fatal(`file ${file} format error: ${err.message}`);
|
|
107
|
+
process.exit();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
class Loader extends core_1.Container {
|
|
111
|
+
ctx;
|
|
112
|
+
loadCount = 0;
|
|
113
|
+
constructor(options) {
|
|
114
|
+
super();
|
|
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
|
+
};
|
|
121
|
+
const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
|
|
122
|
+
ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
|
|
123
|
+
ctx.mixin('runner', ['baseDir', 'options']);
|
|
124
|
+
core_1.Container.setInstance(ctx);
|
|
125
|
+
this.ctx = core_1.Container.getInstance();
|
|
126
|
+
this.ctx.logger.trace(`options:`, options);
|
|
127
|
+
this.ctx.logger.trace(`runnerConfig:`, runnerConfig);
|
|
128
|
+
this.ctx.logger.trace(`baseDir:`, this.ctx.baseDir);
|
|
129
|
+
this.ctx.logger.trace(`options:`, this.ctx.options);
|
|
130
|
+
this.ctx.logger.trace(`config:`, this.ctx.config);
|
|
131
|
+
this.ctx.logger.trace(`where:`, __dirname, __filename);
|
|
132
|
+
this.ctx.logger.trace(`running:`, process.cwd());
|
|
133
|
+
}
|
|
134
|
+
run() {
|
|
135
|
+
(0, log_1.default)(this.ctx.pkg, this.ctx);
|
|
136
|
+
this.catchError();
|
|
137
|
+
this.listenMessage();
|
|
138
|
+
this.setPreService();
|
|
139
|
+
this.loadAllModules();
|
|
140
|
+
this.checkUpdate();
|
|
141
|
+
}
|
|
142
|
+
handleError(err, prefix) {
|
|
143
|
+
if (!(err instanceof core_1.KotoriError)) {
|
|
144
|
+
if (err instanceof Error) {
|
|
145
|
+
this.ctx.logger.label(prefix).error(err.message, err.stack);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.ctx.logger.label(prefix).error(err);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const list = {
|
|
153
|
+
ServiceError: () => this.ctx.logger.label('service').warn,
|
|
154
|
+
ModuleError: () => this.ctx.logger.label('module').error,
|
|
155
|
+
UnknownError: () => this.ctx.logger.error,
|
|
156
|
+
DevError: () => this.ctx.logger.label('error').debug
|
|
157
|
+
};
|
|
158
|
+
list[err.name]().bind(this.ctx.logger)(err.message, err.stack);
|
|
159
|
+
}
|
|
160
|
+
catchError() {
|
|
161
|
+
process.on('uncaughtExceptionMonitor', (err) => this.handleError(err, 'sync'));
|
|
162
|
+
process.on('unhandledRejection', (err) => this.handleError(err, 'async'));
|
|
163
|
+
process.on('SIGINT', () => process.exit());
|
|
164
|
+
this.ctx.logger.debug('run info: develop with debugging...');
|
|
165
|
+
}
|
|
166
|
+
listenMessage() {
|
|
167
|
+
this.ctx.on('connect', (data) => {
|
|
168
|
+
const { type, mode, normal, address, adapter } = data;
|
|
169
|
+
let msg;
|
|
170
|
+
if (type === 'connect') {
|
|
171
|
+
switch (mode) {
|
|
172
|
+
case 'ws':
|
|
173
|
+
msg = `${normal ? 'Connect' : 'Reconnect'} server to ${address}`;
|
|
174
|
+
break;
|
|
175
|
+
case 'ws-reverse':
|
|
176
|
+
msg = `server ${normal ? 'start' : 'restart'} at ${address}`;
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
msg = `ready completed about ${address}`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
switch (mode) {
|
|
184
|
+
case 'ws':
|
|
185
|
+
msg = `disconnect server from ${address}${normal ? '' : ' unexpectedly'}`;
|
|
186
|
+
break;
|
|
187
|
+
case 'ws-reverse':
|
|
188
|
+
msg = `server stop at ${address}${normal ? '' : ' unexpectedly'}`;
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
msg = `dispose completed about ${address}`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
adapter.ctx.logger[normal ? 'info' : 'warn'](msg);
|
|
195
|
+
});
|
|
196
|
+
this.ctx.on('status', (data) => {
|
|
197
|
+
const { status, adapter } = data;
|
|
198
|
+
adapter.ctx.logger.info(status);
|
|
199
|
+
});
|
|
200
|
+
this.ctx.on('ready_module', (data) => {
|
|
201
|
+
if (typeof data.instance !== 'object')
|
|
202
|
+
return;
|
|
203
|
+
const pkg = data.instance.name
|
|
204
|
+
? this.ctx.get('runner')[core_1.Symbols.modules].get(data.instance.name)
|
|
205
|
+
: undefined;
|
|
206
|
+
if (!pkg)
|
|
207
|
+
return;
|
|
208
|
+
this.loadCount += 1;
|
|
209
|
+
const { name, version, author, peerDependencies } = pkg[0].pkg;
|
|
210
|
+
this.ctx.logger.info(`loaded module ${name} version: ${version} ${Array.isArray(author) ? `authors: ${author.join(',')}` : `author: ${author}`}`);
|
|
211
|
+
const requiredVersion = peerDependencies['kotori-bot'];
|
|
212
|
+
if (!requiredVersion.includes('workspace') &&
|
|
213
|
+
(!constants_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
|
|
214
|
+
if (constants_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
|
|
215
|
+
this.ctx.logger.warn(`incomplete supported module version: ${requiredVersion}`);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
this.ctx.logger.error(`unsupported module version: ${requiredVersion}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
setPreService() {
|
|
224
|
+
this.ctx.service('server', new server_1.default(this.ctx.extends(), { port: this.ctx.config.global.port }));
|
|
225
|
+
this.ctx.service('file', new file_1.default(this.ctx.extends()));
|
|
226
|
+
}
|
|
227
|
+
loadAllModules() {
|
|
228
|
+
this.ctx.get('runner').loadAll();
|
|
229
|
+
const failLoadCount = this.ctx.get('runner')[core_1.Symbols.modules].size - this.loadCount;
|
|
230
|
+
this.ctx.logger.info(`loaded ${this.loadCount} modules successfully${failLoadCount > 0 ? `, failed to load ${failLoadCount} modules` : ''} `);
|
|
231
|
+
this.loadAllAdapter();
|
|
232
|
+
this.ctx.emit('ready');
|
|
233
|
+
}
|
|
234
|
+
loadAllAdapter() {
|
|
235
|
+
const adapters = this.ctx[core_1.Symbols.adapter];
|
|
236
|
+
Object.keys(this.ctx.config.adapter).forEach((botName) => {
|
|
237
|
+
const botConfig = this.ctx.config.adapter[botName];
|
|
238
|
+
const array = adapters.get(botConfig.extends);
|
|
239
|
+
if (!array) {
|
|
240
|
+
this.ctx.logger.warn(`cannot find adapter '${botConfig.extends}' for ${botName}`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const result = array[1]?.parseSafe(botConfig);
|
|
244
|
+
if (result && !result.value)
|
|
245
|
+
throw new core_1.ModuleError(`Config format of adapter ${botName} is error: ${result.error.message}`);
|
|
246
|
+
const bot = new array[0](this.ctx.extends({}, `${botConfig.extends}/${botName}`), result ? result.data : botConfig, botName);
|
|
247
|
+
this.ctx.on('ready', () => bot.start());
|
|
248
|
+
this.ctx.on('dispose', () => bot.stop());
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
async checkUpdate() {
|
|
252
|
+
const { version } = this.ctx.pkg;
|
|
253
|
+
const res = await this.ctx.http
|
|
254
|
+
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* GLOBAL.UPDATE */)
|
|
255
|
+
.catch(() => this.ctx.logger.error('get update failed, please check your network'));
|
|
256
|
+
if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
|
|
257
|
+
this.ctx.logger.warn(`detection update failed`);
|
|
258
|
+
}
|
|
259
|
+
else if (version === res.version) {
|
|
260
|
+
this.ctx.logger.info('kotori is currently the latest version');
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
this.ctx.logger.warn(`the current version of Kotori is ${version}, and the latest version is ${res.version}. please go to ${"https://github.com/kotorijs/kotori" /* GLOBAL.REPO */} to update`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.Loader = Loader;
|
|
268
|
+
exports.default = Loader;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Context, LocaleType, ModuleConfig, Symbols } from '@kotori-bot/core';
|
|
2
|
+
import { BUILD_MODE, DEV_MODE, DEV_SOURCE_MODE } from '../constants';
|
|
3
|
+
interface BaseDir {
|
|
4
|
+
root: string;
|
|
5
|
+
modules: string;
|
|
6
|
+
data: string;
|
|
7
|
+
logs: string;
|
|
8
|
+
}
|
|
9
|
+
interface Options {
|
|
10
|
+
mode: typeof BUILD_MODE | typeof DEV_MODE | typeof DEV_SOURCE_MODE;
|
|
11
|
+
}
|
|
12
|
+
interface RunnerConfig {
|
|
13
|
+
baseDir: BaseDir;
|
|
14
|
+
options: Options;
|
|
15
|
+
level: number;
|
|
16
|
+
}
|
|
17
|
+
interface ModulePackage {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
description: string;
|
|
21
|
+
main: string;
|
|
22
|
+
keywords: string[];
|
|
23
|
+
license: 'GPL-3.0';
|
|
24
|
+
author: string | string[];
|
|
25
|
+
peerDependencies: {
|
|
26
|
+
'kotori-bot': string;
|
|
27
|
+
[propName: string]: string;
|
|
28
|
+
};
|
|
29
|
+
kotori: {
|
|
30
|
+
enforce?: 'pre' | 'post';
|
|
31
|
+
meta: {
|
|
32
|
+
language: LocaleType[];
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface ModuleMeta {
|
|
37
|
+
pkg: ModulePackage;
|
|
38
|
+
files: string[];
|
|
39
|
+
main: string;
|
|
40
|
+
}
|
|
41
|
+
export declare const localeTypeSchema: import("@kotori-bot/core").UnionParser<[import("@kotori-bot/core").UnionParser<[import("@kotori-bot/core").LiteralParser<"en_US">, import("@kotori-bot/core").LiteralParser<"ja_JP">]>, import("@kotori-bot/core").UnionParser<[import("@kotori-bot/core").LiteralParser<"zh_TW">, import("@kotori-bot/core").AnyParser<any>]>]>;
|
|
42
|
+
export declare class Runner {
|
|
43
|
+
readonly baseDir: BaseDir;
|
|
44
|
+
readonly options: Options;
|
|
45
|
+
private readonly ctx;
|
|
46
|
+
private readonly isDev;
|
|
47
|
+
private readonly isSourceDev;
|
|
48
|
+
readonly [Symbols.modules]: Map<string, [ModuleMeta, ModuleConfig]>;
|
|
49
|
+
constructor(ctx: Context, config: RunnerConfig);
|
|
50
|
+
private getDirFiles;
|
|
51
|
+
private getModuleRootDir;
|
|
52
|
+
private checkModuleFiles;
|
|
53
|
+
private getModuleList;
|
|
54
|
+
private loadLang;
|
|
55
|
+
private loadEx;
|
|
56
|
+
private unloadEx;
|
|
57
|
+
loadAll(): void;
|
|
58
|
+
watcher(): void;
|
|
59
|
+
}
|
|
60
|
+
export default Runner;
|
|
@@ -0,0 +1,233 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Runner = exports.localeTypeSchema = void 0;
|
|
30
|
+
const fs_1 = __importStar(require("fs"));
|
|
31
|
+
const path_1 = __importStar(require("path"));
|
|
32
|
+
const core_1 = require("@kotori-bot/core");
|
|
33
|
+
const logger_1 = require("@kotori-bot/logger");
|
|
34
|
+
const constants_1 = require("../constants");
|
|
35
|
+
const logger_2 = __importDefault(require("../utils/logger"));
|
|
36
|
+
exports.localeTypeSchema = core_1.Tsu.Union([
|
|
37
|
+
core_1.Tsu.Union([core_1.Tsu.Literal('en_US'), core_1.Tsu.Literal('ja_JP')]),
|
|
38
|
+
core_1.Tsu.Union([core_1.Tsu.Literal('zh_TW'), core_1.Tsu.Any()])
|
|
39
|
+
]);
|
|
40
|
+
const modulePackageSchema = core_1.Tsu.Object({
|
|
41
|
+
name: core_1.Tsu.String().regexp(/kotori-plugin-[a-z]([a-z,0-9]{2,13})\b/),
|
|
42
|
+
version: core_1.Tsu.String(),
|
|
43
|
+
description: core_1.Tsu.String(),
|
|
44
|
+
main: core_1.Tsu.String(),
|
|
45
|
+
license: core_1.Tsu.Literal('GPL-3.0'),
|
|
46
|
+
keywords: core_1.Tsu.Custom((val) => Array.isArray(val) && val.includes('kotori') && val.includes('chatbot') && val.includes('kotori-plugin')),
|
|
47
|
+
author: core_1.Tsu.Union([core_1.Tsu.String(), core_1.Tsu.Array(core_1.Tsu.String())]),
|
|
48
|
+
peerDependencies: core_1.Tsu.Object({
|
|
49
|
+
'kotori-bot': core_1.Tsu.String()
|
|
50
|
+
}),
|
|
51
|
+
kotori: core_1.Tsu.Object({
|
|
52
|
+
enforce: core_1.Tsu.Union([core_1.Tsu.Literal('pre'), core_1.Tsu.Literal('post')]).optional(),
|
|
53
|
+
meta: core_1.Tsu.Object({
|
|
54
|
+
language: core_1.Tsu.Array(exports.localeTypeSchema).default([])
|
|
55
|
+
}).default({ language: [] })
|
|
56
|
+
}).default({
|
|
57
|
+
enforce: undefined,
|
|
58
|
+
meta: { language: [] }
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
function moduleLoadOrder(pkg) {
|
|
62
|
+
if (pkg.name.includes(core_1.DATABASE_PREFIX))
|
|
63
|
+
return 1;
|
|
64
|
+
if (pkg.name.includes(core_1.ADAPTER_PREFIX))
|
|
65
|
+
return 2;
|
|
66
|
+
// if (CORE_MODULES.includes(pkg.name)) return 3;
|
|
67
|
+
if (pkg.kotori.enforce === 'pre')
|
|
68
|
+
return 4;
|
|
69
|
+
if (!pkg.kotori.enforce)
|
|
70
|
+
return 5;
|
|
71
|
+
return 6;
|
|
72
|
+
}
|
|
73
|
+
class Runner {
|
|
74
|
+
baseDir;
|
|
75
|
+
options;
|
|
76
|
+
ctx;
|
|
77
|
+
isDev;
|
|
78
|
+
isSourceDev;
|
|
79
|
+
[core_1.Symbols.modules] = new Map();
|
|
80
|
+
constructor(ctx, config) {
|
|
81
|
+
this.ctx = ctx;
|
|
82
|
+
/* handle config */
|
|
83
|
+
this.baseDir = config.baseDir;
|
|
84
|
+
this.options = config.options;
|
|
85
|
+
this.isDev = this.options.mode.startsWith(constants_1.DEV_MODE);
|
|
86
|
+
this.isSourceDev = this.options.mode === constants_1.DEV_SOURCE_MODE;
|
|
87
|
+
const loggerOptions = {
|
|
88
|
+
level: this.ctx.config.global.level ?? config.level,
|
|
89
|
+
label: [],
|
|
90
|
+
transports: [
|
|
91
|
+
new logger_1.ConsoleTransport(),
|
|
92
|
+
new logger_1.FileTransport({ dir: this.baseDir.logs, filter: (data) => data.level >= logger_1.LoggerLevel.WARN })
|
|
93
|
+
]
|
|
94
|
+
};
|
|
95
|
+
ctx.provide('logger', new logger_2.default(loggerOptions, this.ctx));
|
|
96
|
+
ctx.inject('logger');
|
|
97
|
+
}
|
|
98
|
+
getDirFiles(rootDir) {
|
|
99
|
+
const files = fs_1.default.readdirSync(rootDir);
|
|
100
|
+
const list = [];
|
|
101
|
+
files.forEach((fileName) => {
|
|
102
|
+
const file = path_1.default.join(rootDir, fileName);
|
|
103
|
+
if (fs_1.default.statSync(file).isDirectory()) {
|
|
104
|
+
list.push(...this.getDirFiles(file));
|
|
105
|
+
}
|
|
106
|
+
if (path_1.default.parse(file).ext !== (this.isSourceDev ? constants_1.DEV_FILE : constants_1.BUILD_FILE))
|
|
107
|
+
return;
|
|
108
|
+
list.push(path_1.default.resolve(file));
|
|
109
|
+
});
|
|
110
|
+
return list;
|
|
111
|
+
}
|
|
112
|
+
getModuleRootDir() {
|
|
113
|
+
const moduleRootDir = [];
|
|
114
|
+
[
|
|
115
|
+
...this.ctx.config.global.dirs.map((dir) => path_1.default.resolve(this.ctx.baseDir.root, dir)),
|
|
116
|
+
this.ctx.baseDir.modules
|
|
117
|
+
].forEach((dir) => {
|
|
118
|
+
if (fs_1.default.existsSync(dir) && fs_1.default.statSync(dir).isDirectory())
|
|
119
|
+
moduleRootDir.push(dir);
|
|
120
|
+
});
|
|
121
|
+
return moduleRootDir;
|
|
122
|
+
}
|
|
123
|
+
async checkModuleFiles(rootDir, filename) {
|
|
124
|
+
const dir = path_1.default.join(rootDir, filename);
|
|
125
|
+
if (!fs_1.default.statSync(dir).isDirectory())
|
|
126
|
+
return;
|
|
127
|
+
if (rootDir !== this.ctx.baseDir.modules && !filename.startsWith(core_1.PLUGIN_PREFIX))
|
|
128
|
+
return;
|
|
129
|
+
const packagePath = path_1.default.join(dir, 'package.json');
|
|
130
|
+
let pkg;
|
|
131
|
+
if (!fs_1.default.existsSync(packagePath))
|
|
132
|
+
return;
|
|
133
|
+
try {
|
|
134
|
+
pkg = JSON.parse(fs_1.default.readFileSync(packagePath).toString());
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new core_1.DevError(`illegal package.json ${packagePath}`);
|
|
138
|
+
}
|
|
139
|
+
const result = modulePackageSchema.parseSafe(pkg);
|
|
140
|
+
if (!result.value) {
|
|
141
|
+
if (rootDir !== this.ctx.baseDir.modules)
|
|
142
|
+
return;
|
|
143
|
+
throw new core_1.DevError(`package.json format error ${packagePath}: ${result.error.message}`);
|
|
144
|
+
}
|
|
145
|
+
pkg = result.data;
|
|
146
|
+
const devMode = this.isSourceDev && (0, fs_1.existsSync)(path_1.default.resolve(dir, constants_1.DEV_IMPORT));
|
|
147
|
+
const main = path_1.default.resolve(dir, devMode ? constants_1.DEV_IMPORT : pkg.main);
|
|
148
|
+
if (!fs_1.default.existsSync(main))
|
|
149
|
+
throw new core_1.DevError(`cannot find ${main}`);
|
|
150
|
+
const dirs = path_1.default.join(dir, devMode ? constants_1.DEV_CODE_DIRS : path_1.default.dirname(pkg.main));
|
|
151
|
+
const files = fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
|
|
152
|
+
this[core_1.Symbols.modules].set(pkg.name, [
|
|
153
|
+
{ pkg, files, main },
|
|
154
|
+
this.ctx.config.plugin[(0, core_1.stringRightSplit)(pkg.name, core_1.PLUGIN_PREFIX)] || {}
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
getModuleList(rootDir) {
|
|
158
|
+
this.ctx.logger.trace('load dirs:', rootDir);
|
|
159
|
+
fs_1.default.readdirSync(rootDir).forEach(async (filename) => {
|
|
160
|
+
await this.checkModuleFiles(rootDir, filename);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
loadLang(lang) {
|
|
164
|
+
if (lang)
|
|
165
|
+
this.ctx.i18n.use((0, path_1.resolve)(...(Array.isArray(lang) ? lang : [lang])));
|
|
166
|
+
}
|
|
167
|
+
loadEx(instance, origin) {
|
|
168
|
+
this.ctx.logger.trace('module:', instance, origin);
|
|
169
|
+
const parsed = (schema) => {
|
|
170
|
+
const result = schema.parseSafe(config);
|
|
171
|
+
if (!result.value)
|
|
172
|
+
throw new core_1.ModuleError(`Config format of module ${pkg.name} is error: ${result.error.message}`);
|
|
173
|
+
return result.data;
|
|
174
|
+
};
|
|
175
|
+
const { main, pkg } = instance;
|
|
176
|
+
/* eslint-disable-next-line import/no-dynamic-require, global-require, @typescript-eslint/no-var-requires */
|
|
177
|
+
let obj = require(main);
|
|
178
|
+
let config = origin;
|
|
179
|
+
const adapterName = pkg.name.split(core_1.ADAPTER_PREFIX)[1];
|
|
180
|
+
if (core_1.Adapter.isPrototypeOf.call(core_1.Adapter, obj.default) &&
|
|
181
|
+
adapterName &&
|
|
182
|
+
(!obj.config || obj.config instanceof core_1.Parser)) {
|
|
183
|
+
/* Adapter Class */
|
|
184
|
+
this.ctx[core_1.Symbols.adapter].set(adapterName, [obj.default, obj.config]);
|
|
185
|
+
obj = {};
|
|
186
|
+
}
|
|
187
|
+
else if (core_1.Service.isPrototypeOf.call(core_1.Service, obj.default)) {
|
|
188
|
+
/* Service Class */
|
|
189
|
+
obj = {};
|
|
190
|
+
}
|
|
191
|
+
else if (obj.config instanceof core_1.Parser) {
|
|
192
|
+
config = parsed(obj.config);
|
|
193
|
+
}
|
|
194
|
+
if (obj.lang)
|
|
195
|
+
this.loadLang(obj.lang);
|
|
196
|
+
if (obj.default) {
|
|
197
|
+
if (obj.default.lang)
|
|
198
|
+
this.loadLang(obj.default.lang);
|
|
199
|
+
if (obj.default.config instanceof core_1.Parser)
|
|
200
|
+
config = parsed(obj.default.config);
|
|
201
|
+
}
|
|
202
|
+
else if (obj.Main) {
|
|
203
|
+
if (obj.Main.lang)
|
|
204
|
+
this.loadLang(obj.Main.lang);
|
|
205
|
+
if (obj.Main.config instanceof core_1.Parser)
|
|
206
|
+
config = parsed(obj.Main.config);
|
|
207
|
+
}
|
|
208
|
+
this.ctx.load({ name: pkg.name, ...obj, config });
|
|
209
|
+
}
|
|
210
|
+
unloadEx(instance) {
|
|
211
|
+
instance.files.forEach((file) => delete require.cache[require.resolve(file)]);
|
|
212
|
+
this.ctx.load({ name: instance.pkg.name });
|
|
213
|
+
}
|
|
214
|
+
loadAll() {
|
|
215
|
+
this.getModuleRootDir().forEach((dir) => this.getModuleList(dir));
|
|
216
|
+
const modules = [];
|
|
217
|
+
this[core_1.Symbols.modules].forEach((val) => modules.push(val));
|
|
218
|
+
modules
|
|
219
|
+
.sort((el1, el2) => moduleLoadOrder(el1[0].pkg) - moduleLoadOrder(el2[0].pkg))
|
|
220
|
+
.forEach((el) => this.loadEx(...el));
|
|
221
|
+
if (this.isDev)
|
|
222
|
+
this.watcher();
|
|
223
|
+
}
|
|
224
|
+
watcher() {
|
|
225
|
+
this[core_1.Symbols.modules].forEach((data) => data[0].files.forEach((file) => fs_1.default.watchFile(file, async () => {
|
|
226
|
+
this.ctx.logger.debug(`file happen changed, module ${data[0].pkg.name} is reloading...`);
|
|
227
|
+
this.unloadEx(data[0]);
|
|
228
|
+
this.loadEx(...data);
|
|
229
|
+
})));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.Runner = Runner;
|
|
233
|
+
exports.default = Runner;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const DEV_FILE = ".ts";
|
|
2
|
+
export declare const BUILD_FILE = ".js";
|
|
3
|
+
export declare const DEV_CODE_DIRS = "./src/";
|
|
4
|
+
export declare const DEV_IMPORT = "./src/index.ts";
|
|
5
|
+
export declare const BUILD_CONFIG_NAME = "kotori.yml";
|
|
6
|
+
export declare const DEV_CONFIG_NAME = "kotori.dev.yml";
|
|
7
|
+
export declare const SUPPORTS_VERSION: RegExp;
|
|
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 fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("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, fs_1.readFileSync)((0, 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 path_1 = require("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, 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-02
|
|
7
|
+
* @LastEditTime: 2024-05-02 10:44:55
|
|
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,7 @@ 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);
|
|
26
27
|
__exportStar(require("@kotori-bot/logger"), exports);
|
package/lib/loader.js
CHANGED
|
@@ -32,7 +32,7 @@ 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-01 22:06:58
|
|
36
36
|
*/
|
|
37
37
|
const core_1 = require("@kotori-bot/core");
|
|
38
38
|
const path_1 = __importDefault(require("path"));
|
|
@@ -40,7 +40,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
40
40
|
const logger_1 = __importDefault(require("@kotori-bot/logger"));
|
|
41
41
|
const runner_1 = __importStar(require("./runner"));
|
|
42
42
|
const log_1 = __importDefault(require("./log"));
|
|
43
|
-
const
|
|
43
|
+
const constants_1 = require("./constants");
|
|
44
44
|
const server_1 = __importDefault(require("./service/server"));
|
|
45
45
|
const file_1 = __importDefault(require("./service/file"));
|
|
46
46
|
function getRunnerConfig(file, dir) {
|
|
@@ -58,7 +58,7 @@ function getRunnerConfig(file, dir) {
|
|
|
58
58
|
return baseDir;
|
|
59
59
|
};
|
|
60
60
|
const options = {
|
|
61
|
-
mode: file ===
|
|
61
|
+
mode: file === constants_1.DEV_CONFIG_NAME ? 'dev' : 'build'
|
|
62
62
|
};
|
|
63
63
|
if (dir)
|
|
64
64
|
return { baseDir: handle(path_1.default.resolve(dir)), options };
|
|
@@ -115,7 +115,7 @@ class Loader extends core_1.Container {
|
|
|
115
115
|
loadCount = 0;
|
|
116
116
|
constructor(options) {
|
|
117
117
|
super();
|
|
118
|
-
const file = options && options.mode === 'dev' ?
|
|
118
|
+
const file = options && options.mode === 'dev' ? constants_1.DEV_CONFIG_NAME : constants_1.BUILD_CONFIG_NAME;
|
|
119
119
|
const runnerConfig = getRunnerConfig(file, options?.dir);
|
|
120
120
|
const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
|
|
121
121
|
ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
|
|
@@ -202,8 +202,8 @@ class Loader extends core_1.Container {
|
|
|
202
202
|
this.ctx.logger.info(`loaded module ${name} version: ${version} ${Array.isArray(author) ? `authors: ${author.join(',')}` : `author: ${author}`}`);
|
|
203
203
|
const requiredVersion = peerDependencies['kotori-bot'];
|
|
204
204
|
if (!requiredVersion.includes('workspace') &&
|
|
205
|
-
(!
|
|
206
|
-
if (
|
|
205
|
+
(!constants_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
|
|
206
|
+
if (constants_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
|
|
207
207
|
this.ctx.logger.warn(`incomplete supported module version: ${requiredVersion}`);
|
|
208
208
|
}
|
|
209
209
|
else {
|
|
@@ -243,7 +243,7 @@ class Loader extends core_1.Container {
|
|
|
243
243
|
async checkUpdate() {
|
|
244
244
|
const { version } = this.ctx.pkg;
|
|
245
245
|
const res = await this.ctx.http
|
|
246
|
-
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/
|
|
246
|
+
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* GLOBAL.UPDATE */)
|
|
247
247
|
.catch(() => this.ctx.logger.error('get update failed, please check your network'));
|
|
248
248
|
if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
|
|
249
249
|
this.ctx.logger.warn(`detection update failed`);
|
package/lib/runner.js
CHANGED
|
@@ -31,7 +31,7 @@ const fs_1 = __importStar(require("fs"));
|
|
|
31
31
|
const path_1 = __importDefault(require("path"));
|
|
32
32
|
const core_1 = require("@kotori-bot/core");
|
|
33
33
|
const logger_1 = require("@kotori-bot/logger");
|
|
34
|
-
const
|
|
34
|
+
const constants_1 = require("./constants");
|
|
35
35
|
const logger_2 = __importDefault(require("./utils/logger"));
|
|
36
36
|
exports.localeTypeSchema = core_1.Tsu.Union([
|
|
37
37
|
core_1.Tsu.Union([core_1.Tsu.Literal('en_US'), core_1.Tsu.Literal('ja_JP')]),
|
|
@@ -101,7 +101,7 @@ class Runner {
|
|
|
101
101
|
if (fs_1.default.statSync(file).isDirectory()) {
|
|
102
102
|
list.push(...this.getDirFiles(file));
|
|
103
103
|
}
|
|
104
|
-
if (path_1.default.parse(file).ext !== (this.isDev ?
|
|
104
|
+
if (path_1.default.parse(file).ext !== (this.isDev ? constants_1.DEV_FILE : constants_1.BUILD_FILE))
|
|
105
105
|
return;
|
|
106
106
|
list.push(path_1.default.resolve(file));
|
|
107
107
|
});
|
|
@@ -142,11 +142,11 @@ class Runner {
|
|
|
142
142
|
throw new core_1.DevError(`package.json format error ${packagePath}: ${result.error.message}`);
|
|
143
143
|
}
|
|
144
144
|
pkg = result.data;
|
|
145
|
-
const devMode = this.isDev && (0, fs_1.existsSync)(path_1.default.resolve(dir,
|
|
146
|
-
const main = path_1.default.resolve(dir, devMode ?
|
|
145
|
+
const devMode = this.isDev && (0, fs_1.existsSync)(path_1.default.resolve(dir, constants_1.DEV_IMPORT));
|
|
146
|
+
const main = path_1.default.resolve(dir, devMode ? constants_1.DEV_IMPORT : pkg.main);
|
|
147
147
|
if (!fs_1.default.existsSync(main))
|
|
148
148
|
throw new core_1.DevError(`cannot find ${main}`);
|
|
149
|
-
const dirs = path_1.default.join(dir, devMode ?
|
|
149
|
+
const dirs = path_1.default.join(dir, devMode ? constants_1.DEV_CODE_DIRS : path_1.default.dirname(pkg.main));
|
|
150
150
|
const files = fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
|
|
151
151
|
this[core_1.Symbols.modules].set(pkg.name, [
|
|
152
152
|
{ pkg, files, main },
|
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
|
@@ -13,6 +13,7 @@ class File extends core_1.Service {
|
|
|
13
13
|
getFile(filename) {
|
|
14
14
|
return (0, 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,14 +1,18 @@
|
|
|
1
1
|
/// <reference types="body-parser" />
|
|
2
2
|
/// <reference types="connect" />
|
|
3
3
|
/// <reference types="serve-static" />
|
|
4
|
+
/// <reference types="node" />
|
|
4
5
|
import { Context, Service } from '@kotori-bot/core';
|
|
5
6
|
import express from 'express';
|
|
7
|
+
import { IncomingMessage } from 'http';
|
|
8
|
+
import ws from 'ws';
|
|
6
9
|
interface ServerConfig {
|
|
7
10
|
port: number;
|
|
8
11
|
}
|
|
9
12
|
export declare class Server extends Service<ServerConfig> {
|
|
10
13
|
private app;
|
|
11
14
|
private server?;
|
|
15
|
+
private wsServer?;
|
|
12
16
|
constructor(ctx: Context, config: ServerConfig);
|
|
13
17
|
start(): void;
|
|
14
18
|
stop(): void;
|
|
@@ -22,5 +26,6 @@ export declare class Server extends Service<ServerConfig> {
|
|
|
22
26
|
router: typeof express.Router;
|
|
23
27
|
json: (options?: import("body-parser").OptionsJson | undefined) => import("connect").NextHandleFunction;
|
|
24
28
|
static: import("serve-static").RequestHandlerConstructor<express.Response<any, Record<string, any>>>;
|
|
29
|
+
wss(path?: string): ws.Server<typeof ws, typeof IncomingMessage> | undefined;
|
|
25
30
|
}
|
|
26
31
|
export default Server;
|
package/lib/service/server.js
CHANGED
|
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.Server = void 0;
|
|
7
7
|
const core_1 = require("@kotori-bot/core");
|
|
8
8
|
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const ws_1 = __importDefault(require("ws"));
|
|
9
10
|
class Server extends core_1.Service {
|
|
10
11
|
app;
|
|
11
12
|
server;
|
|
13
|
+
wsServer;
|
|
12
14
|
constructor(ctx, config) {
|
|
13
15
|
super(ctx, config, 'server');
|
|
14
16
|
this.app = (0, express_1.default)();
|
|
@@ -35,15 +37,21 @@ class Server extends core_1.Service {
|
|
|
35
37
|
this.all = this.app.all.bind(this.app);
|
|
36
38
|
}
|
|
37
39
|
start() {
|
|
38
|
-
if (this.server)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if (!this.server) {
|
|
41
|
+
this.server = this.app.listen(this.config.port);
|
|
42
|
+
this.ctx.logger.label('server').info(`http server start at http://127.0.0.1:${this.config.port}`);
|
|
43
|
+
}
|
|
44
|
+
if (!this.wsServer) {
|
|
45
|
+
const port = this.config.port + 1;
|
|
46
|
+
this.wsServer = new ws_1.default.Server({ port });
|
|
47
|
+
this.ctx.logger.label('server').info(`websocket server start at ws://127.0.0.1:${port}`);
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
stop() {
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
this.
|
|
51
|
+
if (this.server)
|
|
52
|
+
this.server.close();
|
|
53
|
+
if (this.wsServer)
|
|
54
|
+
this.wsServer.close();
|
|
47
55
|
}
|
|
48
56
|
get;
|
|
49
57
|
post;
|
|
@@ -55,6 +63,32 @@ class Server extends core_1.Service {
|
|
|
55
63
|
router = express_1.default.Router;
|
|
56
64
|
json = express_1.default.json;
|
|
57
65
|
static = express_1.default.static;
|
|
66
|
+
wss(path) {
|
|
67
|
+
if (!this.wsServer)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (!path)
|
|
70
|
+
return this.wsServer;
|
|
71
|
+
const eventEmiter = new Proxy(this.wsServer.on, {
|
|
72
|
+
apply: (target, thisArg, argArray) => {
|
|
73
|
+
const [event, callback] = argArray;
|
|
74
|
+
if (event !== 'connection')
|
|
75
|
+
return Reflect.apply(target, thisArg, argArray);
|
|
76
|
+
return this.wsServer.on(event, (ws, req) => {
|
|
77
|
+
if (req.url !== path && path)
|
|
78
|
+
return;
|
|
79
|
+
callback(ws, req);
|
|
80
|
+
this.ctx.logger.label('server').info(`websocket connection from ${req.url}`);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return new Proxy(this.wsServer, {
|
|
85
|
+
get: (target, p, receiver) => {
|
|
86
|
+
if (p !== 'on')
|
|
87
|
+
return Reflect.get(target, p, receiver);
|
|
88
|
+
return eventEmiter;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
58
92
|
}
|
|
59
93
|
exports.Server = Server;
|
|
60
94
|
exports.default = Server;
|
package/lib/utils/log.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadInfo = void 0;
|
|
4
|
+
function loadInfo(info, ctx) {
|
|
5
|
+
process.stdout.write('Kotori Bot is loading...');
|
|
6
|
+
process.stdout.write(`
|
|
7
|
+
██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗
|
|
8
|
+
██║ ██╔╝██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗██║
|
|
9
|
+
█████╔╝ ██║ ██║ ██║ ██║ ██║██████╔╝██║
|
|
10
|
+
██╔═██╗ ██║ ██║ ██║ ██║ ██║██╔══██╗██║
|
|
11
|
+
██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
|
|
12
|
+
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
|
|
13
|
+
`);
|
|
14
|
+
ctx.logger.info('loader base dir:', ctx.baseDir.root);
|
|
15
|
+
ctx.logger.info(`Kotori Bot Version: ${info.version} License: ${info.license}`);
|
|
16
|
+
ctx.logger.info(`Kotori Bot By ${info.author}`);
|
|
17
|
+
ctx.logger.info(`Copyright © 2023 - 2024 ${info.author} All rights reserved`);
|
|
18
|
+
}
|
|
19
|
+
exports.loadInfo = loadInfo;
|
|
20
|
+
exports.default = loadInfo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kotori-bot/loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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": {
|