@zhin.js/core 1.0.0 → 1.0.1
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 +9 -0
- package/LICENSE +21 -0
- package/README.md +295 -74
- package/lib/adapter.d.ts +39 -0
- package/lib/adapter.d.ts.map +1 -0
- package/{dist → lib}/adapter.js +20 -2
- package/lib/adapter.js.map +1 -0
- package/lib/app.d.ts +115 -0
- package/lib/app.d.ts.map +1 -0
- package/{dist → lib}/app.js +148 -78
- package/lib/app.js.map +1 -0
- package/lib/bot.d.ts +31 -0
- package/lib/bot.d.ts.map +1 -0
- package/lib/command.d.ts +32 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +46 -0
- package/lib/command.js.map +1 -0
- package/lib/component.d.ts +107 -0
- package/lib/component.d.ts.map +1 -0
- package/lib/component.js +273 -0
- package/lib/component.js.map +1 -0
- package/{dist → lib}/config.d.ts.map +1 -1
- package/{dist → lib}/config.js +6 -9
- package/lib/config.js.map +1 -0
- package/lib/cron.d.ts +81 -0
- package/lib/cron.d.ts.map +1 -0
- package/lib/cron.js +159 -0
- package/lib/cron.js.map +1 -0
- package/lib/errors.d.ts +165 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +306 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +17 -0
- package/lib/index.js.map +1 -0
- package/lib/message.d.ts +44 -0
- package/lib/message.d.ts.map +1 -0
- package/lib/message.js +11 -0
- package/lib/message.js.map +1 -0
- package/lib/plugin.d.ts +50 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +170 -0
- package/lib/plugin.js.map +1 -0
- package/lib/prompt.d.ts +116 -0
- package/lib/prompt.d.ts.map +1 -0
- package/lib/prompt.js +240 -0
- package/lib/prompt.js.map +1 -0
- package/lib/schema.d.ts +83 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +245 -0
- package/lib/schema.js.map +1 -0
- package/{dist → lib}/types-generator.d.ts.map +1 -1
- package/{dist → lib}/types-generator.js +6 -3
- package/lib/types-generator.js.map +1 -0
- package/lib/types.d.ts +119 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils.d.ts +52 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +338 -0
- package/lib/utils.js.map +1 -0
- package/package.json +15 -9
- package/src/adapter.ts +25 -9
- package/src/app.ts +363 -258
- package/src/bot.ts +29 -8
- package/src/command.ts +50 -0
- package/src/component.ts +318 -0
- package/src/config.ts +9 -12
- package/src/cron.ts +176 -0
- package/src/errors.ts +365 -0
- package/src/index.ts +16 -13
- package/src/message.ts +44 -0
- package/src/plugin.ts +148 -66
- package/src/prompt.ts +290 -0
- package/src/schema.ts +273 -0
- package/src/types-generator.ts +7 -3
- package/src/types.ts +77 -30
- package/src/utils.ts +312 -0
- package/tests/adapter.test.ts +36 -22
- package/tests/app.test.ts +30 -0
- package/tests/command.test.ts +545 -0
- package/tests/component.test.ts +656 -0
- package/tests/config.test.ts +1 -1
- package/tests/errors.test.ts +311 -0
- package/tests/message.test.ts +402 -0
- package/tests/plugin.test.ts +275 -143
- package/tests/utils.test.ts +80 -0
- package/tsconfig.json +3 -4
- package/dist/adapter.d.ts +0 -22
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js.map +0 -1
- package/dist/app.d.ts +0 -69
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js.map +0 -1
- package/dist/bot.d.ts +0 -9
- package/dist/bot.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -12
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -3
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -3
- package/dist/logger.js.map +0 -1
- package/dist/plugin.d.ts +0 -41
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -95
- package/dist/plugin.js.map +0 -1
- package/dist/types-generator.js.map +0 -1
- package/dist/types.d.ts +0 -69
- package/dist/types.d.ts.map +0 -1
- package/src/logger.ts +0 -3
- package/tests/logger.test.ts +0 -170
- package/tsconfig.tsbuildinfo +0 -1
- /package/{dist → lib}/bot.js +0 -0
- /package/{dist → lib}/bot.js.map +0 -0
- /package/{dist → lib}/config.d.ts +0 -0
- /package/{dist → lib}/types-generator.d.ts +0 -0
- /package/{dist → lib}/types.js +0 -0
- /package/{dist → lib}/types.js.map +0 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// 错误处理系统
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 基础错误类,所有自定义错误都应该继承此类
|
|
7
|
+
*/
|
|
8
|
+
export class ZhinError extends Error {
|
|
9
|
+
public readonly code: string
|
|
10
|
+
public readonly timestamp: Date
|
|
11
|
+
public readonly context?: Record<string, any>
|
|
12
|
+
|
|
13
|
+
constructor(message: string, code: string = 'ZHIN_ERROR', context?: Record<string, any>) {
|
|
14
|
+
super(message)
|
|
15
|
+
this.name = this.constructor.name
|
|
16
|
+
this.code = code
|
|
17
|
+
this.timestamp = new Date()
|
|
18
|
+
this.context = context
|
|
19
|
+
|
|
20
|
+
// 确保错误堆栈正确显示
|
|
21
|
+
if (Error.captureStackTrace) {
|
|
22
|
+
Error.captureStackTrace(this, this.constructor)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 转换为JSON格式
|
|
28
|
+
*/
|
|
29
|
+
toJSON() {
|
|
30
|
+
return {
|
|
31
|
+
name: this.name,
|
|
32
|
+
message: this.message,
|
|
33
|
+
code: this.code,
|
|
34
|
+
timestamp: this.timestamp.toISOString(),
|
|
35
|
+
context: this.context,
|
|
36
|
+
stack: this.stack
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 转换为用户友好的格式
|
|
42
|
+
*/
|
|
43
|
+
toUserString(): string {
|
|
44
|
+
return `[${this.code}] ${this.message}`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 配置相关错误
|
|
50
|
+
*/
|
|
51
|
+
export class ConfigError extends ZhinError {
|
|
52
|
+
constructor(message: string, context?: Record<string, any>) {
|
|
53
|
+
super(message, 'CONFIG_ERROR', context)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 插件相关错误
|
|
59
|
+
*/
|
|
60
|
+
export class PluginError extends ZhinError {
|
|
61
|
+
public readonly pluginName: string
|
|
62
|
+
|
|
63
|
+
constructor(message: string, pluginName: string, context?: Record<string, any>) {
|
|
64
|
+
super(message, 'PLUGIN_ERROR', { ...context, pluginName })
|
|
65
|
+
this.pluginName = pluginName
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 适配器相关错误
|
|
71
|
+
*/
|
|
72
|
+
export class AdapterError extends ZhinError {
|
|
73
|
+
public readonly adapterName: string
|
|
74
|
+
public readonly botName?: string
|
|
75
|
+
|
|
76
|
+
constructor(message: string, adapterName: string, botName?: string, context?: Record<string, any>) {
|
|
77
|
+
super(message, 'ADAPTER_ERROR', { ...context, adapterName, botName })
|
|
78
|
+
this.adapterName = adapterName
|
|
79
|
+
this.botName = botName
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 连接相关错误
|
|
85
|
+
*/
|
|
86
|
+
export class ConnectionError extends ZhinError {
|
|
87
|
+
public readonly retryable: boolean
|
|
88
|
+
|
|
89
|
+
constructor(message: string, retryable: boolean = true, context?: Record<string, any>) {
|
|
90
|
+
super(message, 'CONNECTION_ERROR', { ...context, retryable })
|
|
91
|
+
this.retryable = retryable
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 消息处理错误
|
|
97
|
+
*/
|
|
98
|
+
export class MessageError extends ZhinError {
|
|
99
|
+
public readonly messageId?: string
|
|
100
|
+
public readonly channelId?: string
|
|
101
|
+
|
|
102
|
+
constructor(message: string, messageId?: string, channelId?: string, context?: Record<string, any>) {
|
|
103
|
+
super(message, 'MESSAGE_ERROR', { ...context, messageId, channelId })
|
|
104
|
+
this.messageId = messageId
|
|
105
|
+
this.channelId = channelId
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 上下文相关错误
|
|
111
|
+
*/
|
|
112
|
+
export class ContextError extends ZhinError {
|
|
113
|
+
public readonly contextName: string
|
|
114
|
+
|
|
115
|
+
constructor(message: string, contextName: string, context?: Record<string, any>) {
|
|
116
|
+
super(message, 'CONTEXT_ERROR', { ...context, contextName })
|
|
117
|
+
this.contextName = contextName
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 验证错误
|
|
123
|
+
*/
|
|
124
|
+
export class ValidationError extends ZhinError {
|
|
125
|
+
public readonly field?: string
|
|
126
|
+
public readonly value?: any
|
|
127
|
+
|
|
128
|
+
constructor(message: string, field?: string, value?: any, context?: Record<string, any>) {
|
|
129
|
+
super(message, 'VALIDATION_ERROR', { ...context, field, value })
|
|
130
|
+
this.field = field
|
|
131
|
+
this.value = value
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 权限错误
|
|
137
|
+
*/
|
|
138
|
+
export class PermissionError extends ZhinError {
|
|
139
|
+
public readonly userId?: string
|
|
140
|
+
public readonly requiredPermission?: string
|
|
141
|
+
|
|
142
|
+
constructor(message: string, userId?: string, requiredPermission?: string, context?: Record<string, any>) {
|
|
143
|
+
super(message, 'PERMISSION_ERROR', { ...context, userId, requiredPermission })
|
|
144
|
+
this.userId = userId
|
|
145
|
+
this.requiredPermission = requiredPermission
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 超时错误
|
|
151
|
+
*/
|
|
152
|
+
export class TimeoutError extends ZhinError {
|
|
153
|
+
public readonly timeoutMs: number
|
|
154
|
+
|
|
155
|
+
constructor(message: string, timeoutMs: number, context?: Record<string, any>) {
|
|
156
|
+
super(message, 'TIMEOUT_ERROR', { ...context, timeoutMs })
|
|
157
|
+
this.timeoutMs = timeoutMs
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 错误处理器接口
|
|
163
|
+
*/
|
|
164
|
+
export interface ErrorHandler {
|
|
165
|
+
(error: Error, context?: Record<string, any>): void | Promise<void>
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 错误管理器
|
|
170
|
+
*/
|
|
171
|
+
export class ErrorManager {
|
|
172
|
+
private handlers: Map<string, ErrorHandler[]> = new Map()
|
|
173
|
+
private globalHandlers: ErrorHandler[] = []
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 注册错误处理器
|
|
177
|
+
*/
|
|
178
|
+
register(errorType: string, handler: ErrorHandler): void {
|
|
179
|
+
if (!this.handlers.has(errorType)) {
|
|
180
|
+
this.handlers.set(errorType, [])
|
|
181
|
+
}
|
|
182
|
+
this.handlers.get(errorType)!.push(handler)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 注册全局错误处理器
|
|
187
|
+
*/
|
|
188
|
+
registerGlobal(handler: ErrorHandler): void {
|
|
189
|
+
this.globalHandlers.push(handler)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 处理错误
|
|
194
|
+
*/
|
|
195
|
+
async handle(error: Error, context?: Record<string, any>): Promise<void> {
|
|
196
|
+
// 首先调用全局处理器
|
|
197
|
+
for (const handler of this.globalHandlers) {
|
|
198
|
+
try {
|
|
199
|
+
await handler(error, context)
|
|
200
|
+
} catch (handlerError) {
|
|
201
|
+
// console.error 已替换为注释
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 然后调用特定类型的处理器
|
|
206
|
+
const errorType = error.constructor.name
|
|
207
|
+
const handlers = this.handlers.get(errorType) || []
|
|
208
|
+
|
|
209
|
+
for (const handler of handlers) {
|
|
210
|
+
try {
|
|
211
|
+
await handler(error, context)
|
|
212
|
+
} catch (handlerError) {
|
|
213
|
+
// console.error 已替换为注释
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 移除错误处理器
|
|
220
|
+
*/
|
|
221
|
+
unregister(errorType: string, handler: ErrorHandler): boolean {
|
|
222
|
+
const handlers = this.handlers.get(errorType)
|
|
223
|
+
if (handlers) {
|
|
224
|
+
const index = handlers.indexOf(handler)
|
|
225
|
+
if (index !== -1) {
|
|
226
|
+
handlers.splice(index, 1)
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 清理所有处理器
|
|
235
|
+
*/
|
|
236
|
+
clear(): void {
|
|
237
|
+
this.handlers.clear()
|
|
238
|
+
this.globalHandlers.length = 0
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 错误重试工具
|
|
244
|
+
*/
|
|
245
|
+
export class RetryManager {
|
|
246
|
+
/**
|
|
247
|
+
* 执行重试逻辑
|
|
248
|
+
*/
|
|
249
|
+
static async retry<T>(
|
|
250
|
+
fn: () => Promise<T>,
|
|
251
|
+
options: {
|
|
252
|
+
maxRetries?: number
|
|
253
|
+
delay?: number
|
|
254
|
+
exponentialBackoff?: boolean
|
|
255
|
+
retryCondition?: (error: Error) => boolean
|
|
256
|
+
} = {}
|
|
257
|
+
): Promise<T> {
|
|
258
|
+
const {
|
|
259
|
+
maxRetries = 3,
|
|
260
|
+
delay = 1000,
|
|
261
|
+
exponentialBackoff = true,
|
|
262
|
+
retryCondition = () => true
|
|
263
|
+
} = options
|
|
264
|
+
|
|
265
|
+
let lastError: Error
|
|
266
|
+
|
|
267
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
268
|
+
try {
|
|
269
|
+
return await fn()
|
|
270
|
+
} catch (error) {
|
|
271
|
+
lastError = error as Error
|
|
272
|
+
|
|
273
|
+
// 如果是最后一次尝试或不满足重试条件,直接抛出错误
|
|
274
|
+
if (attempt === maxRetries || !retryCondition(lastError)) {
|
|
275
|
+
throw lastError
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 计算延迟时间
|
|
279
|
+
const currentDelay = exponentialBackoff
|
|
280
|
+
? delay * Math.pow(2, attempt)
|
|
281
|
+
: delay
|
|
282
|
+
|
|
283
|
+
// 等待后重试
|
|
284
|
+
await new Promise(resolve => setTimeout(resolve, currentDelay))
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
throw lastError!
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 断路器模式实现
|
|
294
|
+
*/
|
|
295
|
+
export class CircuitBreaker {
|
|
296
|
+
private failures: number = 0
|
|
297
|
+
private lastFailureTime: number = 0
|
|
298
|
+
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED'
|
|
299
|
+
|
|
300
|
+
constructor(
|
|
301
|
+
private failureThreshold: number = 5,
|
|
302
|
+
private timeoutMs: number = 60000,
|
|
303
|
+
private monitoringPeriodMs: number = 10000
|
|
304
|
+
) {}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 执行受保护的操作
|
|
308
|
+
*/
|
|
309
|
+
async execute<T>(fn: () => Promise<T>): Promise<T> {
|
|
310
|
+
if (this.state === 'OPEN') {
|
|
311
|
+
if (Date.now() - this.lastFailureTime > this.timeoutMs) {
|
|
312
|
+
this.state = 'HALF_OPEN'
|
|
313
|
+
} else {
|
|
314
|
+
throw new Error('Circuit breaker is OPEN')
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const result = await fn()
|
|
320
|
+
this.onSuccess()
|
|
321
|
+
return result
|
|
322
|
+
} catch (error) {
|
|
323
|
+
this.onFailure()
|
|
324
|
+
throw error
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private onSuccess(): void {
|
|
329
|
+
this.failures = 0
|
|
330
|
+
this.state = 'CLOSED'
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private onFailure(): void {
|
|
334
|
+
this.failures++
|
|
335
|
+
this.lastFailureTime = Date.now()
|
|
336
|
+
|
|
337
|
+
if (this.failures >= this.failureThreshold) {
|
|
338
|
+
this.state = 'OPEN'
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* 获取断路器状态
|
|
344
|
+
*/
|
|
345
|
+
getState(): 'CLOSED' | 'OPEN' | 'HALF_OPEN' {
|
|
346
|
+
return this.state
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* 重置断路器
|
|
351
|
+
*/
|
|
352
|
+
reset(): void {
|
|
353
|
+
this.failures = 0
|
|
354
|
+
this.lastFailureTime = 0
|
|
355
|
+
this.state = 'CLOSED'
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 默认错误管理器实例
|
|
360
|
+
export const errorManager = new ErrorManager()
|
|
361
|
+
|
|
362
|
+
// 默认错误处理器
|
|
363
|
+
errorManager.registerGlobal((error, context) => {
|
|
364
|
+
// Default error handler - logs to console
|
|
365
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
export { loadConfig, saveConfig, createDefaultConfig } from './config.js';
|
|
8
|
-
export * from './types.js';
|
|
9
|
-
export * from './config.js';
|
|
1
|
+
// Core exports
|
|
2
|
+
export * from './app.js'
|
|
3
|
+
export * from './bot.js'
|
|
4
|
+
export * from './plugin.js'
|
|
5
|
+
export * from './command.js'
|
|
6
|
+
export * from './component.js'
|
|
10
7
|
export * from './adapter.js'
|
|
11
|
-
export * from './
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export * from '
|
|
8
|
+
export * from './config.js'
|
|
9
|
+
export * from './message.js'
|
|
10
|
+
// Logger moved to @zhin.js/logger package
|
|
11
|
+
export * from './types.js'
|
|
12
|
+
export * from './utils.js'
|
|
13
|
+
export * from './errors.js' // 导出错误处理系统
|
|
14
|
+
export * from './cron.js'
|
|
15
|
+
export * from '@zhin.js/database'
|
|
16
|
+
|
|
17
|
+
export { Dependency } from '@zhin.js/hmr'
|
package/src/message.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {MaybePromise} from "@zhin.js/types";
|
|
2
|
+
import {MessageSegment, MessageSender, RegisteredAdapter, SendContent} from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 消息组件类型:用于自定义消息结构
|
|
6
|
+
*/
|
|
7
|
+
export type MessageComponent<T extends object>=(props:T&{children:SendContent})=>MaybePromise<SendContent>
|
|
8
|
+
/**
|
|
9
|
+
* 消息频道信息
|
|
10
|
+
*/
|
|
11
|
+
export interface MessageChannel{
|
|
12
|
+
id: string;
|
|
13
|
+
type: MessageType;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 消息类型枚举
|
|
17
|
+
*/
|
|
18
|
+
export type MessageType = 'group' | 'private' | 'channel'
|
|
19
|
+
/**
|
|
20
|
+
* 消息基础结构
|
|
21
|
+
*/
|
|
22
|
+
export interface MessageBase {
|
|
23
|
+
$id: string;
|
|
24
|
+
$adapter:string
|
|
25
|
+
$bot:string
|
|
26
|
+
$content: MessageSegment[];
|
|
27
|
+
$sender: MessageSender;
|
|
28
|
+
$reply(content:SendContent,quote?:boolean|string):Promise<void>
|
|
29
|
+
$channel: MessageChannel;
|
|
30
|
+
$timestamp: number;
|
|
31
|
+
$raw: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 完整消息类型,支持扩展
|
|
35
|
+
*/
|
|
36
|
+
export type Message<T extends object={}>=MessageBase&T;
|
|
37
|
+
export namespace Message{
|
|
38
|
+
/**
|
|
39
|
+
* 工具方法:合并自定义字段与基础消息结构
|
|
40
|
+
*/
|
|
41
|
+
export function from<T extends object>(input:T,format:MessageBase):Message<T>{
|
|
42
|
+
return Object.assign(input,format)
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/plugin.ts
CHANGED
|
@@ -1,52 +1,123 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
|
-
//
|
|
2
|
+
// 插件类型定义(定义插件中间件、生命周期、命令、组件等核心类型)
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
import {MaybePromise} from '@zhin.js/types'
|
|
7
|
-
import {
|
|
7
|
+
import {AdapterMessage, BeforeSendHandler, RegisteredAdapter, SendOptions} from "./types.js";
|
|
8
|
+
import {Message} from './message.js'
|
|
8
9
|
import {Dependency, Logger,} from "@zhin.js/hmr";
|
|
9
10
|
import {App} from "./app";
|
|
11
|
+
import {MessageCommand} from "./command.js";
|
|
12
|
+
import {Component} from "./component.js";
|
|
13
|
+
import { PluginError, MessageError, errorManager } from './errors.js';
|
|
14
|
+
import {remove} from "./utils.js";
|
|
15
|
+
import {Prompt} from "./prompt.js";
|
|
16
|
+
import { Schema } from '@zhin.js/database';
|
|
17
|
+
import { Cron} from './cron.js';
|
|
10
18
|
|
|
11
19
|
/** 消息中间件函数 */
|
|
12
|
-
export type MessageMiddleware = (message: Message
|
|
13
|
-
|
|
14
|
-
/** 事件监听器函数 */
|
|
15
|
-
export type EventListener<T = any> = (data: T) => void | Promise<void>;
|
|
16
|
-
|
|
17
|
-
/** 定时任务配置 */
|
|
18
|
-
export interface CronJob {
|
|
19
|
-
name: string;
|
|
20
|
-
schedule: string; // cron 表达式
|
|
21
|
-
handler: () => void | Promise<void>;
|
|
22
|
-
enabled?: boolean;
|
|
23
|
-
}
|
|
20
|
+
export type MessageMiddleware<P extends RegisteredAdapter=RegisteredAdapter> = (message: Message<AdapterMessage<P>>, next: () => Promise<void>) => MaybePromise<void>;
|
|
24
21
|
|
|
25
22
|
|
|
26
23
|
// ============================================================================
|
|
27
|
-
// Plugin
|
|
24
|
+
// Plugin 类(插件的生命周期、命令/中间件/组件/定时任务等管理)
|
|
28
25
|
// ============================================================================
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
|
-
* 插件类:继承自Dependency
|
|
28
|
+
* 插件类:继承自 Dependency,提供机器人特定功能与生命周期管理。
|
|
29
|
+
* 支持命令注册、中间件、组件、定时任务、模型等。
|
|
32
30
|
*/
|
|
33
31
|
export class Plugin extends Dependency<Plugin> {
|
|
34
|
-
middlewares: MessageMiddleware[] = [];
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
middlewares: MessageMiddleware<any>[] = [];
|
|
33
|
+
components: Map<string, Component<any, any, any>> = new Map();
|
|
34
|
+
schemas: Map<string,Schema<any>>=new Map();
|
|
35
|
+
commands:MessageCommand[]=[];
|
|
36
|
+
crons:Cron[]=[];
|
|
38
37
|
#logger?:Logger
|
|
38
|
+
/**
|
|
39
|
+
* 构造函数:初始化插件,注册消息事件、命令中间件、资源清理等
|
|
40
|
+
* @param parent 所属 App 实例
|
|
41
|
+
* @param name 插件名
|
|
42
|
+
* @param filePath 插件文件路径
|
|
43
|
+
*/
|
|
39
44
|
constructor(parent: Dependency<Plugin>, name: string, filePath: string) {
|
|
40
45
|
super(parent, name, filePath);
|
|
46
|
+
// 绑定消息事件,自动分发到命令和中间件
|
|
41
47
|
this.on('message.receive',this.#handleMessage.bind(this))
|
|
48
|
+
// 注册命令处理为默认中间件
|
|
49
|
+
this.addMiddleware(async (message,next)=>{
|
|
50
|
+
for(const command of this.commands){
|
|
51
|
+
const result=await command.handle(message);
|
|
52
|
+
if(result) message.$reply(result);
|
|
53
|
+
}
|
|
54
|
+
return next()
|
|
55
|
+
});
|
|
56
|
+
// 发送前渲染组件
|
|
57
|
+
this.beforeSend((options)=>Component.render(this.components,options))
|
|
58
|
+
// 资源清理:卸载时清空模型、定时任务等
|
|
59
|
+
this.on('dispose',()=>{
|
|
60
|
+
for(const name of this.schemas.keys()){
|
|
61
|
+
this.app.database?.models.delete(name);
|
|
62
|
+
}
|
|
63
|
+
this.schemas.clear();
|
|
64
|
+
for(const cron of this.crons){
|
|
65
|
+
cron.dispose();
|
|
66
|
+
}
|
|
67
|
+
this.crons.length = 0;
|
|
68
|
+
});
|
|
69
|
+
// 挂载时启动定时任务
|
|
70
|
+
this.on('mounted',()=>{
|
|
71
|
+
for(const cron of this.crons){
|
|
72
|
+
cron.run();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
42
75
|
}
|
|
43
|
-
#handleMessage(message:Message){
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
async #handleMessage(message: Message) {
|
|
77
|
+
try {
|
|
78
|
+
await this.#runMiddlewares(message, 0)
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const messageError = new MessageError(
|
|
81
|
+
`消息处理失败: ${(error as Error).message}`,
|
|
82
|
+
message.$id,
|
|
83
|
+
message.$channel.id,
|
|
84
|
+
{ pluginName: this.name, originalError: error }
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
await errorManager.handle(messageError)
|
|
88
|
+
|
|
89
|
+
// 可选:发送错误回复给用户
|
|
90
|
+
try {
|
|
91
|
+
await message.$reply('抱歉,处理您的消息时出现了错误。')
|
|
92
|
+
} catch (replyError) {
|
|
93
|
+
// 静默处理回复错误,避免错误循环
|
|
94
|
+
// console.error 已替换为注释
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
cron(cronExpression:string,callback:()=>void){
|
|
99
|
+
const cronJob = new Cron(cronExpression,callback);
|
|
100
|
+
this.crons.push(cronJob);
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
async #runMiddlewares(message: Message, index: number): Promise<void> {
|
|
104
|
+
if (index >= this.middlewares.length) return
|
|
105
|
+
|
|
106
|
+
const middleware = this.middlewares[index]
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await middleware(message, () => this.#runMiddlewares(message, index + 1))
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw new PluginError(
|
|
112
|
+
`中间件执行失败: ${(error as Error).message}`,
|
|
113
|
+
this.name,
|
|
114
|
+
{ middlewareIndex: index, originalError: error }
|
|
115
|
+
)
|
|
48
116
|
}
|
|
49
|
-
|
|
117
|
+
}
|
|
118
|
+
defineModel<S extends Record<string,any>>(name:string,schema:Schema<S>){
|
|
119
|
+
this.schemas.set(name,schema);
|
|
120
|
+
return this;
|
|
50
121
|
}
|
|
51
122
|
beforeSend(handler:BeforeSendHandler){
|
|
52
123
|
this.before('message.send',handler)
|
|
@@ -60,63 +131,74 @@ export class Plugin extends Dependency<Plugin> {
|
|
|
60
131
|
}
|
|
61
132
|
get logger(): Logger {
|
|
62
133
|
if(this.#logger) return this.#logger
|
|
63
|
-
const names = [
|
|
134
|
+
const names = [];
|
|
64
135
|
let temp=this as Dependency<Plugin>
|
|
65
136
|
while(temp.parent){
|
|
66
|
-
names.unshift(temp.
|
|
137
|
+
names.unshift(temp.name)
|
|
67
138
|
temp=temp.parent
|
|
68
139
|
}
|
|
69
|
-
return
|
|
140
|
+
return temp.getLogger(names.join('/'))
|
|
141
|
+
}
|
|
142
|
+
/** 添加组件 */
|
|
143
|
+
addComponent<T = {}, D = {}, P = Component.Props<T>>(component:Component<T,D,P>){
|
|
144
|
+
this.components.set(component.name,component);
|
|
70
145
|
}
|
|
71
|
-
|
|
72
146
|
/** 添加中间件 */
|
|
73
|
-
|
|
147
|
+
addCommand(command:MessageCommand){
|
|
148
|
+
this.commands.push(command);
|
|
149
|
+
this.dispatch('command.add',command);
|
|
150
|
+
}
|
|
151
|
+
/** 添加中间件 */
|
|
152
|
+
addMiddleware<T extends RegisteredAdapter>(middleware: MessageMiddleware<T>) {
|
|
74
153
|
this.middlewares.push(middleware);
|
|
75
154
|
this.dispatch('middleware.add',middleware)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
/** 添加事件监听器 */
|
|
79
|
-
addEventListener<T = any>(event: string, listener: EventListener<T>): void {
|
|
80
|
-
if (!this.eventListeners.has(event)) {
|
|
81
|
-
this.eventListeners.set(event, []);
|
|
155
|
+
return ()=>{
|
|
156
|
+
remove(this.middlewares,middleware)
|
|
82
157
|
}
|
|
83
|
-
this.eventListeners.get(event)!.push(listener);
|
|
84
|
-
this.dispatch('listener.add',event,listener)
|
|
85
158
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
addCronJob(job: CronJob): void {
|
|
89
|
-
this.cronJobs.set(job.name, job);
|
|
90
|
-
this.dispatch('cron-job.add',job)
|
|
159
|
+
prompt<P extends RegisteredAdapter>(message:Message<AdapterMessage<P>>){
|
|
160
|
+
return new Prompt<P>(this,message)
|
|
91
161
|
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
92
165
|
/** 发送消息 */
|
|
93
|
-
async sendMessage(options:SendOptions): Promise<void> {
|
|
94
|
-
|
|
166
|
+
async sendMessage(options: SendOptions): Promise<void> {
|
|
167
|
+
try {
|
|
168
|
+
await this.app.sendMessage(options);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const messageError = new MessageError(
|
|
171
|
+
`发送消息失败: ${(error as Error).message}`,
|
|
172
|
+
undefined,
|
|
173
|
+
(options as any).channel_id,
|
|
174
|
+
{ pluginName: this.name, sendOptions: options, originalError: error }
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
await errorManager.handle(messageError)
|
|
178
|
+
throw messageError
|
|
179
|
+
}
|
|
95
180
|
}
|
|
96
181
|
|
|
97
182
|
/** 销毁插件 */
|
|
98
183
|
dispose(): void {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.middlewares = []
|
|
104
|
-
|
|
105
|
-
// 移除所有事件监听器
|
|
106
|
-
for (const [event, listeners] of this.eventListeners) {
|
|
107
|
-
for (const listener of listeners) {
|
|
108
|
-
this.dispatch('listener.remove', event, listener)
|
|
184
|
+
try {
|
|
185
|
+
// 移除所有中间件
|
|
186
|
+
for (const middleware of this.middlewares) {
|
|
187
|
+
this.dispatch('middleware.remove', middleware)
|
|
109
188
|
}
|
|
189
|
+
this.middlewares = []
|
|
190
|
+
|
|
191
|
+
// 调用父类的dispose方法
|
|
192
|
+
super.dispose()
|
|
193
|
+
} catch (error) {
|
|
194
|
+
const pluginError = new PluginError(
|
|
195
|
+
`插件销毁失败: ${(error as Error).message}`,
|
|
196
|
+
this.name,
|
|
197
|
+
{ originalError: error }
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
errorManager.handle(pluginError).catch(console.error)
|
|
201
|
+
throw pluginError
|
|
110
202
|
}
|
|
111
|
-
this.eventListeners.clear()
|
|
112
|
-
|
|
113
|
-
// 移除所有定时任务
|
|
114
|
-
for (const [name, job] of this.cronJobs) {
|
|
115
|
-
this.dispatch('cron-job.remove', job)
|
|
116
|
-
}
|
|
117
|
-
this.cronJobs.clear()
|
|
118
|
-
|
|
119
|
-
// 调用父类的dispose方法
|
|
120
|
-
super.dispose()
|
|
121
203
|
}
|
|
122
204
|
}
|