@kotori-bot/loader 1.4.1 → 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/README.md CHANGED
@@ -1,27 +1,27 @@
1
- # @kotori-bot/core
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
-
25
- ## Reference
26
-
27
- - [Kotori Docs](https://kotori.js.org/)
1
+ # @kotori-bot/core
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
+
25
+ ## Reference
26
+
27
+ - [Kotori Docs](https://kotori.js.org/)
@@ -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";
@@ -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';
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)|(1\.2\.0)/;
10
+ exports.SUPPORTS_VERSION = /(1\.1\.0)|(1\.2\.0)|(1\.3\.(.*?))/;
11
11
  exports.SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
@@ -0,0 +1,5 @@
1
+ import Decorators from './utils';
2
+ export declare function plugins(plugin: string | string[] | {
3
+ name: string;
4
+ }): Decorators;
5
+ export default plugins;
@@ -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
@@ -1,3 +1,4 @@
1
- export * from './loader';
2
- export * from './consts';
1
+ export * from './class/loader';
2
+ export * from './constants';
3
+ export * from './decorators';
3
4
  export * from '@kotori-bot/logger';
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-20 20:20:54
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("./consts"), exports);
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.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Container, Symbols } from '@kotori-bot/core';
2
2
  import Logger from '@kotori-bot/logger';
3
3
  import Runner from './runner';
4
4
  import Server from './service/server';
5
- import Database from './service/database';
5
+ import type Database from './service/database';
6
6
  import File from './service/file';
7
7
  declare module '@kotori-bot/core' {
8
8
  interface Context {
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-21 11:35:04
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,9 +40,8 @@ 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 consts_1 = require("./consts");
43
+ const constants_1 = require("./constants");
44
44
  const server_1 = __importDefault(require("./service/server"));
45
- const database_1 = __importDefault(require("./service/database"));
46
45
  const file_1 = __importDefault(require("./service/file"));
47
46
  function getRunnerConfig(file, dir) {
48
47
  const handle = (root) => {
@@ -59,7 +58,7 @@ function getRunnerConfig(file, dir) {
59
58
  return baseDir;
60
59
  };
61
60
  const options = {
62
- mode: file === consts_1.DEV_CONFIG_NAME ? 'dev' : 'build'
61
+ mode: file === constants_1.DEV_CONFIG_NAME ? 'dev' : 'build'
63
62
  };
64
63
  if (dir)
65
64
  return { baseDir: handle(path_1.default.resolve(dir)), options };
@@ -116,7 +115,7 @@ class Loader extends core_1.Container {
116
115
  loadCount = 0;
117
116
  constructor(options) {
118
117
  super();
119
- const file = options && options.mode === 'dev' ? consts_1.DEV_CONFIG_NAME : consts_1.BUILD_CONFIG_NAME;
118
+ const file = options && options.mode === 'dev' ? constants_1.DEV_CONFIG_NAME : constants_1.BUILD_CONFIG_NAME;
120
119
  const runnerConfig = getRunnerConfig(file, options?.dir);
121
120
  const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
122
121
  ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
@@ -203,8 +202,8 @@ class Loader extends core_1.Container {
203
202
  this.ctx.logger.info(`loaded module ${name} version: ${version} ${Array.isArray(author) ? `authors: ${author.join(',')}` : `author: ${author}`}`);
204
203
  const requiredVersion = peerDependencies['kotori-bot'];
205
204
  if (!requiredVersion.includes('workspace') &&
206
- (!consts_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
207
- if (consts_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
205
+ (!constants_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
206
+ if (constants_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
208
207
  this.ctx.logger.warn(`incomplete supported module version: ${requiredVersion}`);
209
208
  }
210
209
  else {
@@ -215,7 +214,6 @@ class Loader extends core_1.Container {
215
214
  }
216
215
  setPreService() {
217
216
  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
217
  this.ctx.service('file', new file_1.default(this.ctx.extends()));
220
218
  }
221
219
  loadAllModules() {
@@ -245,7 +243,7 @@ class Loader extends core_1.Container {
245
243
  async checkUpdate() {
246
244
  const { version } = this.ctx.pkg;
247
245
  const res = await this.ctx.http
248
- .get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/kotori/package.json" /* GLOBAL.UPDATE */)
246
+ .get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* GLOBAL.UPDATE */)
249
247
  .catch(() => this.ctx.logger.error('get update failed, please check your network'));
250
248
  if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
251
249
  this.ctx.logger.warn(`detection update failed`);
package/lib/log.js CHANGED
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.loadInfo = void 0;
4
4
  function loadInfo(info, ctx) {
5
5
  process.stdout.write('Kotori Bot is loading...');
6
- process.stdout.write(`
7
- ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗
8
- ██║ ██╔╝██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗██║
9
- █████╔╝ ██║ ██║ ██║ ██║ ██║██████╔╝██║
10
- ██╔═██╗ ██║ ██║ ██║ ██║ ██║██╔══██╗██║
11
- ██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
12
- ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
6
+ process.stdout.write(`
7
+ ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗
8
+ ██║ ██╔╝██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗██║
9
+ █████╔╝ ██║ ██║ ██║ ██║ ██║██████╔╝██║
10
+ ██╔═██╗ ██║ ██║ ██║ ██║ ██║██╔══██╗██║
11
+ ██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
12
+ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
13
13
  `);
14
14
  ctx.logger.info('loader base dir:', ctx.baseDir.root);
15
15
  ctx.logger.info(`Kotori Bot Version: ${info.version} License: ${info.license}`);
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 consts_1 = require("./consts");
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 ? consts_1.DEV_FILE : consts_1.BUILD_FILE))
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, consts_1.DEV_IMPORT));
146
- const main = path_1.default.resolve(dir, devMode ? consts_1.DEV_IMPORT : pkg.main);
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 ? consts_1.DEV_CODE_DIRS : path_1.default.dirname(pkg.main));
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 },
@@ -1,12 +1,11 @@
1
- import { Context, Service } from '@kotori-bot/core';
2
- import knex from 'knex';
3
- export declare class Database extends Service {
1
+ import { Service } from '@kotori-bot/core';
2
+ import type knex from 'knex';
3
+ export interface Database extends Service {
4
4
  internal: ReturnType<typeof knex>;
5
5
  select: ReturnType<typeof knex>['select'];
6
6
  delete: ReturnType<typeof knex>['delete'];
7
7
  update: ReturnType<typeof knex>['update'];
8
8
  insert: ReturnType<typeof knex>['insert'];
9
9
  schema: ReturnType<typeof knex>['schema'];
10
- constructor(ctx: Context);
11
10
  }
12
11
  export default Database;
@@ -1,43 +1,2 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  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;
@@ -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]): string | object | unknown[] | null;
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
  }
@@ -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
  }
@@ -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;
@@ -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
- 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}`);
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 (!this.server)
45
- return;
46
- this.server.close();
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;
@@ -0,0 +1,3 @@
1
+ import type { Context } from '@kotori-bot/core';
2
+ export declare function loadInfo(info: Context['pkg'], ctx: Context): void;
3
+ export default loadInfo;
@@ -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.4.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,10 +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
- "sqlite3": "^5.1.7",
30
- "@kotori-bot/core": "^1.3.0",
30
+ "ws": "^8.14.2",
31
+ "@kotori-bot/core": "^1.4.0",
31
32
  "@kotori-bot/logger": "^1.2.0"
32
33
  },
33
34
  "devDependencies": {