@kotori-bot/loader 1.3.0 → 1.4.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,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-06 19:03:21
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
- useAll(): void;
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 loadAllModule;
30
- private loadAllService;
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-06 21:05:17
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 = (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);
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', 'useAll', 'watcher']);
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.loadAllModule();
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
- this.ctx.logger.label(prefix).error(err);
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
- 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]();
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
- this.ctx.logger.label([adapter.platform, adapter.identity])[normal ? 'info' : 'warn'](msg);
187
+ adapter.ctx.logger[normal ? 'info' : 'warn'](msg);
178
188
  });
179
189
  this.ctx.on('status', (data) => {
180
190
  const { status, adapter } = data;
181
- this.ctx.logger.label([adapter.platform, adapter.identity]).info(status);
191
+ adapter.ctx.logger.info(status);
182
192
  });
183
193
  this.ctx.on('ready_module', (data) => {
184
- if (!data.module || typeof data.module === 'string')
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 } = 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
+ 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
- loadAllModule() {
219
- this.ctx.useAll();
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
- loadAllService() {
222
- /* load database before adapters */
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 isSchema = array[1]?.parseSafe(botConfig);
232
- if (isSchema && !isSchema.value)
233
- return;
234
- /* adapter donot support hot reload, so no extends for context */
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('https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/kotori/package.json')
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, ModuleConfig, ModuleInstance, Symbols } from '@kotori-bot/core';
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]: Set<[ModuleInstance, ModuleConfig]>;
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 moduleQuick;
26
- useAll(): void;
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 = __importStar(require("@kotori-bot/logger"));
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]{3,13})\b/),
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 Set();
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: new logger_1.ConsoleTransport()
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
- 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
- })));
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].add([
158
- { pkg, main, files },
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
- moduleQuick(instance, config) {
164
- this.ctx.use(instance, config);
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
- useAll() {
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.moduleQuick(...el));
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.ctx.dispose(data[0]);
178
- this.moduleQuick(...data);
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": "v1.3.0",
3
+ "version": "1.4.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
- "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/logger": "^1.1.1",
31
+ "@kotori-bot/core": "^1.3.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
  }