@kotori-bot/loader 1.2.0 → 1.3.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 ADDED
@@ -0,0 +1,5 @@
1
+ # @kotori-bot/core
2
+
3
+ ## Reference
4
+
5
+ - [Kotori Docs](https://kotori.js.org/)
package/lib/consts.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export declare const DEV_FILE = ".ts";
2
2
  export declare const BUILD_FILE = ".js";
3
- export declare const DEV_IMPORT = "./src/index.ts";
4
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;
package/lib/consts.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEV_CODE_DIRS = exports.DEV_IMPORT = exports.BUILD_FILE = exports.DEV_FILE = void 0;
3
+ exports.SUPPORTS_HALF_VERSION = exports.SUPPORTS_VERSION = exports.DEV_CONFIG_NAME = exports.BUILD_CONFIG_NAME = exports.DEV_IMPORT = exports.DEV_CODE_DIRS = exports.BUILD_FILE = exports.DEV_FILE = void 0;
4
4
  exports.DEV_FILE = '.ts';
5
5
  exports.BUILD_FILE = '.js';
6
- exports.DEV_IMPORT = './src/index.ts';
7
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)/;
11
+ exports.SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
package/lib/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import loader from './loader';
2
- export default loader;
1
+ export * from './loader';
2
+ export * from './consts';
3
+ export * from '@kotori-bot/logger';
package/lib/index.js CHANGED
@@ -1,14 +1,26 @@
1
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
2
  /*
7
3
  * @Author: Hotaru biyuehuya@gmail.com
8
4
  * @Blog: https://hotaru.icu
9
5
  * @Date: 2023-10-29 16:20:51
10
6
  * @LastEditors: Hotaru biyuehuya@gmail.com
11
- * @LastEditTime: 2023-12-30 17:32:51
7
+ * @LastEditTime: 2024-02-06 19:03:21
12
8
  */
13
- const loader_1 = __importDefault(require("./loader"));
14
- exports.default = loader_1.default;
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ __exportStar(require("./loader"), exports);
25
+ __exportStar(require("./consts"), exports);
26
+ __exportStar(require("@kotori-bot/logger"), exports);
package/lib/loader.d.ts CHANGED
@@ -1,13 +1,33 @@
1
- import { ContextInstance } from '@kotori-bot/core';
2
- declare class Main extends ContextInstance {
1
+ import { Container, Symbols } from '@kotori-bot/core';
2
+ import Logger from '@kotori-bot/logger';
3
+ import Runner from './runner';
4
+ declare module '@kotori-bot/core' {
5
+ interface Context {
6
+ readonly baseDir: Runner['baseDir'];
7
+ readonly options: Runner['options'];
8
+ readonly [Symbols.modules]: Runner[typeof Symbols.modules];
9
+ useAll(): void;
10
+ watcher(): void;
11
+ logger: Logger;
12
+ }
13
+ interface GlobalConfig {
14
+ dirs: string[];
15
+ }
16
+ }
17
+ export declare class Loader extends Container {
3
18
  private ctx;
4
- constructor();
19
+ private loadCount;
20
+ private failLoadCount;
21
+ constructor(options?: {
22
+ dir?: string;
23
+ mode?: string;
24
+ });
5
25
  run(): void;
6
26
  private handleError;
7
27
  private catchError;
8
28
  private listenMessage;
9
29
  private loadAllModule;
10
- private loadAllAdapter;
30
+ private loadAllService;
11
31
  private checkUpdate;
12
32
  }
13
- export default Main;
33
+ export default Loader;
package/lib/loader.js CHANGED
@@ -1,130 +1,257 @@
1
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
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Loader = void 0;
6
30
  /*
7
31
  * @Author: hotaru biyuehuya@gmail.com
8
32
  * @Blog: https://hotaru.icu
9
33
  * @Date: 2023-06-24 15:12:55
10
34
  * @LastEditors: Hotaru biyuehuya@gmail.com
11
- * @LastEditTime: 2023-12-30 19:12:43
35
+ * @LastEditTime: 2024-02-06 21:05:17
12
36
  */
13
37
  const core_1 = require("@kotori-bot/core");
14
- const modules_1 = __importDefault(require("./modules"));
15
- const global_1 = require("./global");
38
+ const path_1 = __importDefault(require("path"));
39
+ const fs_1 = __importDefault(require("fs"));
40
+ const logger_1 = __importDefault(require("@kotori-bot/logger"));
41
+ const runner_1 = __importStar(require("./runner"));
16
42
  const log_1 = __importDefault(require("./log"));
17
- const kotoriConfig = () => {
18
- let result = {};
19
- const baseDir = (0, global_1.getBaseDir)();
20
- result = {
21
- baseDir,
22
- config: (0, global_1.getGlobalConfig)(baseDir),
23
- options: {
24
- env: (0, global_1.isDev)() ? 'dev' : 'build',
25
- },
43
+ const consts_1 = require("./consts");
44
+ function getRunnerConfig(file, dir) {
45
+ const handle = (baseDir) => {
46
+ if (!fs_1.default.existsSync(baseDir.modules))
47
+ fs_1.default.mkdirSync(baseDir.modules);
48
+ if (!fs_1.default.existsSync(baseDir.logs))
49
+ fs_1.default.mkdirSync(baseDir.logs);
50
+ return baseDir;
26
51
  };
27
- return result;
28
- };
29
- class Main extends core_1.ContextInstance {
52
+ const options = {
53
+ mode: file === consts_1.DEV_CONFIG_NAME ? 'dev' : 'build'
54
+ };
55
+ 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
+ };
60
+ let root = path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
61
+ let count = 0;
62
+ while (!fs_1.default.existsSync(path_1.default.join(root, file))) {
63
+ if (count > 5) {
64
+ logger_1.default.fatal(`cannot find file ${file} `);
65
+ process.exit();
66
+ }
67
+ root = path_1.default.join(root, '..');
68
+ count += 1;
69
+ }
70
+ return {
71
+ baseDir: handle({ root, modules: path_1.default.join(root, 'modules'), logs: path_1.default.join(root, 'logs') }),
72
+ options
73
+ };
74
+ }
75
+ /* eslint consistent-return: 0 */
76
+ function getCoreConfig(file, baseDir) {
77
+ try {
78
+ const result1 = core_1.Tsu.Object({
79
+ global: core_1.Tsu.Object({
80
+ dirs: core_1.Tsu.Array(core_1.Tsu.String()).default([]),
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 {
30
111
  ctx;
31
- constructor() {
112
+ loadCount = 0;
113
+ failLoadCount = 0;
114
+ constructor(options) {
32
115
  super();
33
- core_1.ContextInstance.setInstance(new modules_1.default(kotoriConfig()));
34
- this.ctx = core_1.ContextInstance.getInstance();
35
- // 静态类型继续居然她妈是隔离的
116
+ const file = options && options.mode === 'dev' ? consts_1.DEV_CONFIG_NAME : consts_1.BUILD_CONFIG_NAME;
117
+ const runnerConfig = getRunnerConfig(file, options?.dir);
118
+ const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
119
+ ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
120
+ ctx.mixin('runner', ['baseDir', 'options', 'useAll', 'watcher']);
121
+ core_1.Container.setInstance(ctx);
122
+ this.ctx = core_1.Container.getInstance();
36
123
  }
37
124
  run() {
38
- (0, log_1.default)(this.ctx.package, this.ctx);
125
+ (0, log_1.default)(this.ctx.pkg, this.ctx);
39
126
  this.catchError();
40
127
  this.listenMessage();
41
128
  this.loadAllModule();
42
129
  this.checkUpdate();
43
130
  }
44
131
  handleError(err, prefix) {
45
- const isKotoriError = err instanceof core_1.KotoriError;
46
- if (!isKotoriError) {
47
- this.ctx.logger.tag(prefix, 'default', prefix === 'UCE' ? 'cyanBG' : 'greenBG').error(err);
132
+ if (!(err instanceof core_1.KotoriError)) {
133
+ this.ctx.logger.label(prefix).error(err);
48
134
  return;
49
135
  }
50
- this.ctx.logger
51
- .tag(err.name.split('Error')[0].toUpperCase(), 'yellow', 'default')[err.level === 'normal' ? 'error' : err.level](err.message, err.stack);
52
- if (err.name !== 'CoreError')
53
- return;
54
- this.ctx.logger.tag('CORE', 'black', 'red').error(err.message);
55
- process.emit('SIGINT');
136
+ ({
137
+ DatabaseError: () => this.ctx.logger.label('database').warn(err.message, err.stack),
138
+ ModuleError: () => this.ctx.logger.label('module').error(err.message, err.stack),
139
+ UnknownError: () => this.ctx.logger.error(err.name, err.stack),
140
+ DevError: () => this.ctx.logger.label('error').debug(err.name, err.stack)
141
+ })[err.name]();
56
142
  }
57
143
  catchError() {
58
- process.on('uncaughtExceptionMonitor', err => this.handleError(err, 'UCE'));
59
- process.on('unhandledRejection', err => this.handleError(err, 'UHR'));
60
- process.on('SIGINT', () => {
61
- process.exit();
62
- });
63
- this.ctx.logger.debug('Run info: develop with debuing...');
144
+ process.on('uncaughtExceptionMonitor', (err) => this.handleError(err, 'sync'));
145
+ process.on('unhandledRejection', (err) => this.handleError(err, 'async'));
146
+ process.on('SIGINT', () => process.exit());
147
+ this.ctx.logger.debug('run info: develop with debuing...');
64
148
  }
65
149
  listenMessage() {
66
- const handleConnectInfo = (data) => {
67
- if (!data.info)
150
+ this.ctx.on('connect', (data) => {
151
+ const { type, mode, normal, address, adapter } = data;
152
+ let msg;
153
+ if (type === 'connect') {
154
+ switch (mode) {
155
+ case 'ws':
156
+ msg = `${normal ? 'Connect' : 'Reconnect'} server to ${address}`;
157
+ break;
158
+ case 'ws-reverse':
159
+ msg = `server ${normal ? 'start' : 'restart'} at ${address}`;
160
+ break;
161
+ default:
162
+ msg = `ready completed about ${address}`;
163
+ }
164
+ }
165
+ else {
166
+ switch (mode) {
167
+ case 'ws':
168
+ msg = `disconnect server from ${address}${normal ? '' : ' unexpectedly'}`;
169
+ break;
170
+ case 'ws-reverse':
171
+ msg = `server stop at ${address}${normal ? '' : ' unexpectedly'}`;
172
+ break;
173
+ default:
174
+ msg = `dispose completed about ${address}`;
175
+ }
176
+ }
177
+ this.ctx.logger.label([adapter.platform, adapter.identity])[normal ? 'info' : 'warn'](msg);
178
+ });
179
+ this.ctx.on('status', (data) => {
180
+ const { status, adapter } = data;
181
+ this.ctx.logger.label([adapter.platform, adapter.identity]).info(status);
182
+ });
183
+ this.ctx.on('ready_module', (data) => {
184
+ if (!data.module || typeof data.module === 'string')
68
185
  return;
69
- this.ctx.logger[data.normal ? 'log' : 'warn'](`[${data.adapter.platform}]`, `${data.adapter.identity}:`, data.info);
70
- };
71
- this.ctx.on('connect', handleConnectInfo);
72
- this.ctx.on('disconnect', handleConnectInfo);
73
- this.ctx.on('load_module', data => {
74
- if (!data.module)
186
+ this.loadCount += 1;
187
+ const { name, version, author } = data.module.pkg;
188
+ if (data.error) {
189
+ this.failLoadCount += 1;
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
+ if (!requiredVersion.includes('workspace') &&
203
+ (!consts_1.SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion !== this.ctx.pkg.version)) {
204
+ if (consts_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
205
+ this.ctx.logger.warn(`incomplete supported module version: ${requiredVersion}`);
206
+ }
207
+ else {
208
+ this.ctx.logger.error(`unsupported module version: ${requiredVersion}`);
209
+ }
210
+ }
211
+ if (this.loadCount !== this.ctx.get('runner')[core_1.Symbols.modules].size)
75
212
  return;
76
- const { name, version, author } = data.module.package;
77
- this.ctx.logger.info(`Loaded ${data.moduleType} ${name} Version: ${version} ${Array.isArray(author) ? `Authors: ${author.join(',')}` : `Author: ${author}`}`);
78
- });
79
- this.ctx.on('load_all_module', data => {
80
- const failed = data.expected - data.reality;
81
- this.ctx.logger.info(`Loaded ${data.reality} modules (plugins)${failed > 0 ? `, failed to load ${failed} modules` : ''}`);
82
- this.loadAllAdapter();
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');
83
216
  });
84
217
  }
85
218
  loadAllModule() {
86
- this.ctx.moduleAll();
87
- if ((0, global_1.isDev)())
88
- this.ctx.watchFile();
219
+ this.ctx.useAll();
89
220
  }
90
- loadAllAdapter() {
91
- const adapters = this.ctx.internal.getAdapters();
92
- Object.keys(this.ctx.config.adapter).forEach(botName => {
221
+ loadAllService() {
222
+ /* load database before adapters */
223
+ const adapters = this.ctx[core_1.Symbols.adapter];
224
+ Object.keys(this.ctx.config.adapter).forEach((botName) => {
93
225
  const botConfig = this.ctx.config.adapter[botName];
94
- if (!(botConfig.extends in adapters)) {
95
- this.ctx.logger.warn(`Cannot find adapter '${botConfig.extends}' for ${botName}`);
226
+ const array = adapters.get(botConfig.extends);
227
+ if (!array) {
228
+ this.ctx.logger.warn(`cannot find adapter '${botConfig.extends}' for ${botName}`);
96
229
  return;
97
230
  }
98
- const array = adapters[botConfig.extends];
99
231
  const isSchema = array[1]?.parseSafe(botConfig);
100
- if (isSchema && !isSchema.value) {
232
+ if (isSchema && !isSchema.value)
101
233
  return;
102
- }
234
+ /* adapter donot support hot reload, so no extends for context */
103
235
  const bot = new array[0](this.ctx, isSchema ? isSchema.data : botConfig, botName);
104
- // if (!(botConfig.extend in Adapter)) Adapter.apis[botConfig.extend] = []; // I dont know whats this
105
- // this.ctx.botStack[botConfig.extend].push(bot.api);
106
- bot.start();
107
- }); /*
108
- const adapters: Adapter[] = [];
109
- Object.values(this.ctx.botStack).forEach(apis => {
110
- apis.forEach(api => adapters.push(api.adapter));
111
- }); */
112
- // this.ctx.emit({ type: 'adapters', adapters });
236
+ this.ctx.on('ready', () => bot.start());
237
+ });
238
+ /* load custom services after adapters */
113
239
  }
114
240
  async checkUpdate() {
115
- const { version } = this.ctx.package;
241
+ const { version } = this.ctx.pkg;
116
242
  const res = await this.ctx.http
117
243
  .get('https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/kotori/package.json')
118
- .catch(() => this.ctx.logger.error('Get update failed, please check your network'));
244
+ .catch(() => this.ctx.logger.error('get update failed, please check your network'));
119
245
  if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
120
- this.ctx.logger.error(`Detection update failed`);
246
+ this.ctx.logger.warn(`detection update failed`);
121
247
  }
122
248
  else if (version === res.version) {
123
- this.ctx.logger.log('Kotori is currently the latest version');
249
+ this.ctx.logger.info('kotori is currently the latest version');
124
250
  }
125
251
  else {
126
- 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`);
252
+ 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`);
127
253
  }
128
254
  }
129
255
  }
130
- exports.default = Main;
256
+ exports.Loader = Loader;
257
+ exports.default = Loader;
package/lib/log.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import type { Context, PackageInfo } from '@kotori-bot/core';
2
- export declare function loadInfo(info: PackageInfo, ctx: Context): void;
1
+ import type { Context } from '@kotori-bot/core';
2
+ export declare function loadInfo(info: Context['pkg'], ctx: Context): void;
3
3
  export default loadInfo;
package/lib/log.js CHANGED
@@ -2,18 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.loadInfo = void 0;
4
4
  function loadInfo(info, ctx) {
5
- console.info('Kotori Bot is loading...');
6
- console.info(`
7
- ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗
8
- ██║ ██╔╝██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗██║
9
- █████╔╝ ██║ ██║ ██║ ██║ ██║██████╔╝██║
10
- ██╔═██╗ ██║ ██║ ██║ ██║ ██║██╔══██╗██║
11
- ██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
12
- ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
5
+ process.stdout.write('Kotori Bot is loading...');
6
+ process.stdout.write(`
7
+ ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗
8
+ ██║ ██╔╝██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗██║
9
+ █████╔╝ ██║ ██║ ██║ ██║ ██║██████╔╝██║
10
+ ██╔═██╗ ██║ ██║ ██║ ██║ ██║██╔══██╗██║
11
+ ██║ ██╗╚██████╔╝ ██║ ╚██████╔╝██║ ██║██║
12
+ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
13
13
  `);
14
14
  ctx.logger.info(`Kotori Bot Version: ${info.version} License: ${info.license}`);
15
15
  ctx.logger.info(`Kotori Bot By ${info.author}`);
16
- ctx.logger.info(`Copyright © 2023 ${info.author} All rights reserved.`);
16
+ ctx.logger.info(`Copyright © 2023 - 2024 ${info.author} All rights reserved`);
17
17
  }
18
18
  exports.loadInfo = loadInfo;
19
19
  exports.default = loadInfo;
@@ -0,0 +1,29 @@
1
+ import { Context, ModuleConfig, ModuleInstance, Symbols } from '@kotori-bot/core';
2
+ interface BaseDir {
3
+ root: string;
4
+ modules: string;
5
+ logs: string;
6
+ }
7
+ interface Options {
8
+ mode: 'dev' | 'build';
9
+ }
10
+ interface RunnerConfig {
11
+ baseDir: BaseDir;
12
+ options: Options;
13
+ }
14
+ 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
+ export declare class Runner {
16
+ readonly baseDir: BaseDir;
17
+ readonly options: Options;
18
+ private ctx;
19
+ private isDev;
20
+ readonly [Symbols.modules]: Set<[ModuleInstance, ModuleConfig]>;
21
+ constructor(ctx: Context, config: RunnerConfig);
22
+ private getDirFiles;
23
+ private getModuleRootDir;
24
+ private getModuleList;
25
+ private moduleQuick;
26
+ useAll(): void;
27
+ watcher(): void;
28
+ }
29
+ export default Runner;
package/lib/runner.js ADDED
@@ -0,0 +1,183 @@
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 = __importDefault(require("path"));
32
+ const core_1 = require("@kotori-bot/core");
33
+ const logger_1 = __importStar(require("@kotori-bot/logger"));
34
+ const consts_1 = require("./consts");
35
+ exports.localeTypeSchema = core_1.Tsu.Union([
36
+ core_1.Tsu.Union([core_1.Tsu.Literal('en_US'), core_1.Tsu.Literal('ja_JP')]),
37
+ core_1.Tsu.Union([core_1.Tsu.Literal('zh_TW'), core_1.Tsu.Any()])
38
+ ]);
39
+ const modulePackageSchema = core_1.Tsu.Object({
40
+ name: core_1.Tsu.String().regexp(/kotori-plugin-[a-z]([a-z,0-9]{3,13})\b/),
41
+ version: core_1.Tsu.String(),
42
+ description: core_1.Tsu.String(),
43
+ main: core_1.Tsu.String(),
44
+ license: core_1.Tsu.Literal('GPL-3.0'),
45
+ keywords: core_1.Tsu.Custom((val) => Array.isArray(val) && val.includes('kotori') && val.includes('chatbot') && val.includes('kotori-plugin')),
46
+ author: core_1.Tsu.Union([core_1.Tsu.String(), core_1.Tsu.Array(core_1.Tsu.String())]),
47
+ peerDependencies: core_1.Tsu.Object({
48
+ 'kotori-bot': core_1.Tsu.String()
49
+ }),
50
+ kotori: core_1.Tsu.Object({
51
+ enforce: core_1.Tsu.Union([core_1.Tsu.Literal('pre'), core_1.Tsu.Literal('post')]).optional(),
52
+ meta: core_1.Tsu.Object({
53
+ language: core_1.Tsu.Array(exports.localeTypeSchema).default([])
54
+ }).default({ language: [] })
55
+ }).default({
56
+ enforce: undefined,
57
+ meta: { language: [] }
58
+ })
59
+ });
60
+ function moduleLoadOrder(pkg) {
61
+ if (pkg.name.includes(core_1.DATABASE_PREFIX))
62
+ return 1;
63
+ if (pkg.name.includes(core_1.ADAPTER_PREFIX))
64
+ return 2;
65
+ // if (CORE_MODULES.includes(pkg.name)) return 3;
66
+ if (pkg.kotori.enforce === 'pre')
67
+ return 4;
68
+ if (!pkg.kotori.enforce)
69
+ return 5;
70
+ return 6;
71
+ }
72
+ class Runner {
73
+ baseDir;
74
+ options;
75
+ ctx;
76
+ isDev;
77
+ [core_1.Symbols.modules] = new Set();
78
+ constructor(ctx, config) {
79
+ this.ctx = ctx;
80
+ /* handle config */
81
+ this.baseDir = config.baseDir;
82
+ this.options = config.options;
83
+ this.isDev = this.options.mode === 'dev';
84
+ if (this.isDev)
85
+ this.watcher();
86
+ const loggerOptions = {
87
+ level: this.isDev ? logger_1.LoggerLevel.TRACE : logger_1.LoggerLevel.INFO,
88
+ label: [],
89
+ transports: new logger_1.ConsoleTransport()
90
+ };
91
+ const logger = Object.assign(new logger_1.default(), { ctx: this.ctx });
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
+ })));
100
+ ctx.inject('logger');
101
+ }
102
+ getDirFiles(rootDir) {
103
+ const files = fs_1.default.readdirSync(rootDir);
104
+ const list = [];
105
+ files.forEach((fileName) => {
106
+ const file = path_1.default.join(rootDir, fileName);
107
+ if (fs_1.default.statSync(file).isDirectory()) {
108
+ list.push(...this.getDirFiles(file));
109
+ }
110
+ if (path_1.default.parse(file).ext !== (this.isDev ? consts_1.DEV_FILE : consts_1.BUILD_FILE))
111
+ return;
112
+ list.push(path_1.default.resolve(file));
113
+ });
114
+ return list;
115
+ }
116
+ getModuleRootDir() {
117
+ const moduleRootDir = [];
118
+ [
119
+ ...this.ctx.config.global.dirs.map((dir) => path_1.default.resolve(this.ctx.baseDir.root, dir)),
120
+ this.ctx.baseDir.modules
121
+ ].forEach((dir) => {
122
+ if (fs_1.default.existsSync(dir) && fs_1.default.statSync(dir).isDirectory())
123
+ moduleRootDir.push(dir);
124
+ });
125
+ return moduleRootDir;
126
+ }
127
+ getModuleList(rootDir) {
128
+ fs_1.default.readdirSync(rootDir).forEach(async (fileName) => {
129
+ const dir = path_1.default.join(rootDir, fileName);
130
+ if (!fs_1.default.statSync(dir).isDirectory())
131
+ return;
132
+ if (rootDir !== this.ctx.baseDir.modules && !fileName.startsWith(core_1.PLUGIN_PREFIX))
133
+ return;
134
+ const packagePath = path_1.default.join(dir, 'package.json');
135
+ let pkg;
136
+ if (!fs_1.default.existsSync(packagePath))
137
+ return;
138
+ try {
139
+ pkg = JSON.parse(fs_1.default.readFileSync(packagePath).toString());
140
+ }
141
+ catch {
142
+ throw new core_1.DevError(`illegal package.json ${packagePath}`);
143
+ }
144
+ const result = modulePackageSchema.parseSafe(pkg);
145
+ if (!result.value) {
146
+ if (rootDir !== this.ctx.baseDir.modules)
147
+ return;
148
+ throw new core_1.DevError(`package.json format error ${packagePath}: ${result.error.message}`);
149
+ }
150
+ pkg = result.data;
151
+ const devMode = this.isDev && (0, fs_1.existsSync)(path_1.default.resolve(dir, consts_1.DEV_IMPORT));
152
+ const main = path_1.default.resolve(dir, devMode ? consts_1.DEV_IMPORT : pkg.main);
153
+ if (!fs_1.default.existsSync(main))
154
+ throw new core_1.DevError(`cannot find ${main}`);
155
+ const dirs = path_1.default.join(dir, devMode ? consts_1.DEV_CODE_DIRS : path_1.default.dirname(pkg.main));
156
+ const files = fs_1.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
157
+ this[core_1.Symbols.modules].add([
158
+ { pkg, main, files },
159
+ this.ctx.config.plugin[(0, core_1.stringRightSplit)(pkg.name, core_1.PLUGIN_PREFIX)] || {}
160
+ ]);
161
+ });
162
+ }
163
+ moduleQuick(instance, config) {
164
+ this.ctx.use(instance, config);
165
+ }
166
+ useAll() {
167
+ this.getModuleRootDir().forEach((dir) => this.getModuleList(dir));
168
+ const modules = [];
169
+ this[core_1.Symbols.modules].forEach((val) => modules.push(val));
170
+ modules
171
+ .sort((el1, el2) => moduleLoadOrder(el1[0].pkg) - moduleLoadOrder(el2[0].pkg))
172
+ .forEach((el) => this.moduleQuick(...el));
173
+ }
174
+ watcher() {
175
+ this[core_1.Symbols.modules].forEach((data) => data[0].files.forEach((file) => fs_1.default.watchFile(file, async () => {
176
+ this.ctx.logger.debug(`file happen changed, module ${data[0].pkg.name} is reloading...`);
177
+ this.ctx.dispose(data[0]);
178
+ this.moduleQuick(...data);
179
+ })));
180
+ }
181
+ }
182
+ exports.Runner = Runner;
183
+ exports.default = Runner;
package/package.json CHANGED
@@ -1,18 +1,30 @@
1
1
  {
2
2
  "name": "@kotori-bot/loader",
3
- "version": "v1.2.0",
3
+ "version": "v1.3.0",
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
8
  "dependencies": {
9
- "@kotori-bot/core": "^v1.1.0"
9
+ "@kotori-bot/core": "^v1.2.0",
10
+ "@kotori-bot/logger": "^v1.1.1"
10
11
  },
12
+ "keywords": [
13
+ "kotori",
14
+ "chatbot",
15
+ "loader"
16
+ ],
11
17
  "files": [
12
- "lib"
18
+ "lib",
19
+ "LICENSE",
20
+ "README.md"
13
21
  ],
14
- "devDependencies": {
15
- "ts-node": "^10.9.2",
16
- "typescript": "^5.1.3"
17
- }
22
+ "bugs": {
23
+ "url": "https://github.com/kotorijs/kotori/issues"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/kotorijs/kotori.git"
28
+ },
29
+ "homepage": "https://kotori.js.org"
18
30
  }
package/lib/global.d.ts DELETED
@@ -1,25 +0,0 @@
1
- import { BaseDir } from '@kotori-bot/core';
2
- export declare const isDev: () => boolean;
3
- export declare const CONFIG_FILE: () => "kotori.dev.yml" | "kotori.yml";
4
- export declare const getBaseDir: () => {
5
- root: any;
6
- modules: string;
7
- };
8
- export declare const getGlobalConfig: (baseDir: BaseDir) => import("@kotori-bot/core").ObjectParserInfer<{
9
- global: import("@kotori-bot/core").IntersectionParser<[import("@kotori-bot/core").ObjectParser<{
10
- dirs: import("@kotori-bot/core").ArrayParser<import("@kotori-bot/core").StringParser>;
11
- }>, import("@kotori-bot/core").ObjectParser<{
12
- lang: 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_CN">, import("@kotori-bot/core").LiteralParser<"zh_TW">]>]>;
13
- 'command-prefix': import("@kotori-bot/core").StringParser;
14
- }>]>;
15
- adapter: import("@kotori-bot/core").Parser<import("@kotori-bot/core").IndexObject<import("@kotori-bot/core").ObjectParserInfer<{
16
- extends: import("@kotori-bot/core").StringParser;
17
- master: import("@kotori-bot/core").UnionParser<[import("@kotori-bot/core").NumberParser, import("@kotori-bot/core").StringParser]>;
18
- }> & import("@kotori-bot/core").ObjectParserInfer<{
19
- lang: 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_CN">, import("@kotori-bot/core").LiteralParser<"zh_TW">]>]>;
20
- 'command-prefix': import("@kotori-bot/core").StringParser;
21
- }>>>;
22
- plugin: import("@kotori-bot/core").Parser<import("@kotori-bot/core").IndexObject<import("@kotori-bot/core").ObjectParserInfer<{
23
- priority: import("@kotori-bot/core").NumberParser;
24
- }>>>;
25
- }>;
package/lib/global.js DELETED
@@ -1,53 +0,0 @@
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.getGlobalConfig = exports.getBaseDir = exports.CONFIG_FILE = exports.isDev = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const core_1 = require("@kotori-bot/core");
10
- const isDev = () => globalThis.env_mode === 'dev';
11
- exports.isDev = isDev;
12
- const CONFIG_FILE = () => ((0, exports.isDev)() ? 'kotori.dev.yml' : 'kotori.yml');
13
- exports.CONFIG_FILE = CONFIG_FILE;
14
- const getBaseDir = () => {
15
- const { env_dir: envDir } = globalThis;
16
- if (envDir) {
17
- return {
18
- root: envDir,
19
- modules: path_1.default.join(envDir, 'modules'),
20
- };
21
- }
22
- let root = path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
23
- let count = 0;
24
- while (!fs_1.default.existsSync(path_1.default.join(root, (0, exports.CONFIG_FILE)()))) {
25
- if (count > 5)
26
- throw new core_1.CoreError(`cannot find kotori-bot global ${exports.CONFIG_FILE}`);
27
- root = path_1.default.join(root, '..');
28
- count += 1;
29
- }
30
- return {
31
- root,
32
- modules: path_1.default.join(root, 'modules'),
33
- };
34
- };
35
- exports.getBaseDir = getBaseDir;
36
- const getGlobalConfig = (baseDir) => {
37
- const data = (0, core_1.loadConfig)(path_1.default.join(baseDir.root, (0, exports.CONFIG_FILE)()), 'yaml');
38
- const isExistsGlobal = data && typeof data === 'object' && data.global && typeof data.global === 'object';
39
- try {
40
- if (!isExistsGlobal)
41
- throw new core_1.TsuError('en_US', 'array_error');
42
- const lang = data.global.lang ? core_1.localeTypeSchema.parse(data.global.lang) : undefined;
43
- const commandPrefix = data.global['command-prefix'] ? data.global['command-prefix'] : undefined;
44
- return (0, core_1.globalConfigSchemaController)(lang, commandPrefix).parse(data);
45
- }
46
- catch (err) {
47
- if (!(err instanceof core_1.TsuError))
48
- throw err;
49
- // process.exit(1);
50
- throw new core_1.CoreError(`kotori-bot global ${exports.CONFIG_FILE} format error: ${err.message}`);
51
- }
52
- };
53
- exports.getGlobalConfig = getGlobalConfig;
package/lib/modules.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import { Context } from '@kotori-bot/core';
2
- declare module '@kotori-bot/core' {
3
- interface Context {
4
- readonly moduleAll?: () => void;
5
- readonly watchFile?: () => void;
6
- }
7
- }
8
- export declare class Modules extends Context {
9
- private isDev;
10
- private readonly moduleRootDir;
11
- private getDirFiles;
12
- private getModuleRootDir;
13
- private getModuleList;
14
- private moduleQuick;
15
- readonly moduleAll: () => Promise<void>;
16
- readonly watchFile: () => Promise<void>;
17
- }
18
- export default Modules;
package/lib/modules.js DELETED
@@ -1,84 +0,0 @@
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.Modules = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const core_1 = require("@kotori-bot/core");
10
- const consts_1 = require("./consts");
11
- class Modules extends core_1.Context {
12
- isDev = this.options.env === 'dev';
13
- moduleRootDir = [];
14
- getDirFiles(rootDir) {
15
- const files = fs_1.default.readdirSync(rootDir);
16
- const list = [];
17
- files.forEach(fileName => {
18
- const file = path_1.default.join(rootDir, fileName);
19
- if (fs_1.default.statSync(file).isDirectory()) {
20
- list.push(...this.getDirFiles(file));
21
- }
22
- if (path_1.default.parse(file).ext !== (this.isDev ? consts_1.DEV_FILE : consts_1.BUILD_FILE))
23
- return;
24
- list.push(path_1.default.resolve(file));
25
- });
26
- return list;
27
- }
28
- getModuleRootDir() {
29
- Object.assign(this.config.global.dirs, [this.baseDir.modules]).forEach(dir => {
30
- if (fs_1.default.existsSync(dir) && fs_1.default.statSync(dir).isDirectory())
31
- this.moduleRootDir.push(dir);
32
- });
33
- }
34
- getModuleList(rootDir) {
35
- const files = fs_1.default.readdirSync(rootDir);
36
- files.forEach(fileName => {
37
- const dir = path_1.default.join(rootDir, fileName);
38
- if (!fs_1.default.statSync(dir).isDirectory())
39
- return;
40
- if (rootDir !== this.baseDir.modules && fileName.startsWith(core_1.PLUGIN_PREFIX))
41
- return;
42
- const packagePath = path_1.default.join(dir, 'package.json');
43
- let packageJson;
44
- if (!fs_1.default.existsSync(packagePath))
45
- return;
46
- try {
47
- packageJson = JSON.parse(fs_1.default.readFileSync(packagePath).toString());
48
- }
49
- catch {
50
- throw new core_1.DevError(`illegal package.json ${packagePath}`);
51
- }
52
- const result = core_1.ModulePackageSchema.parseSafe(packageJson);
53
- if (!result.value && rootDir === this.baseDir.modules) {
54
- throw new core_1.DevError(`package.json format error ${packagePath}: ${result.error.message}`);
55
- }
56
- const mainPath = path_1.default.join(dir, this.isDev ? consts_1.DEV_IMPORT : packageJson.main);
57
- if (!fs_1.default.existsSync(mainPath))
58
- throw new core_1.DevError(`cannot find ${mainPath}`);
59
- const codeDirs = path_1.default.join(dir, this.isDev ? consts_1.DEV_CODE_DIRS : path_1.default.dirname(packageJson.main));
60
- this.moduleStack.push({
61
- package: packageJson,
62
- fileList: fs_1.default.statSync(codeDirs).isDirectory() ? this.getDirFiles(codeDirs) : [],
63
- mainPath: path_1.default.resolve(mainPath),
64
- });
65
- });
66
- }
67
- moduleQuick(moduleData) {
68
- return this.use(moduleData, this, this.config.plugin[(0, core_1.stringRightSplit)(moduleData.package.name, core_1.PLUGIN_PREFIX)] ?? {});
69
- }
70
- moduleAll = async () => {
71
- this.getModuleRootDir();
72
- this.moduleRootDir.forEach(dir => {
73
- this.getModuleList(dir);
74
- });
75
- const array = this.moduleStack.filter(data => data.package.name.startsWith(core_1.OFFICIAL_MODULES_SCOPE));
76
- array.push(...this.moduleStack.filter(data => !array.includes(data)));
77
- array.forEach(moduleData => this.moduleQuick(moduleData));
78
- };
79
- watchFile = async () => {
80
- this.moduleStack.forEach(moduleData => moduleData.fileList.forEach(file => fs_1.default.watchFile(file, () => this.dispose(moduleData) && this.moduleQuick(moduleData))));
81
- };
82
- }
83
- exports.Modules = Modules;
84
- exports.default = Modules;