@zhin.js/core 1.0.57 → 1.1.2

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.
Files changed (126) hide show
  1. package/lib/adapter.d.ts +1 -26
  2. package/lib/adapter.d.ts.map +1 -1
  3. package/lib/adapter.js +20 -117
  4. package/lib/adapter.js.map +1 -1
  5. package/lib/ai/index.d.ts +2 -0
  6. package/lib/ai/index.d.ts.map +1 -1
  7. package/lib/ai/index.js +1 -0
  8. package/lib/ai/index.js.map +1 -1
  9. package/lib/built/adapter-process.d.ts +0 -4
  10. package/lib/built/adapter-process.d.ts.map +1 -1
  11. package/lib/built/adapter-process.js +0 -95
  12. package/lib/built/adapter-process.js.map +1 -1
  13. package/lib/built/agent-preset.d.ts +2 -0
  14. package/lib/built/agent-preset.d.ts.map +1 -1
  15. package/lib/built/agent-preset.js +4 -0
  16. package/lib/built/agent-preset.js.map +1 -1
  17. package/lib/built/command.d.ts +4 -0
  18. package/lib/built/command.d.ts.map +1 -1
  19. package/lib/built/command.js +6 -0
  20. package/lib/built/command.js.map +1 -1
  21. package/lib/built/component.d.ts.map +1 -1
  22. package/lib/built/component.js +1 -0
  23. package/lib/built/component.js.map +1 -1
  24. package/lib/built/dispatcher.d.ts.map +1 -1
  25. package/lib/built/dispatcher.js +0 -13
  26. package/lib/built/dispatcher.js.map +1 -1
  27. package/lib/built/message-filter.d.ts +2 -0
  28. package/lib/built/message-filter.d.ts.map +1 -1
  29. package/lib/built/message-filter.js +5 -0
  30. package/lib/built/message-filter.js.map +1 -1
  31. package/lib/built/skill.d.ts +11 -0
  32. package/lib/built/skill.d.ts.map +1 -1
  33. package/lib/built/skill.js +14 -0
  34. package/lib/built/skill.js.map +1 -1
  35. package/lib/built/tool.d.ts +11 -44
  36. package/lib/built/tool.d.ts.map +1 -1
  37. package/lib/built/tool.js +14 -353
  38. package/lib/built/tool.js.map +1 -1
  39. package/lib/plugin.d.ts +1 -25
  40. package/lib/plugin.d.ts.map +1 -1
  41. package/lib/plugin.js +1 -77
  42. package/lib/plugin.js.map +1 -1
  43. package/lib/types.d.ts +0 -25
  44. package/lib/types.d.ts.map +1 -1
  45. package/package.json +10 -7
  46. package/CHANGELOG.md +0 -538
  47. package/REFACTORING_COMPLETE.md +0 -178
  48. package/REFACTORING_STATUS.md +0 -263
  49. package/src/adapter.ts +0 -275
  50. package/src/ai/index.ts +0 -52
  51. package/src/ai/providers/anthropic.ts +0 -379
  52. package/src/ai/providers/base.ts +0 -175
  53. package/src/ai/providers/index.ts +0 -13
  54. package/src/ai/providers/ollama.ts +0 -302
  55. package/src/ai/providers/openai.ts +0 -174
  56. package/src/ai/types.ts +0 -348
  57. package/src/bot.ts +0 -37
  58. package/src/built/adapter-process.ts +0 -177
  59. package/src/built/agent-preset.ts +0 -136
  60. package/src/built/ai-trigger.ts +0 -259
  61. package/src/built/command.ts +0 -108
  62. package/src/built/common-adapter-tools.ts +0 -242
  63. package/src/built/component.ts +0 -130
  64. package/src/built/config.ts +0 -335
  65. package/src/built/cron.ts +0 -156
  66. package/src/built/database.ts +0 -134
  67. package/src/built/dispatcher.ts +0 -496
  68. package/src/built/login-assist.ts +0 -131
  69. package/src/built/message-filter.ts +0 -390
  70. package/src/built/permission.ts +0 -151
  71. package/src/built/schema-feature.ts +0 -190
  72. package/src/built/skill.ts +0 -221
  73. package/src/built/tool.ts +0 -948
  74. package/src/command.ts +0 -87
  75. package/src/component.ts +0 -565
  76. package/src/cron.ts +0 -4
  77. package/src/errors.ts +0 -46
  78. package/src/feature.ts +0 -7
  79. package/src/index.ts +0 -53
  80. package/src/jsx-dev-runtime.ts +0 -2
  81. package/src/jsx-runtime.ts +0 -12
  82. package/src/jsx.ts +0 -135
  83. package/src/message.ts +0 -48
  84. package/src/models/system-log.ts +0 -20
  85. package/src/models/user.ts +0 -15
  86. package/src/notice.ts +0 -98
  87. package/src/plugin.ts +0 -896
  88. package/src/prompt.ts +0 -293
  89. package/src/request.ts +0 -95
  90. package/src/scheduler/index.ts +0 -19
  91. package/src/scheduler/scheduler.ts +0 -372
  92. package/src/scheduler/types.ts +0 -74
  93. package/src/tool-zod.ts +0 -115
  94. package/src/types-generator.ts +0 -78
  95. package/src/types.ts +0 -505
  96. package/src/utils.ts +0 -227
  97. package/tests/adapter.test.ts +0 -638
  98. package/tests/ai/ai-trigger.test.ts +0 -368
  99. package/tests/ai/providers.integration.test.ts +0 -227
  100. package/tests/ai/setup.ts +0 -308
  101. package/tests/ai/tool.test.ts +0 -800
  102. package/tests/bot.test.ts +0 -151
  103. package/tests/command.test.ts +0 -737
  104. package/tests/component-new.test.ts +0 -361
  105. package/tests/config.test.ts +0 -372
  106. package/tests/cron.test.ts +0 -82
  107. package/tests/dispatcher.test.ts +0 -293
  108. package/tests/errors.test.ts +0 -21
  109. package/tests/expression-evaluation.test.ts +0 -258
  110. package/tests/features-builtin.test.ts +0 -191
  111. package/tests/jsx-runtime.test.ts +0 -45
  112. package/tests/jsx.test.ts +0 -319
  113. package/tests/message-filter.test.ts +0 -566
  114. package/tests/message.test.ts +0 -402
  115. package/tests/notice.test.ts +0 -198
  116. package/tests/plugin.test.ts +0 -779
  117. package/tests/prompt.test.ts +0 -78
  118. package/tests/redos-protection.test.ts +0 -198
  119. package/tests/request.test.ts +0 -221
  120. package/tests/schema.test.ts +0 -248
  121. package/tests/skill-feature.test.ts +0 -179
  122. package/tests/test-utils.ts +0 -59
  123. package/tests/tool-feature.test.ts +0 -254
  124. package/tests/types.test.ts +0 -162
  125. package/tests/utils.test.ts +0 -135
  126. package/tsconfig.json +0 -24
@@ -1,737 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest'
2
- import { MessageCommand } from '../src/command'
3
- import { Message } from '../src/message'
4
- import { Plugin } from '../src/plugin'
5
- import { App } from '../src/app'
6
-
7
- // Mock segment-matcher
8
- vi.mock('segment-matcher', () => {
9
- class MatchResult {
10
- matched: any[] = []
11
- params: Record<string, any> = {}
12
- remaining: any[] = []
13
-
14
- addMatched(segment: any) {
15
- this.matched.push(segment)
16
- }
17
-
18
- addParam(name: string, value: any) {
19
- this.params[name] = value
20
- }
21
-
22
- addRemaining(segment: any) {
23
- this.remaining.push(segment)
24
- }
25
- }
26
-
27
- class SegmentMatcher {
28
- constructor(public pattern: string) {}
29
-
30
- match(content: any) {
31
- // 简单的mock实现
32
- if (Array.isArray(content) && content.length > 0) {
33
- const text = content[0]?.data?.text || ''
34
- if (text.includes(this.pattern)) {
35
- const result = new MatchResult()
36
- result.addMatched(content[0])
37
- const value = text.replace(this.pattern, '').trim()
38
- if (value) {
39
- result.addParam('text', value)
40
- }
41
- return result
42
- }
43
- }
44
- return null
45
- }
46
- }
47
-
48
- return { SegmentMatcher, MatchResult }
49
- })
50
-
51
- // Mock App with permissions
52
- const mockPermissionService = {
53
- check: vi.fn(async (perm: string, message: any) => {
54
- if (perm === 'adapter(discord)') {
55
- return message.$adapter === 'discord'
56
- }
57
- if (perm === 'adapter(telegram)') {
58
- return message.$adapter === 'telegram'
59
- }
60
- if (perm === 'adapter(email)') {
61
- return message.$adapter === 'email'
62
- }
63
- if (perm === 'adapter(test)') {
64
- return message.$adapter === 'test'
65
- }
66
- return true
67
- })
68
- }
69
-
70
- const mockApp = {
71
- contextIsReady: vi.fn((name: string) => name === 'permission'),
72
- inject: vi.fn((name: string) => {
73
- if (name === 'permission') return mockPermissionService
74
- return null
75
- })
76
- } as any
77
-
78
- // 为多个权限测试创建特殊的 mock app
79
- const multiPermitPermissionService = {
80
- check: vi.fn(async (perm: string, message: any) => {
81
- // 对于多个权限,只要有一个匹配就返回 true
82
- if (perm === 'adapter(discord)' && message.$adapter === 'discord') {
83
- return true
84
- }
85
- if (perm === 'adapter(telegram)' && message.$adapter === 'telegram') {
86
- return true
87
- }
88
- if (perm === 'adapter(email)' && message.$adapter === 'email') {
89
- return true
90
- }
91
- if (perm === 'adapter(test)' && message.$adapter === 'test') {
92
- return true
93
- }
94
- return false
95
- })
96
- }
97
-
98
- const multiPermitMockApp = {
99
- contextIsReady: vi.fn((name: string) => name === 'permission'),
100
- inject: vi.fn((name: string) => {
101
- if (name === 'permission') return multiPermitPermissionService
102
- return null
103
- })
104
- } as any
105
-
106
- describe('Command系统测试', () => {
107
- describe('MessageCommand基础功能测试', () => {
108
- it('应该正确创建MessageCommand实例', () => {
109
- const command = new MessageCommand('hello')
110
- expect(command).toBeInstanceOf(MessageCommand)
111
- // 注意:pattern 属性可能不是公开的,这里只测试实例创建
112
- })
113
-
114
- it('应该支持链式调用', () => {
115
- const command = new MessageCommand('test')
116
- .permit('adapter(discord)')
117
- .action(() => 'response')
118
-
119
- expect(command).toBeInstanceOf(MessageCommand)
120
- expect(typeof command.permit).toBe('function')
121
- expect(typeof command.action).toBe('function')
122
- })
123
- })
124
-
125
- describe('权限(permit)测试', () => {
126
- it('应该正确设置单个权限', async () => {
127
- const command = new MessageCommand('hello')
128
- .permit('adapter(discord)')
129
- .action(() => 'Hello from Discord!')
130
-
131
- // 匹配的适配器
132
- const discordMessage: Message = {
133
- $id: '1',
134
- $adapter: 'discord',
135
- $bot: 'discord-bot',
136
- $content: [{ type: 'text', data: { text: 'hello world' } }],
137
- $sender: { id: 'user1', name: 'User' },
138
- $reply: vi.fn(),
139
- $channel: { id: 'channel1', type: 'channel' },
140
- $timestamp: Date.now(),
141
- $raw: 'hello world'
142
- }
143
-
144
- // 不匹配的适配器
145
- const telegramMessage: Message = {
146
- $id: '2',
147
- $adapter: 'telegram',
148
- $bot: 'telegram-bot',
149
- $content: [{ type: 'text', data: { text: 'hello world' } }],
150
- $sender: { id: 'user2', name: 'User' },
151
- $reply: vi.fn(),
152
- $channel: { id: 'channel2', type: 'private' },
153
- $timestamp: Date.now(),
154
- $raw: 'hello world'
155
- }
156
-
157
- const discordResult = await command.handle(discordMessage, mockApp)
158
- const telegramResult = await command.handle(telegramMessage, mockApp)
159
-
160
- expect(discordResult).toBe('Hello from Discord!')
161
- expect(telegramResult).toBeUndefined()
162
- })
163
-
164
- it('应该正确设置多个权限', async () => {
165
- const command = new MessageCommand('hello')
166
- .permit('adapter(discord)')
167
- .action(() => 'Hello!')
168
-
169
- const discordMessage: Message = {
170
- $id: '1',
171
- $adapter: 'discord',
172
- $bot: 'discord-bot',
173
- $content: [{ type: 'text', data: { text: 'hello' } }],
174
- $sender: { id: 'user1', name: 'User' },
175
- $reply: vi.fn(),
176
- $channel: { id: 'channel1', type: 'channel' },
177
- $timestamp: Date.now(),
178
- $raw: 'hello'
179
- }
180
-
181
- const telegramMessage: Message = {
182
- $id: '2',
183
- $adapter: 'telegram',
184
- $bot: 'telegram-bot',
185
- $content: [{ type: 'text', data: { text: 'hello' } }],
186
- $sender: { id: 'user2', name: 'User' },
187
- $reply: vi.fn(),
188
- $channel: { id: 'channel2', type: 'private' },
189
- $timestamp: Date.now(),
190
- $raw: 'hello'
191
- }
192
-
193
- const emailMessage: Message = {
194
- $id: '3',
195
- $adapter: 'email',
196
- $bot: 'email-bot',
197
- $content: [{ type: 'text', data: { text: 'hello' } }],
198
- $sender: { id: 'user3', name: 'User' },
199
- $reply: vi.fn(),
200
- $channel: { id: 'channel3', type: 'private' },
201
- $timestamp: Date.now(),
202
- $raw: 'hello'
203
- }
204
-
205
- const discordResult = await command.handle(discordMessage, mockApp)
206
- const telegramResult = await command.handle(telegramMessage, mockApp)
207
- const emailResult = await command.handle(emailMessage, mockApp)
208
-
209
- expect(discordResult).toBe('Hello!')
210
- expect(telegramResult).toBeUndefined()
211
- expect(emailResult).toBeUndefined()
212
- })
213
- })
214
-
215
- describe('动作(action)测试', () => {
216
- it('应该正确执行单个动作', async () => {
217
- const actionSpy = vi.fn().mockReturnValue('Action executed!')
218
-
219
- const command = new MessageCommand('test')
220
- .action(actionSpy)
221
-
222
- const message: Message = {
223
- $id: '1',
224
- $adapter: 'test',
225
- $bot: 'test-bot',
226
- $content: [{ type: 'text', data: { text: 'test message' } }],
227
- $sender: { id: 'user1', name: 'User' },
228
- $reply: vi.fn(),
229
- $channel: { id: 'channel1', type: 'private' },
230
- $timestamp: Date.now(),
231
- $raw: 'test message'
232
- }
233
-
234
- const result = await command.handle(message, mockApp)
235
-
236
- expect(actionSpy).toHaveBeenCalledWith(message, expect.any(Object))
237
- expect(result).toBe('Action executed!')
238
- })
239
-
240
- it('应该正确执行多个动作', async () => {
241
- const action1 = vi.fn()
242
- const action2 = vi.fn().mockReturnValue('Second action result')
243
- const action3 = vi.fn().mockReturnValue('Third action result')
244
-
245
- const command = new MessageCommand('test')
246
- .action(action1)
247
- .action(action2)
248
- .action(action3)
249
-
250
- const message: Message = {
251
- $id: '1',
252
- $adapter: 'test',
253
- $bot: 'test-bot',
254
- $content: [{ type: 'text', data: { text: 'test' } }],
255
- $sender: { id: 'user1', name: 'User' },
256
- $reply: vi.fn(),
257
- $channel: { id: 'channel1', type: 'private' },
258
- $timestamp: Date.now(),
259
- $raw: 'test'
260
- }
261
-
262
- const result = await command.handle(message, mockApp)
263
-
264
- expect(action1).toHaveBeenCalled()
265
- expect(action2).toHaveBeenCalled()
266
- expect(action3).not.toHaveBeenCalled() // 第二个动作有返回值,所以第三个不会执行
267
- expect(result).toBe('Second action result')
268
- })
269
-
270
- it('应该正确处理异步动作', async () => {
271
- const asyncAction = vi.fn().mockResolvedValue('Async result')
272
-
273
- const command = new MessageCommand('async')
274
- .action(asyncAction)
275
-
276
- const message: Message = {
277
- $id: '1',
278
- $adapter: 'test',
279
- $bot: 'test-bot',
280
- $content: [{ type: 'text', data: { text: 'async test' } }],
281
- $sender: { id: 'user1', name: 'User' },
282
- $reply: vi.fn(),
283
- $channel: { id: 'channel1', type: 'private' },
284
- $timestamp: Date.now(),
285
- $raw: 'async test'
286
- }
287
-
288
- const result = await command.handle(message, mockApp)
289
-
290
- expect(asyncAction).toHaveBeenCalled()
291
- expect(result).toBe('Async result')
292
- })
293
-
294
- it('应该正确传递匹配结果给动作', async () => {
295
- const actionSpy = vi.fn().mockReturnValue('Got args!')
296
-
297
- const command = new MessageCommand('echo')
298
- .action(actionSpy)
299
-
300
- const message: Message = {
301
- $id: '1',
302
- $adapter: 'test',
303
- $bot: 'test-bot',
304
- $content: [{ type: 'text', data: { text: 'echo hello world' } }],
305
- $sender: { id: 'user1', name: 'User' },
306
- $reply: vi.fn(),
307
- $channel: { id: 'channel1', type: 'private' },
308
- $timestamp: Date.now(),
309
- $raw: 'echo hello world'
310
- }
311
-
312
- const result = await command.handle(message, mockApp)
313
-
314
- expect(actionSpy).toHaveBeenCalledWith(
315
- message,
316
- expect.objectContaining({
317
- matched: expect.any(Array),
318
- params: expect.any(Object)
319
- })
320
- )
321
- expect(result).toBe('Got args!')
322
- })
323
- })
324
-
325
- describe('消息处理(handle)测试', () => {
326
- it('应该在不匹配时返回undefined', async () => {
327
- const command = new MessageCommand('hello')
328
- .action(() => 'Hello!')
329
-
330
- const message: Message = {
331
- $id: '1',
332
- $adapter: 'test',
333
- $bot: 'test-bot',
334
- $content: [{ type: 'text', data: { text: 'goodbye' } }],
335
- $sender: { id: 'user1', name: 'User' },
336
- $reply: vi.fn(),
337
- $channel: { id: 'channel1', type: 'private' },
338
- $timestamp: Date.now(),
339
- $raw: 'goodbye'
340
- }
341
-
342
- const result = await command.handle(message, mockApp)
343
- expect(result).toBeUndefined()
344
- })
345
-
346
- it('应该正确处理空消息内容', async () => {
347
- const command = new MessageCommand('test')
348
- .action(() => 'Response')
349
-
350
- const message: Message = {
351
- $id: '1',
352
- $adapter: 'test',
353
- $bot: 'test-bot',
354
- $content: [],
355
- $sender: { id: 'user1', name: 'User' },
356
- $reply: vi.fn(),
357
- $channel: { id: 'channel1', type: 'private' },
358
- $timestamp: Date.now(),
359
- $raw: ''
360
- }
361
-
362
- const result = await command.handle(message, mockApp)
363
- expect(result).toBeUndefined()
364
- })
365
-
366
- it('应该正确处理非文本消息', async () => {
367
- const command = new MessageCommand('test')
368
- .action(() => 'Response')
369
-
370
- const message: Message = {
371
- $id: '1',
372
- $adapter: 'test',
373
- $bot: 'test-bot',
374
- $content: [
375
- { type: 'image', data: { url: 'https://example.com/image.png' } }
376
- ],
377
- $sender: { id: 'user1', name: 'User' },
378
- $reply: vi.fn(),
379
- $channel: { id: 'channel1', type: 'private' },
380
- $timestamp: Date.now(),
381
- $raw: '[图片]'
382
- }
383
-
384
- const result = await command.handle(message, mockApp)
385
- expect(result).toBeUndefined()
386
- })
387
- })
388
-
389
- describe('复合条件测试', () => {
390
- it('应该同时满足权限和匹配条件', async () => {
391
- const command = new MessageCommand('admin')
392
- .permit('adapter(discord)')
393
- .action(() => 'Admin command executed')
394
-
395
- // 正确的适配器和匹配的消息
396
- const validMessage: Message = {
397
- $id: '1',
398
- $adapter: 'discord',
399
- $bot: 'discord-bot',
400
- $content: [{ type: 'text', data: { text: 'admin panel' } }],
401
- $sender: { id: 'admin1', name: 'Admin' },
402
- $reply: vi.fn(),
403
- $channel: { id: 'admin-channel', type: 'private' },
404
- $timestamp: Date.now(),
405
- $raw: 'admin panel'
406
- }
407
-
408
- // 错误的适配器但匹配的消息
409
- const wrongAdapterMessage: Message = {
410
- $id: '2',
411
- $adapter: 'telegram',
412
- $bot: 'telegram-bot',
413
- $content: [{ type: 'text', data: { text: 'admin panel' } }],
414
- $sender: { id: 'admin2', name: 'Admin' },
415
- $reply: vi.fn(),
416
- $channel: { id: 'admin-channel', type: 'private' },
417
- $timestamp: Date.now(),
418
- $raw: 'admin panel'
419
- }
420
-
421
- // 正确的适配器但不匹配的消息
422
- const nonMatchingMessage: Message = {
423
- $id: '3',
424
- $adapter: 'discord',
425
- $bot: 'discord-bot',
426
- $content: [{ type: 'text', data: { text: 'hello world' } }],
427
- $sender: { id: 'user1', name: 'User' },
428
- $reply: vi.fn(),
429
- $channel: { id: 'general-channel', type: 'channel' },
430
- $timestamp: Date.now(),
431
- $raw: 'hello world'
432
- }
433
-
434
- const validResult = await command.handle(validMessage, mockApp)
435
- const wrongAdapterResult = await command.handle(wrongAdapterMessage, mockApp)
436
- const nonMatchingResult = await command.handle(nonMatchingMessage, mockApp)
437
-
438
- expect(validResult).toBe('Admin command executed')
439
- expect(wrongAdapterResult).toBeUndefined()
440
- expect(nonMatchingResult).toBeUndefined()
441
- })
442
- })
443
-
444
- describe('错误处理测试', () => {
445
- it('应该正确处理动作中的同步错误', async () => {
446
- const errorAction = vi.fn().mockImplementation(() => {
447
- throw new Error('Action failed')
448
- })
449
-
450
- const command = new MessageCommand('error')
451
- .action(errorAction)
452
-
453
- const message: Message = {
454
- $id: '1',
455
- $adapter: 'test',
456
- $bot: 'test-bot',
457
- $content: [{ type: 'text', data: { text: 'error test' } }],
458
- $sender: { id: 'user1', name: 'User' },
459
- $reply: vi.fn(),
460
- $channel: { id: 'channel1', type: 'private' },
461
- $timestamp: Date.now(),
462
- $raw: 'error test'
463
- }
464
-
465
- await expect(command.handle(message, mockApp)).rejects.toThrow('Action failed')
466
- })
467
-
468
- it('应该正确处理动作中的异步错误', async () => {
469
- const asyncErrorAction = vi.fn().mockRejectedValue(new Error('Async action failed'))
470
-
471
- const command = new MessageCommand('async-error')
472
- .action(asyncErrorAction)
473
-
474
- const message: Message = {
475
- $id: '1',
476
- $adapter: 'test',
477
- $bot: 'test-bot',
478
- $content: [{ type: 'text', data: { text: 'async-error test' } }],
479
- $sender: { id: 'user1', name: 'User' },
480
- $reply: vi.fn(),
481
- $channel: { id: 'channel1', type: 'private' },
482
- $timestamp: Date.now(),
483
- $raw: 'async-error test'
484
- }
485
-
486
- await expect(command.handle(message, mockApp)).rejects.toThrow('Async action failed')
487
- })
488
- })
489
-
490
- describe('类型系统测试', () => {
491
- it('应该支持泛型适配器类型约束', () => {
492
- // 这主要是TypeScript编译时检查
493
- const discordCommand = new MessageCommand('test')
494
- .permit('adapter(discord)')
495
- .action((message) => {
496
- // message 应该有正确的类型
497
- expect(message.$adapter).toBe('discord')
498
- return 'Discord response'
499
- })
500
-
501
- expect(discordCommand).toBeInstanceOf(MessageCommand)
502
- })
503
- })
504
-
505
- describe('性能测试', () => {
506
- it('应该高效处理大量消息检查', async () => {
507
- const action = vi.fn().mockReturnValue('Response')
508
- const command = new MessageCommand('perf')
509
- .permit('adapter(test)')
510
- .action(action)
511
-
512
- const messages: Message[] = Array.from({ length: 1000 }, (_, i) => ({
513
- $id: `msg-${i}`,
514
- $adapter: i % 2 === 0 ? 'test' : 'other',
515
- $bot: 'test-bot',
516
- $content: [{ type: 'text', data: { text: i % 3 === 0 ? 'perf test' : 'other' } }],
517
- $sender: { id: `user${i}`, name: `User ${i}` },
518
- $reply: vi.fn(),
519
- $channel: { id: `channel${i}`, type: 'private' },
520
- $timestamp: Date.now(),
521
- $raw: i % 3 === 0 ? 'perf test' : 'other'
522
- }))
523
-
524
- const startTime = Date.now()
525
- const results = await Promise.all(
526
- messages.map(message => command.handle(message, mockApp))
527
- )
528
- const endTime = Date.now()
529
-
530
- // 验证执行时间在合理范围内 (< 300ms for 1000 messages)
531
- expect(endTime - startTime).toBeLessThan(300)
532
-
533
- // 验证正确的消息被处理
534
- const validResults = results.filter(r => r !== undefined)
535
- expect(validResults.length).toBeGreaterThan(0)
536
- expect(action).toHaveBeenCalled()
537
- })
538
- })
539
-
540
- describe('复杂场景测试', () => {
541
- it('应该支持命令参数解析', async () => {
542
- const actionSpy = vi.fn((message, matchResult) => {
543
- return `参数: ${Object.values(matchResult.params).join(', ')}`
544
- })
545
-
546
- const command = new MessageCommand('say')
547
- .action(actionSpy)
548
-
549
- const message: Message = {
550
- $id: '1',
551
- $adapter: 'test',
552
- $bot: 'test-bot',
553
- $content: [{ type: 'text', data: { text: 'say hello world from bot' } }],
554
- $sender: { id: 'user1', name: 'User' },
555
- $reply: vi.fn(),
556
- $channel: { id: 'channel1', type: 'private' },
557
- $timestamp: Date.now(),
558
- $raw: 'say hello world from bot'
559
- }
560
-
561
- const result = await command.handle(message, mockApp)
562
-
563
- expect(actionSpy).toHaveBeenCalledWith(
564
- message,
565
- expect.objectContaining({
566
- params: expect.any(Object)
567
- })
568
- )
569
- expect(result).toBe('参数: hello world from bot')
570
- })
571
-
572
- it('应该支持条件链式处理', async () => {
573
- const command = new MessageCommand('multi')
574
- .permit('adapter(discord)')
575
- .action((message, result) => {
576
- if (message.$channel.type === 'private') {
577
- return '私人消息响应'
578
- }
579
- return undefined
580
- })
581
- .action(() => {
582
- return '群组消息响应'
583
- })
584
-
585
- const privateMessage: Message = {
586
- $id: '1',
587
- $adapter: 'discord',
588
- $bot: 'discord-bot',
589
- $content: [{ type: 'text', data: { text: 'multi test' } }],
590
- $sender: { id: 'user1', name: 'User' },
591
- $reply: vi.fn(),
592
- $channel: { id: 'dm-channel', type: 'private' },
593
- $timestamp: Date.now(),
594
- $raw: 'multi test'
595
- }
596
-
597
- const groupMessage: Message = {
598
- $id: '2',
599
- $adapter: 'discord',
600
- $bot: 'discord-bot',
601
- $content: [{ type: 'text', data: { text: 'multi test' } }],
602
- $sender: { id: 'user2', name: 'User' },
603
- $reply: vi.fn(),
604
- $channel: { id: 'group-channel', type: 'group' },
605
- $timestamp: Date.now(),
606
- $raw: 'multi test'
607
- }
608
-
609
- const privateResult = await command.handle(privateMessage, mockApp)
610
- const groupResult = await command.handle(groupMessage, mockApp)
611
-
612
- expect(privateResult).toBe('私人消息响应')
613
- expect(groupResult).toBe('群组消息响应')
614
- })
615
- })
616
-
617
- describe('帮助系统测试', () => {
618
- it('应该正确设置和获取描述信息', () => {
619
- const command = new MessageCommand('help')
620
- .desc('这是命令描述', '可以有多行描述')
621
-
622
- expect(command.helpInfo.desc).toEqual(['这是命令描述', '可以有多行描述'])
623
- })
624
-
625
- it('应该正确设置和获取用法信息', () => {
626
- const command = new MessageCommand('help')
627
- .usage('help', 'help <command>')
628
-
629
- expect(command.helpInfo.usage).toEqual(['help', 'help <command>'])
630
- })
631
-
632
- it('应该正确设置和获取示例信息', () => {
633
- const command = new MessageCommand('help')
634
- .examples('help', 'help echo', 'help admin')
635
-
636
- expect(command.helpInfo.examples).toEqual(['help', 'help echo', 'help admin'])
637
- })
638
-
639
- it('应该支持链式调用设置帮助信息', () => {
640
- const command = new MessageCommand('test')
641
- .desc('测试命令', '用于测试功能')
642
- .usage('test', 'test <arg>')
643
- .examples('test', 'test hello')
644
- .action(() => 'Test response')
645
-
646
- expect(command.helpInfo.pattern).toBe('test')
647
- expect(command.helpInfo.desc).toEqual(['测试命令', '用于测试功能'])
648
- expect(command.helpInfo.usage).toEqual(['test', 'test <arg>'])
649
- expect(command.helpInfo.examples).toEqual(['test', 'test hello'])
650
- })
651
-
652
- it('应该正确生成帮助文本', () => {
653
- const command = new MessageCommand('greet')
654
- .desc('打招呼命令')
655
- .usage('greet <name>')
656
- .examples('greet Alice')
657
-
658
- const help = command.help
659
-
660
- expect(help).toContain('greet')
661
- expect(help).toContain('打招呼命令')
662
- expect(help).toContain('greet <name>')
663
- expect(help).toContain('greet Alice')
664
- })
665
-
666
- it('应该处理没有帮助信息的情况', () => {
667
- const command = new MessageCommand('simple')
668
-
669
- expect(command.helpInfo.desc).toEqual([])
670
- expect(command.helpInfo.usage).toEqual([])
671
- expect(command.helpInfo.examples).toEqual([])
672
- expect(command.help).toBe('simple')
673
- })
674
-
675
- it('应该正确返回 helpInfo 对象结构', () => {
676
- const command = new MessageCommand('info')
677
- .desc('信息命令')
678
- .usage('info')
679
- .examples('info')
680
-
681
- const helpInfo = command.helpInfo
682
-
683
- expect(helpInfo).toHaveProperty('pattern')
684
- expect(helpInfo).toHaveProperty('desc')
685
- expect(helpInfo).toHaveProperty('usage')
686
- expect(helpInfo).toHaveProperty('examples')
687
- expect(typeof helpInfo.pattern).toBe('string')
688
- expect(Array.isArray(helpInfo.desc)).toBe(true)
689
- expect(Array.isArray(helpInfo.usage)).toBe(true)
690
- expect(Array.isArray(helpInfo.examples)).toBe(true)
691
- })
692
- })
693
-
694
- describe('权限系统测试', () => {
695
- it('应该正确处理权限检查失败', async () => {
696
- const command = new MessageCommand('admin')
697
- .permit('adapter(discord)')
698
- .action(() => 'Admin command')
699
-
700
- const message: Message = {
701
- $id: '1',
702
- $adapter: 'telegram', // 不匹配的适配器
703
- $bot: 'telegram-bot',
704
- $content: [{ type: 'text', data: { text: 'admin test' } }],
705
- $sender: { id: 'user1', name: 'User' },
706
- $reply: vi.fn(),
707
- $channel: { id: 'channel1', type: 'private' },
708
- $timestamp: Date.now(),
709
- $raw: 'admin test'
710
- }
711
-
712
- const result = await command.handle(message, mockApp)
713
- expect(result).toBeUndefined()
714
- })
715
-
716
- it('应该正确处理权限检查通过', async () => {
717
- const command = new MessageCommand('admin')
718
- .permit('adapter(discord)')
719
- .action(() => 'Admin command')
720
-
721
- const message: Message = {
722
- $id: '1',
723
- $adapter: 'discord', // 匹配的适配器
724
- $bot: 'discord-bot',
725
- $content: [{ type: 'text', data: { text: 'admin test' } }],
726
- $sender: { id: 'user1', name: 'User' },
727
- $reply: vi.fn(),
728
- $channel: { id: 'channel1', type: 'private' },
729
- $timestamp: Date.now(),
730
- $raw: 'admin test'
731
- }
732
-
733
- const result = await command.handle(message, mockApp)
734
- expect(result).toBe('Admin command')
735
- })
736
- })
737
- })