@kotori-bot/loader 1.3.0 → 1.4.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/README.md +22 -0
- package/lib/consts.js +1 -1
- package/lib/index.js +1 -1
- package/lib/loader.d.ts +11 -4
- package/lib/loader.js +63 -58
- package/lib/log.js +1 -0
- package/lib/runner.d.ts +30 -4
- package/lib/runner.js +45 -23
- package/lib/service/database.d.ts +12 -0
- package/lib/service/database.js +43 -0
- package/lib/service/file.d.ts +10 -0
- package/lib/service/file.js +27 -0
- package/lib/service/server.d.ts +26 -0
- package/lib/service/server.js +60 -0
- package/lib/utils/logger.d.ts +15 -0
- package/lib/utils/logger.js +53 -0
- package/package.json +16 -6
package/README.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @kotori-bot/core
|
|
2
2
|
|
|
3
|
+
```typescript
|
|
4
|
+
interface Context {
|
|
5
|
+
readonly baseDir: Runner['baseDir'];
|
|
6
|
+
readonly options: Runner['options'];
|
|
7
|
+
readonly [Symbols.modules]: Runner[typeof Symbols.modules];
|
|
8
|
+
loadAll(): void;
|
|
9
|
+
watcher(): void;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
/* Service */
|
|
12
|
+
server: Server;
|
|
13
|
+
db: Database;
|
|
14
|
+
file: File;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Loader
|
|
19
|
+
- Runner
|
|
20
|
+
- Server
|
|
21
|
+
- Database
|
|
22
|
+
- File
|
|
23
|
+
- log
|
|
24
|
+
|
|
3
25
|
## Reference
|
|
4
26
|
|
|
5
27
|
- [Kotori Docs](https://kotori.js.org/)
|
package/lib/consts.js
CHANGED
|
@@ -7,5 +7,5 @@ exports.DEV_CODE_DIRS = './src/';
|
|
|
7
7
|
exports.DEV_IMPORT = `${exports.DEV_CODE_DIRS}index.ts`;
|
|
8
8
|
exports.BUILD_CONFIG_NAME = 'kotori.yml';
|
|
9
9
|
exports.DEV_CONFIG_NAME = 'kotori.dev.yml';
|
|
10
|
-
exports.SUPPORTS_VERSION = /(1\.1\.0)/;
|
|
10
|
+
exports.SUPPORTS_VERSION = /(1\.1\.0)|(1\.2\.0)/;
|
|
11
11
|
exports.SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
|
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-02-20 20:20:54
|
|
8
8
|
*/
|
|
9
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
package/lib/loader.d.ts
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import { Container, Symbols } from '@kotori-bot/core';
|
|
2
2
|
import Logger from '@kotori-bot/logger';
|
|
3
3
|
import Runner from './runner';
|
|
4
|
+
import Server from './service/server';
|
|
5
|
+
import Database from './service/database';
|
|
6
|
+
import File from './service/file';
|
|
4
7
|
declare module '@kotori-bot/core' {
|
|
5
8
|
interface Context {
|
|
6
9
|
readonly baseDir: Runner['baseDir'];
|
|
7
10
|
readonly options: Runner['options'];
|
|
8
11
|
readonly [Symbols.modules]: Runner[typeof Symbols.modules];
|
|
9
|
-
|
|
12
|
+
loadAll(): void;
|
|
10
13
|
watcher(): void;
|
|
11
14
|
logger: Logger;
|
|
15
|
+
server: Server;
|
|
16
|
+
db: Database;
|
|
17
|
+
file: File;
|
|
12
18
|
}
|
|
13
19
|
interface GlobalConfig {
|
|
14
20
|
dirs: string[];
|
|
21
|
+
port: number;
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
24
|
export declare class Loader extends Container {
|
|
18
25
|
private ctx;
|
|
19
26
|
private loadCount;
|
|
20
|
-
private failLoadCount;
|
|
21
27
|
constructor(options?: {
|
|
22
28
|
dir?: string;
|
|
23
29
|
mode?: string;
|
|
@@ -26,8 +32,9 @@ export declare class Loader extends Container {
|
|
|
26
32
|
private handleError;
|
|
27
33
|
private catchError;
|
|
28
34
|
private listenMessage;
|
|
29
|
-
private
|
|
30
|
-
private
|
|
35
|
+
private setPreService;
|
|
36
|
+
private loadAllModules;
|
|
37
|
+
private loadAllAdapter;
|
|
31
38
|
private checkUpdate;
|
|
32
39
|
}
|
|
33
40
|
export default Loader;
|
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-02-
|
|
35
|
+
* @LastEditTime: 2024-02-21 11:35:04
|
|
36
36
|
*/
|
|
37
37
|
const core_1 = require("@kotori-bot/core");
|
|
38
38
|
const path_1 = __importDefault(require("path"));
|
|
@@ -41,22 +41,28 @@ 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
43
|
const consts_1 = require("./consts");
|
|
44
|
+
const server_1 = __importDefault(require("./service/server"));
|
|
45
|
+
const database_1 = __importDefault(require("./service/database"));
|
|
46
|
+
const file_1 = __importDefault(require("./service/file"));
|
|
44
47
|
function getRunnerConfig(file, dir) {
|
|
45
|
-
const handle = (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const handle = (root) => {
|
|
49
|
+
const baseDir = {
|
|
50
|
+
root,
|
|
51
|
+
modules: path_1.default.join(root, 'modules'),
|
|
52
|
+
data: path_1.default.join(root, 'data'),
|
|
53
|
+
logs: path_1.default.join(root, 'logs')
|
|
54
|
+
};
|
|
55
|
+
Object.values(baseDir).forEach((val) => {
|
|
56
|
+
if (!fs_1.default.existsSync(val))
|
|
57
|
+
fs_1.default.mkdirSync(val);
|
|
58
|
+
});
|
|
50
59
|
return baseDir;
|
|
51
60
|
};
|
|
52
61
|
const options = {
|
|
53
62
|
mode: file === consts_1.DEV_CONFIG_NAME ? 'dev' : 'build'
|
|
54
63
|
};
|
|
55
64
|
if (dir)
|
|
56
|
-
return {
|
|
57
|
-
baseDir: handle({ root: dir, modules: path_1.default.join(dir, 'modules'), logs: path_1.default.join(dir, 'logs') }),
|
|
58
|
-
options
|
|
59
|
-
};
|
|
65
|
+
return { baseDir: handle(path_1.default.resolve(dir)), options };
|
|
60
66
|
let root = path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
|
|
61
67
|
let count = 0;
|
|
62
68
|
while (!fs_1.default.existsSync(path_1.default.join(root, file))) {
|
|
@@ -67,10 +73,7 @@ function getRunnerConfig(file, dir) {
|
|
|
67
73
|
root = path_1.default.join(root, '..');
|
|
68
74
|
count += 1;
|
|
69
75
|
}
|
|
70
|
-
return {
|
|
71
|
-
baseDir: handle({ root, modules: path_1.default.join(root, 'modules'), logs: path_1.default.join(root, 'logs') }),
|
|
72
|
-
options
|
|
73
|
-
};
|
|
76
|
+
return { baseDir: handle(root), options };
|
|
74
77
|
}
|
|
75
78
|
/* eslint consistent-return: 0 */
|
|
76
79
|
function getCoreConfig(file, baseDir) {
|
|
@@ -78,6 +81,7 @@ function getCoreConfig(file, baseDir) {
|
|
|
78
81
|
const result1 = core_1.Tsu.Object({
|
|
79
82
|
global: core_1.Tsu.Object({
|
|
80
83
|
dirs: core_1.Tsu.Array(core_1.Tsu.String()).default([]),
|
|
84
|
+
port: core_1.Tsu.Number().default(core_1.DEFAULT_PORT),
|
|
81
85
|
lang: runner_1.localeTypeSchema.default(core_1.DEFAULT_CORE_CONFIG.global.lang),
|
|
82
86
|
'command-prefix': core_1.Tsu.String().default(core_1.DEFAULT_CORE_CONFIG.global['command-prefix'])
|
|
83
87
|
}),
|
|
@@ -110,14 +114,13 @@ function getCoreConfig(file, baseDir) {
|
|
|
110
114
|
class Loader extends core_1.Container {
|
|
111
115
|
ctx;
|
|
112
116
|
loadCount = 0;
|
|
113
|
-
failLoadCount = 0;
|
|
114
117
|
constructor(options) {
|
|
115
118
|
super();
|
|
116
119
|
const file = options && options.mode === 'dev' ? consts_1.DEV_CONFIG_NAME : consts_1.BUILD_CONFIG_NAME;
|
|
117
120
|
const runnerConfig = getRunnerConfig(file, options?.dir);
|
|
118
121
|
const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
|
|
119
122
|
ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
|
|
120
|
-
ctx.mixin('runner', ['baseDir', 'options'
|
|
123
|
+
ctx.mixin('runner', ['baseDir', 'options']);
|
|
121
124
|
core_1.Container.setInstance(ctx);
|
|
122
125
|
this.ctx = core_1.Container.getInstance();
|
|
123
126
|
}
|
|
@@ -125,20 +128,27 @@ class Loader extends core_1.Container {
|
|
|
125
128
|
(0, log_1.default)(this.ctx.pkg, this.ctx);
|
|
126
129
|
this.catchError();
|
|
127
130
|
this.listenMessage();
|
|
128
|
-
this.
|
|
131
|
+
this.setPreService();
|
|
132
|
+
this.loadAllModules();
|
|
129
133
|
this.checkUpdate();
|
|
130
134
|
}
|
|
131
135
|
handleError(err, prefix) {
|
|
132
136
|
if (!(err instanceof core_1.KotoriError)) {
|
|
133
|
-
|
|
137
|
+
if (err instanceof Error) {
|
|
138
|
+
this.ctx.logger.label(prefix).error(err.message, err.stack);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.ctx.logger.label(prefix).error(err);
|
|
142
|
+
}
|
|
134
143
|
return;
|
|
135
144
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
ModuleError: () => this.ctx.logger.label('module').error
|
|
139
|
-
UnknownError: () => this.ctx.logger.error
|
|
140
|
-
DevError: () => this.ctx.logger.label('error').debug
|
|
141
|
-
}
|
|
145
|
+
const list = {
|
|
146
|
+
ServiceError: () => this.ctx.logger.label('service').warn,
|
|
147
|
+
ModuleError: () => this.ctx.logger.label('module').error,
|
|
148
|
+
UnknownError: () => this.ctx.logger.error,
|
|
149
|
+
DevError: () => this.ctx.logger.label('error').debug
|
|
150
|
+
};
|
|
151
|
+
list[err.name]().bind(this.ctx.logger)(err.message, err.stack);
|
|
142
152
|
}
|
|
143
153
|
catchError() {
|
|
144
154
|
process.on('uncaughtExceptionMonitor', (err) => this.handleError(err, 'sync'));
|
|
@@ -174,31 +184,24 @@ class Loader extends core_1.Container {
|
|
|
174
184
|
msg = `dispose completed about ${address}`;
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
|
-
|
|
187
|
+
adapter.ctx.logger[normal ? 'info' : 'warn'](msg);
|
|
178
188
|
});
|
|
179
189
|
this.ctx.on('status', (data) => {
|
|
180
190
|
const { status, adapter } = data;
|
|
181
|
-
|
|
191
|
+
adapter.ctx.logger.info(status);
|
|
182
192
|
});
|
|
183
193
|
this.ctx.on('ready_module', (data) => {
|
|
184
|
-
if (
|
|
194
|
+
if (typeof data.instance !== 'object')
|
|
195
|
+
return;
|
|
196
|
+
const pkg = data.instance.name
|
|
197
|
+
? this.ctx.get('runner')[core_1.Symbols.modules].get(data.instance.name)
|
|
198
|
+
: undefined;
|
|
199
|
+
if (!pkg)
|
|
185
200
|
return;
|
|
186
201
|
this.loadCount += 1;
|
|
187
|
-
const { name, version, author } =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.ctx.logger.warn(`failed to load module ${name}`);
|
|
191
|
-
if (data.error instanceof core_1.KotoriError) {
|
|
192
|
-
process.emit('uncaughtExceptionMonitor', data.error);
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
this.ctx.logger.warn(data.error);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
this.ctx.logger.info(`loaded module ${name} version: ${version} ${Array.isArray(author) ? `authors: ${author.join(',')}` : `author: ${author}`}`);
|
|
200
|
-
}
|
|
201
|
-
const requiredVersion = data.module.pkg.peerDependencies['kotori-bot'];
|
|
202
|
+
const { name, version, author, peerDependencies } = pkg[0].pkg;
|
|
203
|
+
this.ctx.logger.info(`loaded module ${name} version: ${version} ${Array.isArray(author) ? `authors: ${author.join(',')}` : `author: ${author}`}`);
|
|
204
|
+
const requiredVersion = peerDependencies['kotori-bot'];
|
|
202
205
|
if (!requiredVersion.includes('workspace') &&
|
|
203
206
|
(!consts_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
|
|
204
207
|
if (consts_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
|
|
@@ -208,18 +211,21 @@ class Loader extends core_1.Container {
|
|
|
208
211
|
this.ctx.logger.error(`unsupported module version: ${requiredVersion}`);
|
|
209
212
|
}
|
|
210
213
|
}
|
|
211
|
-
if (this.loadCount !== this.ctx.get('runner')[core_1.Symbols.modules].size)
|
|
212
|
-
return;
|
|
213
|
-
this.ctx.logger.info(`loaded ${this.loadCount - this.failLoadCount} modules successfully${this.failLoadCount > 0 ? `, failed to load ${this.failLoadCount} modules` : ''} `);
|
|
214
|
-
this.loadAllService();
|
|
215
|
-
this.ctx.emit('ready');
|
|
216
214
|
});
|
|
217
215
|
}
|
|
218
|
-
|
|
219
|
-
this.ctx.
|
|
216
|
+
setPreService() {
|
|
217
|
+
this.ctx.service('server', new server_1.default(this.ctx.extends(), { port: this.ctx.config.global.port }));
|
|
218
|
+
this.ctx.service('db', new database_1.default(this.ctx.extends()));
|
|
219
|
+
this.ctx.service('file', new file_1.default(this.ctx.extends()));
|
|
220
220
|
}
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
loadAllModules() {
|
|
222
|
+
this.ctx.get('runner').loadAll();
|
|
223
|
+
const failLoadCount = this.ctx.get('runner')[core_1.Symbols.modules].size - this.loadCount;
|
|
224
|
+
this.ctx.logger.info(`loaded ${this.loadCount} modules successfully${failLoadCount > 0 ? `, failed to load ${failLoadCount} modules` : ''} `);
|
|
225
|
+
this.loadAllAdapter();
|
|
226
|
+
this.ctx.emit('ready');
|
|
227
|
+
}
|
|
228
|
+
loadAllAdapter() {
|
|
223
229
|
const adapters = this.ctx[core_1.Symbols.adapter];
|
|
224
230
|
Object.keys(this.ctx.config.adapter).forEach((botName) => {
|
|
225
231
|
const botConfig = this.ctx.config.adapter[botName];
|
|
@@ -228,19 +234,18 @@ class Loader extends core_1.Container {
|
|
|
228
234
|
this.ctx.logger.warn(`cannot find adapter '${botConfig.extends}' for ${botName}`);
|
|
229
235
|
return;
|
|
230
236
|
}
|
|
231
|
-
const
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const bot = new array[0](this.ctx, isSchema ? isSchema.data : botConfig, botName);
|
|
237
|
+
const result = array[1]?.parseSafe(botConfig);
|
|
238
|
+
if (result && !result.value)
|
|
239
|
+
throw new core_1.ModuleError(`Config format of adapter ${botName} is error: ${result.error.message}`);
|
|
240
|
+
const bot = new array[0](this.ctx.extends({}, `${botConfig.extends}/${botName}`), result ? result.data : botConfig, botName);
|
|
236
241
|
this.ctx.on('ready', () => bot.start());
|
|
242
|
+
this.ctx.on('dispose', () => bot.stop());
|
|
237
243
|
});
|
|
238
|
-
/* load custom services after adapters */
|
|
239
244
|
}
|
|
240
245
|
async checkUpdate() {
|
|
241
246
|
const { version } = this.ctx.pkg;
|
|
242
247
|
const res = await this.ctx.http
|
|
243
|
-
.get(
|
|
248
|
+
.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/kotori/package.json" /* GLOBAL.UPDATE */)
|
|
244
249
|
.catch(() => this.ctx.logger.error('get update failed, please check your network'));
|
|
245
250
|
if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
|
|
246
251
|
this.ctx.logger.warn(`detection update failed`);
|
package/lib/log.js
CHANGED
|
@@ -11,6 +11,7 @@ function loadInfo(info, ctx) {
|
|
|
11
11
|
██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
|
|
12
12
|
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
|
|
13
13
|
`);
|
|
14
|
+
ctx.logger.info('loader base dir:', ctx.baseDir.root);
|
|
14
15
|
ctx.logger.info(`Kotori Bot Version: ${info.version} License: ${info.license}`);
|
|
15
16
|
ctx.logger.info(`Kotori Bot By ${info.author}`);
|
|
16
17
|
ctx.logger.info(`Copyright © 2023 - 2024 ${info.author} All rights reserved`);
|
package/lib/runner.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, LocaleType, ModuleConfig, Symbols } from '@kotori-bot/core';
|
|
2
2
|
interface BaseDir {
|
|
3
3
|
root: string;
|
|
4
4
|
modules: string;
|
|
5
|
+
data: string;
|
|
5
6
|
logs: string;
|
|
6
7
|
}
|
|
7
8
|
interface Options {
|
|
@@ -11,19 +12,44 @@ interface RunnerConfig {
|
|
|
11
12
|
baseDir: BaseDir;
|
|
12
13
|
options: Options;
|
|
13
14
|
}
|
|
15
|
+
interface ModulePackage {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
description: string;
|
|
19
|
+
main: string;
|
|
20
|
+
keywords: string[];
|
|
21
|
+
license: 'GPL-3.0';
|
|
22
|
+
author: string | string[];
|
|
23
|
+
peerDependencies: {
|
|
24
|
+
'kotori-bot': string;
|
|
25
|
+
[propName: string]: string;
|
|
26
|
+
};
|
|
27
|
+
kotori: {
|
|
28
|
+
enforce?: 'pre' | 'post';
|
|
29
|
+
meta: {
|
|
30
|
+
language: LocaleType[];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
interface ModuleMeta {
|
|
35
|
+
pkg: ModulePackage;
|
|
36
|
+
files: string[];
|
|
37
|
+
main: string;
|
|
38
|
+
}
|
|
14
39
|
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>]>]>;
|
|
15
40
|
export declare class Runner {
|
|
16
41
|
readonly baseDir: BaseDir;
|
|
17
42
|
readonly options: Options;
|
|
18
43
|
private ctx;
|
|
19
44
|
private isDev;
|
|
20
|
-
readonly [Symbols.modules]:
|
|
45
|
+
readonly [Symbols.modules]: Map<string, [ModuleMeta, ModuleConfig]>;
|
|
21
46
|
constructor(ctx: Context, config: RunnerConfig);
|
|
22
47
|
private getDirFiles;
|
|
23
48
|
private getModuleRootDir;
|
|
24
49
|
private getModuleList;
|
|
25
|
-
private
|
|
26
|
-
|
|
50
|
+
private loadEx;
|
|
51
|
+
private unloadEx;
|
|
52
|
+
loadAll(): void;
|
|
27
53
|
watcher(): void;
|
|
28
54
|
}
|
|
29
55
|
export default Runner;
|
package/lib/runner.js
CHANGED
|
@@ -30,14 +30,15 @@ exports.Runner = exports.localeTypeSchema = void 0;
|
|
|
30
30
|
const fs_1 = __importStar(require("fs"));
|
|
31
31
|
const path_1 = __importDefault(require("path"));
|
|
32
32
|
const core_1 = require("@kotori-bot/core");
|
|
33
|
-
const logger_1 =
|
|
33
|
+
const logger_1 = require("@kotori-bot/logger");
|
|
34
34
|
const consts_1 = require("./consts");
|
|
35
|
+
const logger_2 = __importDefault(require("./utils/logger"));
|
|
35
36
|
exports.localeTypeSchema = core_1.Tsu.Union([
|
|
36
37
|
core_1.Tsu.Union([core_1.Tsu.Literal('en_US'), core_1.Tsu.Literal('ja_JP')]),
|
|
37
38
|
core_1.Tsu.Union([core_1.Tsu.Literal('zh_TW'), core_1.Tsu.Any()])
|
|
38
39
|
]);
|
|
39
40
|
const modulePackageSchema = core_1.Tsu.Object({
|
|
40
|
-
name: core_1.Tsu.String().regexp(/kotori-plugin-[a-z]([a-z,0-9]{
|
|
41
|
+
name: core_1.Tsu.String().regexp(/kotori-plugin-[a-z]([a-z,0-9]{2,13})\b/),
|
|
41
42
|
version: core_1.Tsu.String(),
|
|
42
43
|
description: core_1.Tsu.String(),
|
|
43
44
|
main: core_1.Tsu.String(),
|
|
@@ -74,29 +75,22 @@ class Runner {
|
|
|
74
75
|
options;
|
|
75
76
|
ctx;
|
|
76
77
|
isDev;
|
|
77
|
-
[core_1.Symbols.modules] = new
|
|
78
|
+
[core_1.Symbols.modules] = new Map();
|
|
78
79
|
constructor(ctx, config) {
|
|
79
80
|
this.ctx = ctx;
|
|
80
81
|
/* handle config */
|
|
81
82
|
this.baseDir = config.baseDir;
|
|
82
83
|
this.options = config.options;
|
|
83
84
|
this.isDev = this.options.mode === 'dev';
|
|
84
|
-
if (this.isDev)
|
|
85
|
-
this.watcher();
|
|
86
85
|
const loggerOptions = {
|
|
87
86
|
level: this.isDev ? logger_1.LoggerLevel.TRACE : logger_1.LoggerLevel.INFO,
|
|
88
87
|
label: [],
|
|
89
|
-
transports:
|
|
88
|
+
transports: [
|
|
89
|
+
new logger_1.ConsoleTransport(),
|
|
90
|
+
new logger_1.FileTransport({ dir: this.baseDir.logs, filter: (data) => data.level >= logger_1.LoggerLevel.WARN })
|
|
91
|
+
]
|
|
90
92
|
};
|
|
91
|
-
|
|
92
|
-
ctx.provide('logger', logger.extends(new Proxy(loggerOptions, {
|
|
93
|
-
get: (target, prop, receiver) => {
|
|
94
|
-
if (prop === 'label') {
|
|
95
|
-
return logger.ctx.identity ? [logger.ctx.identity, ...target.label] : target.label;
|
|
96
|
-
}
|
|
97
|
-
return Reflect.get(target, prop, receiver);
|
|
98
|
-
}
|
|
99
|
-
})));
|
|
93
|
+
ctx.provide('logger', new logger_2.default(loggerOptions, this.ctx));
|
|
100
94
|
ctx.inject('logger');
|
|
101
95
|
}
|
|
102
96
|
getDirFiles(rootDir) {
|
|
@@ -154,28 +148,56 @@ class Runner {
|
|
|
154
148
|
throw new core_1.DevError(`cannot find ${main}`);
|
|
155
149
|
const dirs = path_1.default.join(dir, devMode ? consts_1.DEV_CODE_DIRS : path_1.default.dirname(pkg.main));
|
|
156
150
|
const files = fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
|
|
157
|
-
this[core_1.Symbols.modules].
|
|
158
|
-
{ pkg,
|
|
151
|
+
this[core_1.Symbols.modules].set(pkg.name, [
|
|
152
|
+
{ pkg, files, main },
|
|
159
153
|
this.ctx.config.plugin[(0, core_1.stringRightSplit)(pkg.name, core_1.PLUGIN_PREFIX)] || {}
|
|
160
154
|
]);
|
|
161
155
|
});
|
|
162
156
|
}
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
loadEx(instance, config) {
|
|
158
|
+
const { main, pkg } = instance;
|
|
159
|
+
/* eslint-disable-next-line import/no-dynamic-require, global-require, @typescript-eslint/no-var-requires */
|
|
160
|
+
let obj = require(main);
|
|
161
|
+
let handle = config;
|
|
162
|
+
const adapterName = pkg.name.split(core_1.ADAPTER_PREFIX)[1];
|
|
163
|
+
if (core_1.Adapter.isPrototypeOf.call(core_1.Adapter, obj.default) &&
|
|
164
|
+
adapterName &&
|
|
165
|
+
(!obj.config || obj.config instanceof core_1.Parser)) {
|
|
166
|
+
this.ctx[core_1.Symbols.adapter].set(adapterName, [obj.default, obj.config]);
|
|
167
|
+
obj = {};
|
|
168
|
+
}
|
|
169
|
+
else if (core_1.Service.isPrototypeOf.call(core_1.Service, obj.default)) {
|
|
170
|
+
obj = {};
|
|
171
|
+
}
|
|
172
|
+
else if (obj.config instanceof core_1.Parser) {
|
|
173
|
+
const result = obj.config.parseSafe(handle);
|
|
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;
|
|
177
|
+
}
|
|
178
|
+
if (obj.lang)
|
|
179
|
+
this.ctx.i18n.use(Array.isArray(obj.lang) ? path_1.default.resolve(...obj.lang) : path_1.default.resolve(obj.lang));
|
|
180
|
+
this.ctx.load({ name: pkg.name, ...obj, config: handle });
|
|
165
181
|
}
|
|
166
|
-
|
|
182
|
+
unloadEx(instance) {
|
|
183
|
+
instance.files.forEach((file) => delete require.cache[require.resolve(file)]);
|
|
184
|
+
this.ctx.load({ name: instance.pkg.name });
|
|
185
|
+
}
|
|
186
|
+
loadAll() {
|
|
167
187
|
this.getModuleRootDir().forEach((dir) => this.getModuleList(dir));
|
|
168
188
|
const modules = [];
|
|
169
189
|
this[core_1.Symbols.modules].forEach((val) => modules.push(val));
|
|
170
190
|
modules
|
|
171
191
|
.sort((el1, el2) => moduleLoadOrder(el1[0].pkg) - moduleLoadOrder(el2[0].pkg))
|
|
172
|
-
.forEach((el) => this.
|
|
192
|
+
.forEach((el) => this.loadEx(...el));
|
|
193
|
+
if (this.isDev)
|
|
194
|
+
this.watcher();
|
|
173
195
|
}
|
|
174
196
|
watcher() {
|
|
175
197
|
this[core_1.Symbols.modules].forEach((data) => data[0].files.forEach((file) => fs_1.default.watchFile(file, async () => {
|
|
176
198
|
this.ctx.logger.debug(`file happen changed, module ${data[0].pkg.name} is reloading...`);
|
|
177
|
-
this.
|
|
178
|
-
this.
|
|
199
|
+
this.unloadEx(data[0]);
|
|
200
|
+
this.loadEx(...data);
|
|
179
201
|
})));
|
|
180
202
|
}
|
|
181
203
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Context, Service } from '@kotori-bot/core';
|
|
2
|
+
import knex from 'knex';
|
|
3
|
+
export declare class Database extends Service {
|
|
4
|
+
internal: ReturnType<typeof knex>;
|
|
5
|
+
select: ReturnType<typeof knex>['select'];
|
|
6
|
+
delete: ReturnType<typeof knex>['delete'];
|
|
7
|
+
update: ReturnType<typeof knex>['update'];
|
|
8
|
+
insert: ReturnType<typeof knex>['insert'];
|
|
9
|
+
schema: ReturnType<typeof knex>['schema'];
|
|
10
|
+
constructor(ctx: Context);
|
|
11
|
+
}
|
|
12
|
+
export default Database;
|
|
@@ -0,0 +1,43 @@
|
|
|
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.Database = void 0;
|
|
7
|
+
const core_1 = require("@kotori-bot/core");
|
|
8
|
+
const knex_1 = __importDefault(require("knex"));
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
class Database extends core_1.Service {
|
|
11
|
+
internal;
|
|
12
|
+
select;
|
|
13
|
+
delete;
|
|
14
|
+
update;
|
|
15
|
+
insert;
|
|
16
|
+
schema;
|
|
17
|
+
constructor(ctx) {
|
|
18
|
+
super(ctx, {}, 'database');
|
|
19
|
+
this.internal = (0, knex_1.default)({
|
|
20
|
+
client: 'sqlite',
|
|
21
|
+
connection: {
|
|
22
|
+
filename: (0, path_1.resolve)(this.ctx.baseDir.root, 'kotori.db')
|
|
23
|
+
},
|
|
24
|
+
useNullAsDefault: true
|
|
25
|
+
} /* {
|
|
26
|
+
client: 'mysql',
|
|
27
|
+
connection: {
|
|
28
|
+
host: '127.0.0.1',
|
|
29
|
+
port: 3306,
|
|
30
|
+
user: 'kotori',
|
|
31
|
+
password: 'kotori',
|
|
32
|
+
database: 'kotori'
|
|
33
|
+
}
|
|
34
|
+
} */);
|
|
35
|
+
this.select = this.internal.select.bind(this.internal);
|
|
36
|
+
this.delete = this.internal.delete.bind(this.internal);
|
|
37
|
+
this.update = this.internal.update.bind(this.internal);
|
|
38
|
+
this.insert = this.internal.insert.bind(this.internal);
|
|
39
|
+
this.schema = this.internal.schema;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.Database = Database;
|
|
43
|
+
exports.default = Database;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context, Service, createConfig, loadConfig, saveConfig } from '@kotori-bot/core';
|
|
2
|
+
export declare class File extends Service {
|
|
3
|
+
constructor(ctx: Context);
|
|
4
|
+
getDir(): string;
|
|
5
|
+
getFile(filename: string): string;
|
|
6
|
+
load(filename: string, type?: Parameters<typeof loadConfig>[1], init?: Parameters<typeof loadConfig>[2]): string | object | unknown[] | null;
|
|
7
|
+
save(filename: string, data: Parameters<typeof saveConfig>[1], type?: Parameters<typeof saveConfig>[2]): void;
|
|
8
|
+
create(filename: string, data?: Parameters<typeof createConfig>[1], type?: Parameters<typeof createConfig>[2]): void;
|
|
9
|
+
}
|
|
10
|
+
export default File;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.File = void 0;
|
|
4
|
+
const core_1 = require("@kotori-bot/core");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
class File extends core_1.Service {
|
|
7
|
+
constructor(ctx) {
|
|
8
|
+
super(ctx, {}, 'file');
|
|
9
|
+
}
|
|
10
|
+
getDir() {
|
|
11
|
+
return (0, path_1.join)(this.ctx.baseDir.data, ...(this.ctx.identity ? this.ctx.identity.split('/') : []));
|
|
12
|
+
}
|
|
13
|
+
getFile(filename) {
|
|
14
|
+
return (0, path_1.join)(this.getDir(), filename);
|
|
15
|
+
}
|
|
16
|
+
load(filename, type, init) {
|
|
17
|
+
return (0, core_1.loadConfig)(this.getFile(filename), type, init);
|
|
18
|
+
}
|
|
19
|
+
save(filename, data, type) {
|
|
20
|
+
(0, core_1.saveConfig)(this.getFile(filename), data, type);
|
|
21
|
+
}
|
|
22
|
+
create(filename, data, type) {
|
|
23
|
+
(0, core_1.createConfig)(this.getFile(filename), data, type);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.File = File;
|
|
27
|
+
exports.default = File;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="body-parser" />
|
|
2
|
+
/// <reference types="connect" />
|
|
3
|
+
/// <reference types="serve-static" />
|
|
4
|
+
import { Context, Service } from '@kotori-bot/core';
|
|
5
|
+
import express from 'express';
|
|
6
|
+
interface ServerConfig {
|
|
7
|
+
port: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class Server extends Service<ServerConfig> {
|
|
10
|
+
private app;
|
|
11
|
+
private server?;
|
|
12
|
+
constructor(ctx: Context, config: ServerConfig);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
get: Server['app']['get'];
|
|
16
|
+
post: Server['app']['post'];
|
|
17
|
+
patch: Server['app']['patch'];
|
|
18
|
+
put: Server['app']['put'];
|
|
19
|
+
delete: Server['app']['delete'];
|
|
20
|
+
all: Server['app']['all'];
|
|
21
|
+
use: Server['app']['use'];
|
|
22
|
+
router: typeof express.Router;
|
|
23
|
+
json: (options?: import("body-parser").OptionsJson | undefined) => import("connect").NextHandleFunction;
|
|
24
|
+
static: import("serve-static").RequestHandlerConstructor<express.Response<any, Record<string, any>>>;
|
|
25
|
+
}
|
|
26
|
+
export default Server;
|
|
@@ -0,0 +1,60 @@
|
|
|
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.Server = void 0;
|
|
7
|
+
const core_1 = require("@kotori-bot/core");
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
class Server extends core_1.Service {
|
|
10
|
+
app;
|
|
11
|
+
server;
|
|
12
|
+
constructor(ctx, config) {
|
|
13
|
+
super(ctx, config, 'server');
|
|
14
|
+
this.app = (0, express_1.default)();
|
|
15
|
+
this.app.use('/', (_, res, next) => {
|
|
16
|
+
let isWebui = false;
|
|
17
|
+
ctx[core_1.Symbols.modules].forEach((module) => {
|
|
18
|
+
if (isWebui)
|
|
19
|
+
return;
|
|
20
|
+
if (module[0].pkg.name === '@kotori-bot/kotori-plugin-webui')
|
|
21
|
+
isWebui = true;
|
|
22
|
+
});
|
|
23
|
+
if (!isWebui) {
|
|
24
|
+
res.setHeader('Content-type', 'text/html');
|
|
25
|
+
res.send(/* html */ `<h1>Welcome to kotori!</h1>`);
|
|
26
|
+
}
|
|
27
|
+
next();
|
|
28
|
+
});
|
|
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
|
+
}
|
|
37
|
+
start() {
|
|
38
|
+
if (this.server)
|
|
39
|
+
return;
|
|
40
|
+
this.server = this.app.listen(this.config.port);
|
|
41
|
+
this.ctx.logger.label('server').info(`server start at http://127.0.0.1:${this.config.port}`);
|
|
42
|
+
}
|
|
43
|
+
stop() {
|
|
44
|
+
if (!this.server)
|
|
45
|
+
return;
|
|
46
|
+
this.server.close();
|
|
47
|
+
}
|
|
48
|
+
get;
|
|
49
|
+
post;
|
|
50
|
+
patch;
|
|
51
|
+
put;
|
|
52
|
+
delete;
|
|
53
|
+
all;
|
|
54
|
+
use;
|
|
55
|
+
router = express_1.default.Router;
|
|
56
|
+
json = express_1.default.json;
|
|
57
|
+
static = express_1.default.static;
|
|
58
|
+
}
|
|
59
|
+
exports.Server = Server;
|
|
60
|
+
exports.default = Server;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Context } from '@kotori-bot/core';
|
|
2
|
+
import { Logger } from '@kotori-bot/logger';
|
|
3
|
+
export declare class KotoriLogger extends Logger {
|
|
4
|
+
private optionsSelf;
|
|
5
|
+
private ctx;
|
|
6
|
+
constructor(optionsSelf: Logger['options'], ctx: Context);
|
|
7
|
+
private setLabel;
|
|
8
|
+
fatal(...args: unknown[]): void;
|
|
9
|
+
error(...args: unknown[]): void;
|
|
10
|
+
warn(...args: unknown[]): void;
|
|
11
|
+
info(...args: unknown[]): void;
|
|
12
|
+
debug(...args: unknown[]): void;
|
|
13
|
+
trace(...args: unknown[]): void;
|
|
14
|
+
}
|
|
15
|
+
export default KotoriLogger;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KotoriLogger = void 0;
|
|
4
|
+
const logger_1 = require("@kotori-bot/logger");
|
|
5
|
+
class KotoriLogger extends logger_1.Logger {
|
|
6
|
+
optionsSelf;
|
|
7
|
+
ctx;
|
|
8
|
+
constructor(optionsSelf, ctx) {
|
|
9
|
+
super(optionsSelf);
|
|
10
|
+
this.optionsSelf = optionsSelf;
|
|
11
|
+
this.ctx = ctx;
|
|
12
|
+
}
|
|
13
|
+
setLabel() {
|
|
14
|
+
const origin = Object.create(this.optionsSelf.label);
|
|
15
|
+
const label = this.ctx.identity ? [this.ctx.identity, ...this.optionsSelf.label] : this.optionsSelf.label;
|
|
16
|
+
this[(() => 'options')()].label = label;
|
|
17
|
+
return () => {
|
|
18
|
+
this[(() => 'options')()].label = origin;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
fatal(...args) {
|
|
22
|
+
const dispose = this.setLabel();
|
|
23
|
+
super.fatal(...args);
|
|
24
|
+
dispose();
|
|
25
|
+
}
|
|
26
|
+
error(...args) {
|
|
27
|
+
const dispose = this.setLabel();
|
|
28
|
+
super.error(...args);
|
|
29
|
+
dispose();
|
|
30
|
+
}
|
|
31
|
+
warn(...args) {
|
|
32
|
+
const dispose = this.setLabel();
|
|
33
|
+
super.warn(...args);
|
|
34
|
+
dispose();
|
|
35
|
+
}
|
|
36
|
+
info(...args) {
|
|
37
|
+
const dispose = this.setLabel();
|
|
38
|
+
super.info(...args);
|
|
39
|
+
dispose();
|
|
40
|
+
}
|
|
41
|
+
debug(...args) {
|
|
42
|
+
const dispose = this.setLabel();
|
|
43
|
+
super.debug(...args);
|
|
44
|
+
dispose();
|
|
45
|
+
}
|
|
46
|
+
trace(...args) {
|
|
47
|
+
const dispose = this.setLabel();
|
|
48
|
+
super.trace(...args);
|
|
49
|
+
dispose();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.KotoriLogger = KotoriLogger;
|
|
53
|
+
exports.default = KotoriLogger;
|
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kotori-bot/loader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Loader For KotoriBot",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"author": "Hotaru <biyuehuya@gmail.com>",
|
|
8
|
-
"dependencies": {
|
|
9
|
-
"@kotori-bot/core": "^v1.2.0",
|
|
10
|
-
"@kotori-bot/logger": "^v1.1.1"
|
|
11
|
-
},
|
|
12
8
|
"keywords": [
|
|
13
9
|
"kotori",
|
|
14
10
|
"chatbot",
|
|
@@ -26,5 +22,19 @@
|
|
|
26
22
|
"type": "git",
|
|
27
23
|
"url": "git+https://github.com/kotorijs/kotori.git"
|
|
28
24
|
},
|
|
29
|
-
"homepage": "https://kotori.js.org"
|
|
25
|
+
"homepage": "https://kotori.js.org",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"express": "^4.18.2",
|
|
28
|
+
"knex": "^3.1.0",
|
|
29
|
+
"sqlite3": "^5.1.7",
|
|
30
|
+
"@kotori-bot/core": "^1.3.0",
|
|
31
|
+
"@kotori-bot/logger": "^1.2.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/body-parser": "^1.19.5",
|
|
35
|
+
"@types/connect": "^3.4.38",
|
|
36
|
+
"@types/express": "^4.17.21",
|
|
37
|
+
"@types/express-serve-static-core": "^4.17.43",
|
|
38
|
+
"@types/serve-static": "^1.15.5"
|
|
39
|
+
}
|
|
30
40
|
}
|