@zhin.js/core 1.0.6 → 1.0.8
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 +17 -0
- 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 +241 -83
- 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 -105
- 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/plugin.test.ts +40 -177
- 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,33 +350,54 @@ export class App extends HMR<Plugin> {
|
|
|
181
350
|
* @param filePath 插件文件路径
|
|
182
351
|
*/
|
|
183
352
|
createDependency(name: string, filePath: string): Plugin {
|
|
184
|
-
|
|
185
|
-
// 将插件添加到依赖列表中
|
|
186
|
-
this.dependencies.set(name, plugin);
|
|
187
|
-
return plugin;
|
|
353
|
+
return new Plugin(this, name, filePath);
|
|
188
354
|
}
|
|
189
355
|
|
|
190
356
|
/** 获取App配置 */
|
|
191
357
|
/**
|
|
192
358
|
* 获取App配置(只读)
|
|
193
359
|
*/
|
|
194
|
-
getConfig(): Readonly<AppConfig
|
|
195
|
-
|
|
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);
|
|
196
369
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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) {
|
|
208
398
|
// 动态更新监听目录
|
|
209
399
|
const currentDirs = this.getWatchDirs();
|
|
210
|
-
const newDirs =
|
|
400
|
+
const newDirs = newConfig.plugin_dirs;
|
|
211
401
|
|
|
212
402
|
// 移除不再需要的目录
|
|
213
403
|
for (const dir of currentDirs) {
|
|
@@ -223,52 +413,61 @@ export class App extends HMR<Plugin> {
|
|
|
223
413
|
}
|
|
224
414
|
}
|
|
225
415
|
}
|
|
226
|
-
|
|
227
416
|
this.logger.info("App configuration updated", this.config);
|
|
228
417
|
}
|
|
229
|
-
get
|
|
230
|
-
return this.dependencyList.reduce(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
+
);
|
|
239
431
|
}
|
|
240
432
|
/** 使用插件 */
|
|
241
433
|
use(filePath: string): void {
|
|
242
434
|
this.emit("internal.add", filePath);
|
|
243
435
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
await sleep(200);
|
|
253
|
-
const schemas: Record<string, Schema> = {};
|
|
254
|
-
for (const [name, schema] of this.schemas) {
|
|
255
|
-
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;
|
|
256
444
|
}
|
|
257
445
|
if (this.config.database) {
|
|
258
|
-
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...`);
|
|
259
452
|
await this.database?.start();
|
|
260
453
|
this.logger.info(`database init success`);
|
|
261
|
-
|
|
262
|
-
// 初始化日志传输器
|
|
263
|
-
this.logTransport = new DatabaseLogTransport(this);
|
|
264
|
-
addTransport(this.logTransport);
|
|
265
|
-
this.logger.info(`database log transport registered`);
|
|
454
|
+
this.dispatch("database.ready", this.database);
|
|
266
455
|
} else {
|
|
267
456
|
this.logger.info(`database not configured, skipping database init`);
|
|
268
457
|
}
|
|
269
|
-
this.dispatch("database.ready", this.database);
|
|
270
458
|
// 等待所有插件就绪
|
|
271
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
|
+
}
|
|
272
471
|
this.logger.info("started successfully");
|
|
273
472
|
this.dispatch("app.ready");
|
|
274
473
|
}
|
|
@@ -316,6 +515,50 @@ export class App extends HMR<Plugin> {
|
|
|
316
515
|
return options;
|
|
317
516
|
}
|
|
318
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
|
+
}
|
|
319
562
|
|
|
320
563
|
// ============================================================================
|
|
321
564
|
// Hooks API
|
|
@@ -350,14 +593,17 @@ export function useApp(): App {
|
|
|
350
593
|
}
|
|
351
594
|
export function defineModel<T extends Record<string, any>>(
|
|
352
595
|
name: string,
|
|
353
|
-
schema:
|
|
596
|
+
schema: Definition<T>
|
|
354
597
|
) {
|
|
355
598
|
const plugin = usePlugin();
|
|
356
599
|
return plugin.defineModel(name, schema);
|
|
357
600
|
}
|
|
358
|
-
export function addPermit<T extends RegisteredAdapter>(
|
|
359
|
-
|
|
360
|
-
|
|
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);
|
|
361
607
|
}
|
|
362
608
|
/** 获取当前插件实例 */
|
|
363
609
|
export function usePlugin(): Plugin {
|
|
@@ -391,16 +637,15 @@ export function registerAdapter<T extends Adapter>(adapter: T) {
|
|
|
391
637
|
name: adapter.name,
|
|
392
638
|
description: `adapter for ${adapter.name}`,
|
|
393
639
|
async mounted(plugin) {
|
|
394
|
-
await adapter.
|
|
640
|
+
await adapter.mounted(plugin);
|
|
395
641
|
return adapter;
|
|
396
642
|
},
|
|
397
643
|
dispose() {
|
|
398
|
-
return adapter.
|
|
644
|
+
return adapter.dispose(plugin);
|
|
399
645
|
},
|
|
400
646
|
});
|
|
401
647
|
}
|
|
402
648
|
|
|
403
|
-
|
|
404
649
|
/** 标记必需的Context */
|
|
405
650
|
export function useContext<T extends (keyof GlobalContext)[]>(
|
|
406
651
|
...args: [...T, sideEffect: SideEffect<T>]
|
|
@@ -414,7 +659,14 @@ export function addMiddleware(middleware: MessageMiddleware): void {
|
|
|
414
659
|
const plugin = usePlugin();
|
|
415
660
|
plugin.addMiddleware(middleware);
|
|
416
661
|
}
|
|
417
|
-
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
|
+
) {
|
|
418
670
|
const plugin = usePlugin();
|
|
419
671
|
if (plugin.app.database?.isStarted) callback(plugin.app.database);
|
|
420
672
|
plugin.on("database.ready", callback);
|
|
@@ -435,9 +687,7 @@ export function addCommand(command: MessageCommand): void {
|
|
|
435
687
|
}
|
|
436
688
|
|
|
437
689
|
/** 添加组件 */
|
|
438
|
-
export function addComponent<P = any>(
|
|
439
|
-
component: Component<P>
|
|
440
|
-
): void {
|
|
690
|
+
export function addComponent<P = any>(component: Component<P>): void {
|
|
441
691
|
const plugin = usePlugin();
|
|
442
692
|
plugin.addComponent(component);
|
|
443
693
|
}
|
|
@@ -500,7 +750,10 @@ export async function sendMessage(options: SendOptions): Promise<void> {
|
|
|
500
750
|
const app = useApp();
|
|
501
751
|
await app.sendMessage(options);
|
|
502
752
|
}
|
|
503
|
-
|
|
753
|
+
export function defineSchema<S extends Schema>(rules: S): S {
|
|
754
|
+
const plugin = usePlugin();
|
|
755
|
+
return plugin.defineSchema(rules);
|
|
756
|
+
}
|
|
504
757
|
/** 获取App实例(用于高级操作) */
|
|
505
758
|
export function getAppInstance(): App {
|
|
506
759
|
return useApp();
|
|
@@ -513,8 +766,8 @@ export function useLogger(): Logger {
|
|
|
513
766
|
}
|
|
514
767
|
|
|
515
768
|
/** 创建App实例的工厂函数 */
|
|
516
|
-
export async function createApp(
|
|
517
|
-
const app = new App(
|
|
769
|
+
export async function createApp(config_file?: string): Promise<App> {
|
|
770
|
+
const app = new App(config_file);
|
|
518
771
|
await app.start();
|
|
519
772
|
return app;
|
|
520
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
|
-
}
|