@zhin.js/core 1.0.7 → 1.0.9
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/CHANGELOG.md +15 -0
- package/README.md +28 -12
- package/lib/adapter.d.ts +8 -6
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +13 -7
- package/lib/adapter.js.map +1 -1
- package/lib/app.d.ts +72 -14
- package/lib/app.d.ts.map +1 -1
- package/lib/app.js +240 -79
- package/lib/app.js.map +1 -1
- package/lib/bot.d.ts +10 -8
- package/lib/bot.d.ts.map +1 -1
- package/lib/config.d.ts +44 -14
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +275 -208
- package/lib/config.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/log-transport.js +1 -1
- package/lib/log-transport.js.map +1 -1
- package/lib/models/system-log.d.ts +2 -2
- package/lib/models/system-log.d.ts.map +1 -1
- package/lib/models/system-log.js +1 -1
- package/lib/models/system-log.js.map +1 -1
- package/lib/models/user.d.ts +2 -2
- package/lib/models/user.d.ts.map +1 -1
- package/lib/models/user.js +1 -1
- package/lib/models/user.js.map +1 -1
- package/lib/plugin.d.ts +7 -3
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +16 -5
- package/lib/plugin.js.map +1 -1
- package/lib/prompt.d.ts +1 -1
- package/lib/prompt.d.ts.map +1 -1
- package/lib/prompt.js +9 -7
- package/lib/prompt.js.map +1 -1
- package/lib/types.d.ts +6 -5
- package/lib/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/adapter.ts +18 -11
- package/src/app.ts +358 -102
- package/src/bot.ts +27 -25
- package/src/config.ts +352 -230
- package/src/index.ts +1 -1
- package/src/log-transport.ts +1 -1
- package/src/models/system-log.ts +2 -2
- package/src/models/user.ts +2 -2
- package/src/plugin.ts +19 -6
- package/src/prompt.ts +10 -9
- package/src/types.ts +8 -5
- package/tests/adapter.test.ts +5 -200
- package/tests/app.test.ts +208 -181
- package/tests/command.test.ts +2 -2
- package/tests/config.test.ts +5 -326
- package/tests/cron.test.ts +277 -0
- package/tests/jsx.test.ts +300 -0
- package/tests/permissions.test.ts +358 -0
- package/tests/prompt.test.ts +223 -0
- package/tests/schema.test.ts +248 -0
- package/lib/schema.d.ts +0 -83
- package/lib/schema.d.ts.map +0 -1
- package/lib/schema.js +0 -245
- package/lib/schema.js.map +0 -1
- package/src/schema.ts +0 -273
package/src/app.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { SideEffect, GlobalContext, Models } from "@zhin.js/types";
|
|
3
|
+
import { Schema } from '@zhin.js/hmr';
|
|
3
4
|
import {
|
|
4
5
|
HMR,
|
|
5
6
|
Context,
|
|
6
7
|
Logger,
|
|
7
8
|
getCallerFile,
|
|
8
9
|
getCallerFiles,
|
|
10
|
+
mergeConfig,
|
|
9
11
|
} from "@zhin.js/hmr";
|
|
10
12
|
import {
|
|
11
13
|
AdapterMessage,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
SendOptions,
|
|
16
18
|
MessageMiddleware,
|
|
17
19
|
} from "./types.js";
|
|
20
|
+
import { Config } from "./config.js";
|
|
18
21
|
import { Message } from "./message.js";
|
|
19
22
|
import { fileURLToPath } from "url";
|
|
20
23
|
import { generateEnvTypes } from "./types-generator.js";
|
|
@@ -27,15 +30,21 @@ import { Plugin } from "./plugin.js";
|
|
|
27
30
|
import { Adapter } from "./adapter";
|
|
28
31
|
import { MessageCommand } from "./command";
|
|
29
32
|
import { Component } from "./component";
|
|
30
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
RelatedDatabase,
|
|
35
|
+
DocumentDatabase,
|
|
36
|
+
KeyValueDatabase,
|
|
37
|
+
Definition,
|
|
38
|
+
Registry,
|
|
39
|
+
} from "@zhin.js/database";
|
|
31
40
|
import { DatabaseLogTransport } from "./log-transport.js";
|
|
32
|
-
import { SystemLog,
|
|
33
|
-
import { User,
|
|
41
|
+
import { SystemLog, SystemLogDefinition } from "./models/system-log.js";
|
|
42
|
+
import { User, UserDefinition } from "./models/user.js";
|
|
34
43
|
import { addTransport, removeTransport } from "@zhin.js/logger";
|
|
35
44
|
declare module "@zhin.js/types" {
|
|
36
45
|
interface Models {
|
|
37
46
|
SystemLog: SystemLog;
|
|
38
|
-
User: User
|
|
47
|
+
User: User;
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
|
|
@@ -48,45 +57,47 @@ declare module "@zhin.js/types" {
|
|
|
48
57
|
*/
|
|
49
58
|
export class App extends HMR<Plugin> {
|
|
50
59
|
static currentPlugin: Plugin;
|
|
51
|
-
private config: AppConfig;
|
|
52
60
|
middlewares: MessageMiddleware[] = [];
|
|
53
61
|
adapters: string[] = [];
|
|
54
|
-
|
|
62
|
+
#config: Config<AppConfig>;
|
|
63
|
+
database?:
|
|
64
|
+
| RelatedDatabase<any, Models>
|
|
65
|
+
| DocumentDatabase<any, Models>
|
|
66
|
+
| KeyValueDatabase<any, Models>;
|
|
55
67
|
permissions: Permissions = new Permissions(this);
|
|
56
68
|
private logTransport?: DatabaseLogTransport;
|
|
69
|
+
/** 配置变更处理锁 */
|
|
70
|
+
private configChangeLock: Promise<void> | null = null;
|
|
57
71
|
/**
|
|
58
72
|
* 构造函数:初始化应用,加载配置,注册全局异常处理
|
|
59
|
-
* @param config
|
|
73
|
+
* @param config 可选,应用配置,若为空则自动查找配置文件
|
|
60
74
|
*/
|
|
61
|
-
constructor(config
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// 合并默认配置和传入的配置
|
|
78
|
-
finalConfig = Object.assign({}, App.defaultConfig, config);
|
|
79
|
-
}
|
|
80
|
-
|
|
75
|
+
constructor(config: AppConfig);
|
|
76
|
+
/**
|
|
77
|
+
* 构造函数:初始化应用,加载配置,注册全局异常处理
|
|
78
|
+
* @param config_file 可选,配置文件路径,默认为 'zhin.config.yml'
|
|
79
|
+
*/
|
|
80
|
+
constructor(config_file?: string);
|
|
81
|
+
constructor(config_param: string | AppConfig = "zhin.config.yml") {
|
|
82
|
+
const config_file =
|
|
83
|
+
typeof config_param === "string" ? config_param : "zhin.config.yml";
|
|
84
|
+
const config_obj =
|
|
85
|
+
typeof config_param === "object" ? config_param : App.defaultConfig;
|
|
86
|
+
const config = new Config<AppConfig>(
|
|
87
|
+
config_file,
|
|
88
|
+
App.schema,
|
|
89
|
+
mergeConfig(App.defaultConfig, config_obj)
|
|
90
|
+
);
|
|
81
91
|
// 调用父类构造函数
|
|
82
|
-
super(
|
|
92
|
+
super({
|
|
83
93
|
logger,
|
|
84
|
-
dirs:
|
|
94
|
+
dirs: config.get("plugin_dirs") || [],
|
|
85
95
|
extensions: new Set([".js", ".ts", ".jsx", ".tsx"]),
|
|
86
|
-
debug:
|
|
96
|
+
debug: config.get("debug"),
|
|
87
97
|
});
|
|
98
|
+
this.watching(config.filepath,()=>config.reload());
|
|
88
99
|
this.on("message.send", this.sendMessage.bind(this));
|
|
89
|
-
this.on(
|
|
100
|
+
this.on("message.receive", this.receiveMessage.bind(this));
|
|
90
101
|
process.on("uncaughtException", (e) => {
|
|
91
102
|
const args = e instanceof Error ? [e.message, { stack: e.stack }] : [e];
|
|
92
103
|
this.logger.error(...args);
|
|
@@ -95,17 +106,175 @@ export class App extends HMR<Plugin> {
|
|
|
95
106
|
const args = e instanceof Error ? [e.message, { stack: e.stack }] : [e];
|
|
96
107
|
this.logger.error(...args);
|
|
97
108
|
});
|
|
98
|
-
this
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
this.#config = config;
|
|
110
|
+
// 监听配置变更
|
|
111
|
+
config.on("change", (before, after) => {
|
|
112
|
+
this.handleConfigChange(before, after);
|
|
113
|
+
this.broadcast("config.change", before, after);
|
|
114
|
+
});
|
|
115
|
+
this.defineSchema(App.schema);
|
|
116
|
+
setLevel(config.get("log_level") || LogLevel.INFO);
|
|
117
|
+
this.middleware(this.messageMiddleware.bind(this));
|
|
101
118
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 处理配置变更
|
|
122
|
+
* 如果上一次变更未完成,等待其完成后再处理新的变更
|
|
123
|
+
*/
|
|
124
|
+
private async handleConfigChange(
|
|
125
|
+
before: AppConfig,
|
|
126
|
+
after: AppConfig
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
this.logger.info("configuration changed");
|
|
129
|
+
// 等待上一次配置变更处理完成
|
|
130
|
+
if (this.configChangeLock) {
|
|
131
|
+
this.logger.info("Waiting for previous config change to complete...");
|
|
132
|
+
await this.configChangeLock;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 创建新的锁
|
|
136
|
+
this.configChangeLock = this.applyConfigChanges(before, after);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await this.configChangeLock;
|
|
140
|
+
} finally {
|
|
141
|
+
this.configChangeLock = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 应用配置变更
|
|
147
|
+
*/
|
|
148
|
+
private async applyConfigChanges(
|
|
149
|
+
before: AppConfig,
|
|
150
|
+
after: AppConfig
|
|
151
|
+
): Promise<void> {
|
|
152
|
+
try {
|
|
153
|
+
// 1. 更新日志级别
|
|
154
|
+
if (after.log_level !== before.log_level) {
|
|
155
|
+
setLevel(after.log_level || LogLevel.INFO);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 2. 更新监听目录
|
|
159
|
+
await this.updateWatchDirs(
|
|
160
|
+
before.plugin_dirs || [],
|
|
161
|
+
after.plugin_dirs || []
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// 3. 更新插件加载
|
|
165
|
+
await this.updatePlugins(before.plugins || [], after.plugins || []);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
this.logger.error("Failed to apply configuration changes:", error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
// 4. 更新数据库连接
|
|
171
|
+
if (JSON.stringify(before.database) !== JSON.stringify(after.database)) {
|
|
172
|
+
this.database?.stop();
|
|
173
|
+
if (after.database) {
|
|
174
|
+
this.database = Registry.create(
|
|
175
|
+
(this.config.database as any).dialect,
|
|
176
|
+
this.config.database,
|
|
177
|
+
Object.fromEntries(this.definitions)
|
|
178
|
+
);
|
|
179
|
+
await this.database!.start();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 更新监听目录
|
|
186
|
+
*/
|
|
187
|
+
private async updateWatchDirs(
|
|
188
|
+
oldDirs: string[],
|
|
189
|
+
newDirs: string[]
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
const oldResolved = oldDirs.map((dir) => path.resolve(process.cwd(), dir));
|
|
192
|
+
const newResolved = newDirs.map((dir) => path.resolve(process.cwd(), dir));
|
|
193
|
+
|
|
194
|
+
// 找出需要移除的目录
|
|
195
|
+
const dirsToRemove = oldResolved.filter(
|
|
196
|
+
(dir) => !newResolved.includes(dir)
|
|
197
|
+
);
|
|
198
|
+
// 找出需要添加的目录
|
|
199
|
+
const dirsToAdd = newResolved.filter((dir) => !oldResolved.includes(dir));
|
|
200
|
+
|
|
201
|
+
// 移除过时的监听目录
|
|
202
|
+
for (const dir of dirsToRemove) {
|
|
203
|
+
this.removeWatchDir(dir);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 添加新的监听目录
|
|
207
|
+
for (const dir of dirsToAdd) {
|
|
208
|
+
this.addWatchDir(dir);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 更新插件加载
|
|
214
|
+
*/
|
|
215
|
+
private async updatePlugins(
|
|
216
|
+
oldPlugins: string[],
|
|
217
|
+
newPlugins: string[]
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
// 找出需要卸载的插件
|
|
220
|
+
const pluginsToUnload = oldPlugins.filter(
|
|
221
|
+
(plugin) => !newPlugins.includes(plugin)
|
|
222
|
+
);
|
|
223
|
+
// 找出需要加载的插件
|
|
224
|
+
const pluginsToLoad = newPlugins.filter(
|
|
225
|
+
(plugin) => !oldPlugins.includes(plugin)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// 卸载不再需要的插件
|
|
229
|
+
for (const pluginName of pluginsToUnload) {
|
|
230
|
+
await this.unloadPlugin(pluginName);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 加载新插件
|
|
234
|
+
for (const pluginName of pluginsToLoad) {
|
|
235
|
+
this.use(pluginName);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 等待新插件加载完成
|
|
239
|
+
if (pluginsToLoad.length > 0) {
|
|
240
|
+
await sleep(200);
|
|
241
|
+
await this.waitForReady();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 卸载插件
|
|
247
|
+
*/
|
|
248
|
+
private async unloadPlugin(pluginName: string): Promise<void> {
|
|
249
|
+
// 尝试找到插件 (使用 HMR 提供的方法)
|
|
250
|
+
const plugin = this.findPluginByName<Plugin>(pluginName);
|
|
251
|
+
if (plugin) {
|
|
252
|
+
// 找到插件的文件路径
|
|
253
|
+
const filePath = plugin.filename;
|
|
254
|
+
|
|
255
|
+
// 销毁插件
|
|
256
|
+
plugin.dispose();
|
|
257
|
+
|
|
258
|
+
// 从依赖映射中移除
|
|
259
|
+
this.dependencies.delete(filePath);
|
|
260
|
+
|
|
261
|
+
this.logger.info(`Plugin ${pluginName} unloaded successfully`);
|
|
262
|
+
} else {
|
|
263
|
+
this.logger.warn(`Plugin ${pluginName} not found, skipping unload`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async receiveMessage<P extends RegisteredAdapter>(
|
|
267
|
+
message: Message<AdapterMessage<P>>
|
|
268
|
+
) {
|
|
269
|
+
const middlewares = this.dependencyList.reduce(
|
|
270
|
+
(result, plugin) => {
|
|
271
|
+
result.push(...(plugin.middlewares as MessageMiddleware<P>[]));
|
|
272
|
+
return result;
|
|
273
|
+
},
|
|
274
|
+
[...this.middlewares] as MessageMiddleware<P>[]
|
|
275
|
+
);
|
|
276
|
+
const handle = compose(middlewares);
|
|
277
|
+
await handle(message);
|
|
109
278
|
}
|
|
110
279
|
async messageMiddleware(message: Message, next: () => Promise<void>) {
|
|
111
280
|
for (const command of this.commands) {
|
|
@@ -130,13 +299,13 @@ export class App extends HMR<Plugin> {
|
|
|
130
299
|
*/
|
|
131
300
|
static defaultConfig: AppConfig = {
|
|
132
301
|
log_level: LogLevel.INFO,
|
|
133
|
-
plugin_dirs: [
|
|
302
|
+
plugin_dirs: [],
|
|
134
303
|
plugins: [],
|
|
135
304
|
bots: [],
|
|
136
305
|
debug: false,
|
|
137
306
|
};
|
|
138
307
|
middleware(middleware: MessageMiddleware) {
|
|
139
|
-
this.middlewares.push(middleware)
|
|
308
|
+
this.middlewares.push(middleware);
|
|
140
309
|
}
|
|
141
310
|
/**
|
|
142
311
|
* 发送消息到指定适配器和机器人
|
|
@@ -181,30 +350,54 @@ export class App extends HMR<Plugin> {
|
|
|
181
350
|
* @param filePath 插件文件路径
|
|
182
351
|
*/
|
|
183
352
|
createDependency(name: string, filePath: string): Plugin {
|
|
184
|
-
return new Plugin(this, name, filePath)
|
|
353
|
+
return new Plugin(this, name, filePath);
|
|
185
354
|
}
|
|
186
355
|
|
|
187
356
|
/** 获取App配置 */
|
|
188
357
|
/**
|
|
189
358
|
* 获取App配置(只读)
|
|
190
359
|
*/
|
|
191
|
-
getConfig(): Readonly<AppConfig
|
|
192
|
-
|
|
360
|
+
getConfig(): Readonly<AppConfig>;
|
|
361
|
+
getConfig<T extends Config.Paths<AppConfig>>(
|
|
362
|
+
key: T
|
|
363
|
+
): Readonly<Config.Value<AppConfig, T>>;
|
|
364
|
+
getConfig<T extends Config.Paths<AppConfig>>(key?: T) {
|
|
365
|
+
if (key === undefined) {
|
|
366
|
+
return this.#config.config;
|
|
367
|
+
}
|
|
368
|
+
return this.#config.get(key);
|
|
193
369
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
370
|
+
setConfig(value: AppConfig): void;
|
|
371
|
+
setConfig<T extends Config.Paths<AppConfig>>(
|
|
372
|
+
key: T,
|
|
373
|
+
value: Config.Value<AppConfig, T>
|
|
374
|
+
): void;
|
|
375
|
+
setConfig<T extends Config.Paths<AppConfig>>(
|
|
376
|
+
key: T | AppConfig,
|
|
377
|
+
value?: Config.Value<AppConfig, T>
|
|
378
|
+
): void {
|
|
379
|
+
if (typeof key === "object") {
|
|
380
|
+
this.#config.config = key;
|
|
381
|
+
} else if (value !== undefined) {
|
|
382
|
+
this.#config.set(key, value);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
changeSchema(key: string, value: Schema) {
|
|
386
|
+
if (!App.schema.options.object) {
|
|
387
|
+
App.schema.options.object = {};
|
|
388
|
+
}
|
|
389
|
+
App.schema.options.object[key] = value;
|
|
390
|
+
this.#config.reload();
|
|
391
|
+
}
|
|
392
|
+
get config() {
|
|
393
|
+
return this.#config.config;
|
|
394
|
+
}
|
|
395
|
+
set config(newConfig: AppConfig) {
|
|
396
|
+
this.#config.config = newConfig;
|
|
397
|
+
if (newConfig.plugin_dirs) {
|
|
205
398
|
// 动态更新监听目录
|
|
206
399
|
const currentDirs = this.getWatchDirs();
|
|
207
|
-
const newDirs =
|
|
400
|
+
const newDirs = newConfig.plugin_dirs;
|
|
208
401
|
|
|
209
402
|
// 移除不再需要的目录
|
|
210
403
|
for (const dir of currentDirs) {
|
|
@@ -220,52 +413,61 @@ export class App extends HMR<Plugin> {
|
|
|
220
413
|
}
|
|
221
414
|
}
|
|
222
415
|
}
|
|
223
|
-
|
|
224
416
|
this.logger.info("App configuration updated", this.config);
|
|
225
417
|
}
|
|
226
|
-
get
|
|
227
|
-
return this.dependencyList.reduce(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
418
|
+
get definitions() {
|
|
419
|
+
return this.dependencyList.reduce(
|
|
420
|
+
(result, plugin) => {
|
|
421
|
+
plugin.definitions.forEach((definition, name) => {
|
|
422
|
+
result.set(name, definition);
|
|
423
|
+
});
|
|
424
|
+
return result;
|
|
425
|
+
},
|
|
426
|
+
new Map<string, Definition<any>>([
|
|
427
|
+
["SystemLog", SystemLogDefinition],
|
|
428
|
+
["User", UserDefinition],
|
|
429
|
+
])
|
|
430
|
+
);
|
|
236
431
|
}
|
|
237
432
|
/** 使用插件 */
|
|
238
433
|
use(filePath: string): void {
|
|
239
434
|
this.emit("internal.add", filePath);
|
|
240
435
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
await sleep(200);
|
|
250
|
-
const schemas: Record<string, Schema> = {};
|
|
251
|
-
for (const [name, schema] of this.schemas) {
|
|
252
|
-
schemas[name] = schema;
|
|
436
|
+
async #init() {
|
|
437
|
+
// 首次初始化时,执行配置应用逻辑
|
|
438
|
+
await this.handleConfigChange(App.defaultConfig, this.config);
|
|
439
|
+
|
|
440
|
+
// 初始化数据库
|
|
441
|
+
const definitions: Record<string, Definition> = {};
|
|
442
|
+
for (const [name, schema] of this.definitions) {
|
|
443
|
+
definitions[name] = schema;
|
|
253
444
|
}
|
|
254
445
|
if (this.config.database) {
|
|
255
|
-
this.database = Registry.create(
|
|
446
|
+
this.database = Registry.create(
|
|
447
|
+
(this.config.database as any).dialect,
|
|
448
|
+
this.config.database,
|
|
449
|
+
definitions
|
|
450
|
+
);
|
|
451
|
+
this.logger.info(`database init...`);
|
|
256
452
|
await this.database?.start();
|
|
257
453
|
this.logger.info(`database init success`);
|
|
258
|
-
|
|
259
|
-
// 初始化日志传输器
|
|
260
|
-
this.logTransport = new DatabaseLogTransport(this);
|
|
261
|
-
addTransport(this.logTransport);
|
|
262
|
-
this.logger.info(`database log transport registered`);
|
|
454
|
+
this.dispatch("database.ready", this.database);
|
|
263
455
|
} else {
|
|
264
456
|
this.logger.info(`database not configured, skipping database init`);
|
|
265
457
|
}
|
|
266
|
-
this.dispatch("database.ready", this.database);
|
|
267
458
|
// 等待所有插件就绪
|
|
268
459
|
await this.waitForReady();
|
|
460
|
+
}
|
|
461
|
+
/** 启动App */
|
|
462
|
+
async start(mode: "dev" | "prod" = "prod"): Promise<void> {
|
|
463
|
+
await generateEnvTypes(process.cwd());
|
|
464
|
+
await this.#init();
|
|
465
|
+
if (this.database) {
|
|
466
|
+
// 初始化日志传输器
|
|
467
|
+
this.logTransport = new DatabaseLogTransport(this);
|
|
468
|
+
addTransport(this.logTransport);
|
|
469
|
+
this.logger.info(`database log transport registered`);
|
|
470
|
+
}
|
|
269
471
|
this.logger.info("started successfully");
|
|
270
472
|
this.dispatch("app.ready");
|
|
271
473
|
}
|
|
@@ -313,6 +515,50 @@ export class App extends HMR<Plugin> {
|
|
|
313
515
|
return options;
|
|
314
516
|
}
|
|
315
517
|
}
|
|
518
|
+
export namespace App {
|
|
519
|
+
export const schema = Schema.object({
|
|
520
|
+
database: Schema.any().description("数据库配置"),
|
|
521
|
+
bots: Schema.list(Schema.any()).default([]).description("机器人配置列表"),
|
|
522
|
+
log_level: Schema.number()
|
|
523
|
+
.default(LogLevel.INFO)
|
|
524
|
+
.description("日志级别 (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=SILENT)")
|
|
525
|
+
.min(0)
|
|
526
|
+
.max(4),
|
|
527
|
+
log: Schema.object({
|
|
528
|
+
maxDays: Schema.number().default(7).description("日志最大保存天数"),
|
|
529
|
+
maxRecords: Schema.number().default(10000).description("日志最大记录数"),
|
|
530
|
+
cleanupInterval: Schema.number()
|
|
531
|
+
.default(24)
|
|
532
|
+
.description("日志清理间隔(小时)"),
|
|
533
|
+
})
|
|
534
|
+
.description("日志配置")
|
|
535
|
+
.default({
|
|
536
|
+
maxDays: 7,
|
|
537
|
+
maxRecords: 10000,
|
|
538
|
+
cleanupInterval: 24,
|
|
539
|
+
}),
|
|
540
|
+
plugin_dirs: Schema.list(Schema.string())
|
|
541
|
+
.default(["node_modules"])
|
|
542
|
+
.description("插件目录列表"),
|
|
543
|
+
|
|
544
|
+
plugins: Schema.list(Schema.string())
|
|
545
|
+
.default([])
|
|
546
|
+
.description("需要加载的插件列表"),
|
|
547
|
+
debug: Schema.boolean()
|
|
548
|
+
.default(false)
|
|
549
|
+
.description("是否启用调试模式"),
|
|
550
|
+
}).default({
|
|
551
|
+
log_level: LogLevel.INFO,
|
|
552
|
+
log: {
|
|
553
|
+
maxDays: 7,
|
|
554
|
+
maxRecords: 10000,
|
|
555
|
+
cleanupInterval: 24,
|
|
556
|
+
},
|
|
557
|
+
plugin_dirs: ["node_modules"],
|
|
558
|
+
plugins: [],
|
|
559
|
+
debug: false,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
316
562
|
|
|
317
563
|
// ============================================================================
|
|
318
564
|
// Hooks API
|
|
@@ -347,14 +593,17 @@ export function useApp(): App {
|
|
|
347
593
|
}
|
|
348
594
|
export function defineModel<T extends Record<string, any>>(
|
|
349
595
|
name: string,
|
|
350
|
-
schema:
|
|
596
|
+
schema: Definition<T>
|
|
351
597
|
) {
|
|
352
598
|
const plugin = usePlugin();
|
|
353
599
|
return plugin.defineModel(name, schema);
|
|
354
600
|
}
|
|
355
|
-
export function addPermit<T extends RegisteredAdapter>(
|
|
356
|
-
|
|
357
|
-
|
|
601
|
+
export function addPermit<T extends RegisteredAdapter>(
|
|
602
|
+
name: string | RegExp,
|
|
603
|
+
checker: PermissionChecker<T>
|
|
604
|
+
) {
|
|
605
|
+
const plugin = usePlugin();
|
|
606
|
+
return plugin.addPermit(name, checker);
|
|
358
607
|
}
|
|
359
608
|
/** 获取当前插件实例 */
|
|
360
609
|
export function usePlugin(): Plugin {
|
|
@@ -388,16 +637,15 @@ export function registerAdapter<T extends Adapter>(adapter: T) {
|
|
|
388
637
|
name: adapter.name,
|
|
389
638
|
description: `adapter for ${adapter.name}`,
|
|
390
639
|
async mounted(plugin) {
|
|
391
|
-
await adapter.
|
|
640
|
+
await adapter.mounted(plugin);
|
|
392
641
|
return adapter;
|
|
393
642
|
},
|
|
394
643
|
dispose() {
|
|
395
|
-
return adapter.
|
|
644
|
+
return adapter.dispose(plugin);
|
|
396
645
|
},
|
|
397
646
|
});
|
|
398
647
|
}
|
|
399
648
|
|
|
400
|
-
|
|
401
649
|
/** 标记必需的Context */
|
|
402
650
|
export function useContext<T extends (keyof GlobalContext)[]>(
|
|
403
651
|
...args: [...T, sideEffect: SideEffect<T>]
|
|
@@ -411,7 +659,14 @@ export function addMiddleware(middleware: MessageMiddleware): void {
|
|
|
411
659
|
const plugin = usePlugin();
|
|
412
660
|
plugin.addMiddleware(middleware);
|
|
413
661
|
}
|
|
414
|
-
export function onDatabaseReady(
|
|
662
|
+
export function onDatabaseReady(
|
|
663
|
+
callback: (
|
|
664
|
+
database:
|
|
665
|
+
| RelatedDatabase<any, Models>
|
|
666
|
+
| DocumentDatabase<any, Models>
|
|
667
|
+
| KeyValueDatabase<any, Models>
|
|
668
|
+
) => PromiseLike<void>
|
|
669
|
+
) {
|
|
415
670
|
const plugin = usePlugin();
|
|
416
671
|
if (plugin.app.database?.isStarted) callback(plugin.app.database);
|
|
417
672
|
plugin.on("database.ready", callback);
|
|
@@ -432,9 +687,7 @@ export function addCommand(command: MessageCommand): void {
|
|
|
432
687
|
}
|
|
433
688
|
|
|
434
689
|
/** 添加组件 */
|
|
435
|
-
export function addComponent<P = any>(
|
|
436
|
-
component: Component<P>
|
|
437
|
-
): void {
|
|
690
|
+
export function addComponent<P = any>(component: Component<P>): void {
|
|
438
691
|
const plugin = usePlugin();
|
|
439
692
|
plugin.addComponent(component);
|
|
440
693
|
}
|
|
@@ -497,7 +750,10 @@ export async function sendMessage(options: SendOptions): Promise<void> {
|
|
|
497
750
|
const app = useApp();
|
|
498
751
|
await app.sendMessage(options);
|
|
499
752
|
}
|
|
500
|
-
|
|
753
|
+
export function defineSchema<S extends Schema>(rules: S): S {
|
|
754
|
+
const plugin = usePlugin();
|
|
755
|
+
return plugin.defineSchema(rules);
|
|
756
|
+
}
|
|
501
757
|
/** 获取App实例(用于高级操作) */
|
|
502
758
|
export function getAppInstance(): App {
|
|
503
759
|
return useApp();
|
|
@@ -510,8 +766,8 @@ export function useLogger(): Logger {
|
|
|
510
766
|
}
|
|
511
767
|
|
|
512
768
|
/** 创建App实例的工厂函数 */
|
|
513
|
-
export async function createApp(
|
|
514
|
-
const app = new App(
|
|
769
|
+
export async function createApp(config_file?: string): Promise<App> {
|
|
770
|
+
const app = new App(config_file);
|
|
515
771
|
await app.start();
|
|
516
772
|
return app;
|
|
517
773
|
}
|
package/src/bot.ts
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
import type { SendOptions} from "./types.js";
|
|
2
|
-
import {Message} from "./message.js";
|
|
1
|
+
import type { SendOptions } from "./types.js";
|
|
2
|
+
import { Message } from "./message.js";
|
|
3
3
|
/**
|
|
4
4
|
* Bot接口:所有平台机器人需实现的统一接口。
|
|
5
5
|
* 负责消息格式化、连接、断开、消息发送等。
|
|
6
6
|
* @template M 消息类型
|
|
7
7
|
* @template T 配置类型
|
|
8
8
|
*/
|
|
9
|
-
export interface Bot<M extends object={},T extends
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
export interface Bot<M extends object = {}, T extends Bot.Config = Bot.Config> {
|
|
10
|
+
/** 机器人配置 */
|
|
11
|
+
$config: T;
|
|
12
|
+
/** 是否已连接 */
|
|
13
|
+
$connected?: boolean;
|
|
14
|
+
/** 格式化平台消息为标准Message结构 */
|
|
15
|
+
$formatMessage(message: M): Message<M>;
|
|
16
|
+
/** 连接机器人 */
|
|
17
|
+
$connect(): Promise<void>;
|
|
18
|
+
/** 断开机器人 */
|
|
19
|
+
$disconnect(): Promise<void>;
|
|
20
|
+
/** 撤回消息 */
|
|
21
|
+
$recallMessage(id: string): Promise<void>;
|
|
22
|
+
/** 发送消息返回消息id */
|
|
23
|
+
$sendMessage(options: SendOptions): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
export namespace Bot {
|
|
26
|
+
/**
|
|
27
|
+
* Bot配置类型,所有平台机器人通用
|
|
28
|
+
*/
|
|
29
|
+
export interface Config {
|
|
30
|
+
context: string;
|
|
31
|
+
name: string;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
24
34
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Bot配置类型,所有平台机器人通用
|
|
27
|
-
*/
|
|
28
|
-
export interface BotConfig{
|
|
29
|
-
context:string
|
|
30
|
-
name:string
|
|
31
|
-
[key:string]:any
|
|
32
|
-
}
|