@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.
- package/lib/adapter.d.ts +1 -26
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +20 -117
- package/lib/adapter.js.map +1 -1
- package/lib/ai/index.d.ts +2 -0
- package/lib/ai/index.d.ts.map +1 -1
- package/lib/ai/index.js +1 -0
- package/lib/ai/index.js.map +1 -1
- package/lib/built/adapter-process.d.ts +0 -4
- package/lib/built/adapter-process.d.ts.map +1 -1
- package/lib/built/adapter-process.js +0 -95
- package/lib/built/adapter-process.js.map +1 -1
- package/lib/built/agent-preset.d.ts +2 -0
- package/lib/built/agent-preset.d.ts.map +1 -1
- package/lib/built/agent-preset.js +4 -0
- package/lib/built/agent-preset.js.map +1 -1
- package/lib/built/command.d.ts +4 -0
- package/lib/built/command.d.ts.map +1 -1
- package/lib/built/command.js +6 -0
- package/lib/built/command.js.map +1 -1
- package/lib/built/component.d.ts.map +1 -1
- package/lib/built/component.js +1 -0
- package/lib/built/component.js.map +1 -1
- package/lib/built/dispatcher.d.ts.map +1 -1
- package/lib/built/dispatcher.js +0 -13
- package/lib/built/dispatcher.js.map +1 -1
- package/lib/built/message-filter.d.ts +2 -0
- package/lib/built/message-filter.d.ts.map +1 -1
- package/lib/built/message-filter.js +5 -0
- package/lib/built/message-filter.js.map +1 -1
- package/lib/built/skill.d.ts +11 -0
- package/lib/built/skill.d.ts.map +1 -1
- package/lib/built/skill.js +14 -0
- package/lib/built/skill.js.map +1 -1
- package/lib/built/tool.d.ts +11 -44
- package/lib/built/tool.d.ts.map +1 -1
- package/lib/built/tool.js +14 -353
- package/lib/built/tool.js.map +1 -1
- package/lib/plugin.d.ts +1 -25
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +1 -77
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +0 -25
- package/lib/types.d.ts.map +1 -1
- package/package.json +10 -7
- package/CHANGELOG.md +0 -538
- package/REFACTORING_COMPLETE.md +0 -178
- package/REFACTORING_STATUS.md +0 -263
- package/src/adapter.ts +0 -275
- package/src/ai/index.ts +0 -52
- package/src/ai/providers/anthropic.ts +0 -379
- package/src/ai/providers/base.ts +0 -175
- package/src/ai/providers/index.ts +0 -13
- package/src/ai/providers/ollama.ts +0 -302
- package/src/ai/providers/openai.ts +0 -174
- package/src/ai/types.ts +0 -348
- package/src/bot.ts +0 -37
- package/src/built/adapter-process.ts +0 -177
- package/src/built/agent-preset.ts +0 -136
- package/src/built/ai-trigger.ts +0 -259
- package/src/built/command.ts +0 -108
- package/src/built/common-adapter-tools.ts +0 -242
- package/src/built/component.ts +0 -130
- package/src/built/config.ts +0 -335
- package/src/built/cron.ts +0 -156
- package/src/built/database.ts +0 -134
- package/src/built/dispatcher.ts +0 -496
- package/src/built/login-assist.ts +0 -131
- package/src/built/message-filter.ts +0 -390
- package/src/built/permission.ts +0 -151
- package/src/built/schema-feature.ts +0 -190
- package/src/built/skill.ts +0 -221
- package/src/built/tool.ts +0 -948
- package/src/command.ts +0 -87
- package/src/component.ts +0 -565
- package/src/cron.ts +0 -4
- package/src/errors.ts +0 -46
- package/src/feature.ts +0 -7
- package/src/index.ts +0 -53
- package/src/jsx-dev-runtime.ts +0 -2
- package/src/jsx-runtime.ts +0 -12
- package/src/jsx.ts +0 -135
- package/src/message.ts +0 -48
- package/src/models/system-log.ts +0 -20
- package/src/models/user.ts +0 -15
- package/src/notice.ts +0 -98
- package/src/plugin.ts +0 -896
- package/src/prompt.ts +0 -293
- package/src/request.ts +0 -95
- package/src/scheduler/index.ts +0 -19
- package/src/scheduler/scheduler.ts +0 -372
- package/src/scheduler/types.ts +0 -74
- package/src/tool-zod.ts +0 -115
- package/src/types-generator.ts +0 -78
- package/src/types.ts +0 -505
- package/src/utils.ts +0 -227
- package/tests/adapter.test.ts +0 -638
- package/tests/ai/ai-trigger.test.ts +0 -368
- package/tests/ai/providers.integration.test.ts +0 -227
- package/tests/ai/setup.ts +0 -308
- package/tests/ai/tool.test.ts +0 -800
- package/tests/bot.test.ts +0 -151
- package/tests/command.test.ts +0 -737
- package/tests/component-new.test.ts +0 -361
- package/tests/config.test.ts +0 -372
- package/tests/cron.test.ts +0 -82
- package/tests/dispatcher.test.ts +0 -293
- package/tests/errors.test.ts +0 -21
- package/tests/expression-evaluation.test.ts +0 -258
- package/tests/features-builtin.test.ts +0 -191
- package/tests/jsx-runtime.test.ts +0 -45
- package/tests/jsx.test.ts +0 -319
- package/tests/message-filter.test.ts +0 -566
- package/tests/message.test.ts +0 -402
- package/tests/notice.test.ts +0 -198
- package/tests/plugin.test.ts +0 -779
- package/tests/prompt.test.ts +0 -78
- package/tests/redos-protection.test.ts +0 -198
- package/tests/request.test.ts +0 -221
- package/tests/schema.test.ts +0 -248
- package/tests/skill-feature.test.ts +0 -179
- package/tests/test-utils.ts +0 -59
- package/tests/tool-feature.test.ts +0 -254
- package/tests/types.test.ts +0 -162
- package/tests/utils.test.ts +0 -135
- package/tsconfig.json +0 -24
package/tests/adapter.test.ts
DELETED
|
@@ -1,638 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
-
import { Adapter } from '../src/adapter'
|
|
3
|
-
import { Bot } from '../src/bot'
|
|
4
|
-
import { Plugin } from '../src/plugin'
|
|
5
|
-
import { Message, MessageBase } from '../src/message'
|
|
6
|
-
import { EventEmitter } from 'events'
|
|
7
|
-
|
|
8
|
-
// Mock Bot 实现用于测试
|
|
9
|
-
class MockBot implements Bot<any, any> {
|
|
10
|
-
$id: string
|
|
11
|
-
$config: any
|
|
12
|
-
$connected: boolean = false
|
|
13
|
-
adapter: Adapter
|
|
14
|
-
|
|
15
|
-
constructor(adapter: Adapter, config: any) {
|
|
16
|
-
this.adapter = adapter
|
|
17
|
-
this.$config = config
|
|
18
|
-
this.$id = config.id || 'mock-bot'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
$formatMessage(event: any): Message<any> {
|
|
22
|
-
const base: MessageBase = {
|
|
23
|
-
$id: event.id || 'mock-id',
|
|
24
|
-
$adapter: 'test' as any,
|
|
25
|
-
$bot: this.$id,
|
|
26
|
-
$content: [],
|
|
27
|
-
$sender: { id: 'mock-sender', name: 'Mock Sender' },
|
|
28
|
-
$channel: { id: 'mock-channel', type: 'private' },
|
|
29
|
-
$timestamp: Date.now(),
|
|
30
|
-
$raw: event.raw || event,
|
|
31
|
-
$reply: async (content: any, quote?: boolean | string) => {
|
|
32
|
-
const elements = Array.isArray(content) ? content : [content]
|
|
33
|
-
const finalContent: any[] = []
|
|
34
|
-
|
|
35
|
-
if (quote) {
|
|
36
|
-
finalContent.push({
|
|
37
|
-
type: 'reply',
|
|
38
|
-
data: { id: typeof quote === 'boolean' ? base.$id : quote }
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
finalContent.push(...elements.map((el: any) =>
|
|
43
|
-
typeof el === 'string' ? { type: 'text', data: { text: el } } : el
|
|
44
|
-
))
|
|
45
|
-
|
|
46
|
-
return await this.adapter.sendMessage({
|
|
47
|
-
...base.$channel,
|
|
48
|
-
context: 'test',
|
|
49
|
-
bot: this.$id,
|
|
50
|
-
content: finalContent,
|
|
51
|
-
})
|
|
52
|
-
},
|
|
53
|
-
$recall: async () => {}
|
|
54
|
-
}
|
|
55
|
-
return Message.from(event, base)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async $connect(): Promise<void> {
|
|
59
|
-
this.$connected = true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async $disconnect(): Promise<void> {
|
|
63
|
-
this.$connected = false
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async $sendMessage(options: any): Promise<string> {
|
|
67
|
-
return 'mock-message-id'
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async $recallMessage(id: string): Promise<void> {
|
|
71
|
-
// Mock 撤回消息
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Mock Adapter 类用于测试
|
|
76
|
-
class MockAdapter extends Adapter<MockBot> {
|
|
77
|
-
createBot(config: any): MockBot {
|
|
78
|
-
return new MockBot(this, config)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
describe('Adapter Core Functionality', () => {
|
|
83
|
-
let plugin: Plugin
|
|
84
|
-
let adapter: MockAdapter
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
plugin = new Plugin('/test/plugin.ts')
|
|
88
|
-
adapter = new MockAdapter(plugin, 'test', [])
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
describe('Adapter Constructor', () => {
|
|
92
|
-
it('should create adapter with plugin, name and config', () => {
|
|
93
|
-
const config = [{ id: 'bot1' }]
|
|
94
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
95
|
-
|
|
96
|
-
expect(adapter.plugin).toBe(plugin)
|
|
97
|
-
expect(adapter.name).toBe('test')
|
|
98
|
-
expect(adapter.config).toBe(config)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('should initialize with empty bots map', () => {
|
|
102
|
-
expect(adapter.bots).toBeInstanceOf(Map)
|
|
103
|
-
expect(adapter.bots.size).toBe(0)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should inherit from EventEmitter', () => {
|
|
107
|
-
expect(adapter).toBeInstanceOf(EventEmitter)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('should route message.receive via emit without default listener', () => {
|
|
111
|
-
expect(adapter.listenerCount('message.receive')).toBe(0)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('should register call.recallMessage listener', () => {
|
|
115
|
-
const listeners = adapter.listeners('call.recallMessage')
|
|
116
|
-
expect(listeners.length).toBeGreaterThan(0)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('should have sendMessage method', () => {
|
|
120
|
-
expect(typeof adapter.sendMessage).toBe('function')
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('Adapter Logger', () => {
|
|
125
|
-
it('should get logger from plugin', () => {
|
|
126
|
-
expect(adapter.logger).toBeDefined()
|
|
127
|
-
expect(adapter.logger).toBe(plugin.logger)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should throw error if plugin is not set', () => {
|
|
131
|
-
const adapter = new MockAdapter(plugin, 'test', [])
|
|
132
|
-
adapter.plugin = null as any
|
|
133
|
-
|
|
134
|
-
expect(() => adapter.logger).toThrow('Adapter is not associated with any plugin')
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
describe('Adapter Binding', () => {
|
|
139
|
-
it('should bind to a plugin', () => {
|
|
140
|
-
const newPlugin = new Plugin('/test/new-plugin.ts')
|
|
141
|
-
adapter.binding(newPlugin)
|
|
142
|
-
|
|
143
|
-
expect(adapter.plugin).toBe(newPlugin)
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
describe('Adapter Start', () => {
|
|
148
|
-
it('should start without config', async () => {
|
|
149
|
-
await adapter.start()
|
|
150
|
-
expect(plugin.root.adapters).toContain('test')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('should create and connect bots from config', async () => {
|
|
154
|
-
const config = [
|
|
155
|
-
{ id: 'bot1' },
|
|
156
|
-
{ id: 'bot2' }
|
|
157
|
-
]
|
|
158
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
159
|
-
|
|
160
|
-
await adapter.start()
|
|
161
|
-
|
|
162
|
-
expect(adapter.bots.size).toBe(2)
|
|
163
|
-
expect(adapter.bots.has('bot1')).toBe(true)
|
|
164
|
-
expect(adapter.bots.has('bot2')).toBe(true)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('should add adapter name to plugin adapters', async () => {
|
|
168
|
-
await adapter.start()
|
|
169
|
-
expect(plugin.root.adapters).toContain('test')
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('should handle empty config array', async () => {
|
|
173
|
-
const adapter = new MockAdapter(plugin, 'test', [])
|
|
174
|
-
await adapter.start()
|
|
175
|
-
expect(adapter.bots.size).toBe(0)
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
describe('Adapter Stop', () => {
|
|
180
|
-
it('should disconnect all bots', async () => {
|
|
181
|
-
const config = [{ id: 'bot1' }, { id: 'bot2' }]
|
|
182
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
183
|
-
|
|
184
|
-
await adapter.start()
|
|
185
|
-
expect(adapter.bots.size).toBe(2)
|
|
186
|
-
|
|
187
|
-
await adapter.stop()
|
|
188
|
-
expect(adapter.bots.size).toBe(0)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('should remove adapter from plugin adapters', async () => {
|
|
192
|
-
await adapter.start()
|
|
193
|
-
expect(plugin.root.adapters).toContain('test')
|
|
194
|
-
|
|
195
|
-
await adapter.stop()
|
|
196
|
-
expect(plugin.root.adapters).not.toContain('test')
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
it('should remove all event listeners', async () => {
|
|
200
|
-
await adapter.start()
|
|
201
|
-
const noop = () => {}
|
|
202
|
-
adapter.on('message.receive', noop)
|
|
203
|
-
const beforeCount = adapter.listenerCount('message.receive')
|
|
204
|
-
expect(beforeCount).toBe(1)
|
|
205
|
-
|
|
206
|
-
await adapter.stop()
|
|
207
|
-
const afterCount = adapter.listenerCount('message.receive')
|
|
208
|
-
|
|
209
|
-
expect(afterCount).toBe(0)
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('should handle bot disconnect errors gracefully', async () => {
|
|
213
|
-
const config = [{ id: 'bot1' }, { id: 'bot2' }]
|
|
214
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
215
|
-
|
|
216
|
-
await adapter.start()
|
|
217
|
-
|
|
218
|
-
// Mock first bot disconnect to throw error
|
|
219
|
-
const bot1 = adapter.bots.get('bot1')!
|
|
220
|
-
bot1.$disconnect = vi.fn().mockRejectedValue(new Error('Disconnect failed'))
|
|
221
|
-
|
|
222
|
-
// Mock logger to spy on error logging
|
|
223
|
-
const loggerSpy = vi.spyOn(adapter.logger, 'error')
|
|
224
|
-
|
|
225
|
-
// The adapter should continue cleanup despite errors
|
|
226
|
-
// Note: Current implementation throws, but this test documents the desired behavior
|
|
227
|
-
// where adapter.stop() should handle errors gracefully and continue cleanup
|
|
228
|
-
await expect(adapter.stop()).rejects.toThrow('Disconnect failed')
|
|
229
|
-
|
|
230
|
-
// Even though it throws, we document that graceful handling would be:
|
|
231
|
-
// - Log the error
|
|
232
|
-
// - Continue disconnecting other bots
|
|
233
|
-
// - Complete cleanup (clear bots, remove from adapters list, remove listeners)
|
|
234
|
-
})
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
describe('Adapter Events', () => {
|
|
238
|
-
describe('call.recallMessage', () => {
|
|
239
|
-
it('should recall message from bot', async () => {
|
|
240
|
-
const config = [{ id: 'bot1' }]
|
|
241
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
242
|
-
await adapter.start()
|
|
243
|
-
|
|
244
|
-
const bot = adapter.bots.get('bot1')!
|
|
245
|
-
const recallSpy = vi.spyOn(bot, '$recallMessage')
|
|
246
|
-
|
|
247
|
-
await adapter.emit('call.recallMessage', 'bot1', 'message-id')
|
|
248
|
-
|
|
249
|
-
expect(recallSpy).toHaveBeenCalledWith('message-id')
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
it('should require valid bot id', () => {
|
|
253
|
-
// 验证 adapter 不包含不存在的 bot
|
|
254
|
-
expect(adapter.bots.has('non-existent-bot')).toBe(false)
|
|
255
|
-
})
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
describe('sendMessage', () => {
|
|
259
|
-
it('should send message through bot', async () => {
|
|
260
|
-
const config = [{ id: 'bot1' }]
|
|
261
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
262
|
-
await adapter.start()
|
|
263
|
-
|
|
264
|
-
const bot = adapter.bots.get('bot1')!
|
|
265
|
-
const sendSpy = vi.spyOn(bot, '$sendMessage')
|
|
266
|
-
|
|
267
|
-
const options = {
|
|
268
|
-
context: 'test',
|
|
269
|
-
bot: 'bot1',
|
|
270
|
-
content: 'Hello',
|
|
271
|
-
id: 'channel-id',
|
|
272
|
-
type: 'text' as const
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const messageId = await adapter.sendMessage(options)
|
|
276
|
-
|
|
277
|
-
expect(sendSpy).toHaveBeenCalledWith(options)
|
|
278
|
-
expect(messageId).toBe('mock-message-id')
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
it('should throw error if bot not found', async () => {
|
|
282
|
-
const options = {
|
|
283
|
-
context: 'test',
|
|
284
|
-
bot: 'non-existent-bot',
|
|
285
|
-
content: 'Hello',
|
|
286
|
-
id: 'channel-id',
|
|
287
|
-
type: 'text' as const
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
await expect(adapter.sendMessage(options)).rejects.toThrow('Bot non-existent-bot not found')
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
it('should call before.sendMessage handlers', async () => {
|
|
294
|
-
const config = [{ id: 'bot1' }]
|
|
295
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
296
|
-
await adapter.start()
|
|
297
|
-
|
|
298
|
-
let handlerCalled = false
|
|
299
|
-
let modifiedContent = false
|
|
300
|
-
|
|
301
|
-
plugin.root.on('before.sendMessage', (options) => {
|
|
302
|
-
handlerCalled = true
|
|
303
|
-
// 修改消息内容
|
|
304
|
-
return {
|
|
305
|
-
...options,
|
|
306
|
-
content: 'Modified: ' + options.content
|
|
307
|
-
}
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
const bot = adapter.bots.get('bot1')!
|
|
311
|
-
const sendSpy = vi.spyOn(bot, '$sendMessage')
|
|
312
|
-
|
|
313
|
-
const options = {
|
|
314
|
-
context: 'test',
|
|
315
|
-
bot: 'bot1',
|
|
316
|
-
content: 'Hello',
|
|
317
|
-
id: 'channel-id',
|
|
318
|
-
type: 'text' as const
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
await adapter.sendMessage(options)
|
|
322
|
-
|
|
323
|
-
expect(handlerCalled).toBe(true)
|
|
324
|
-
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
325
|
-
content: 'Modified: Hello'
|
|
326
|
-
}))
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
it('should handle multiple before.sendMessage handlers', async () => {
|
|
330
|
-
const config = [{ id: 'bot1' }]
|
|
331
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
332
|
-
await adapter.start()
|
|
333
|
-
|
|
334
|
-
const handlers: string[] = []
|
|
335
|
-
|
|
336
|
-
plugin.root.on('before.sendMessage', (options) => {
|
|
337
|
-
handlers.push('handler1')
|
|
338
|
-
return options
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
plugin.root.on('before.sendMessage', (options) => {
|
|
342
|
-
handlers.push('handler2')
|
|
343
|
-
return options
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
const options = {
|
|
347
|
-
context: 'test',
|
|
348
|
-
bot: 'bot1',
|
|
349
|
-
content: 'Hello',
|
|
350
|
-
id: 'channel-id',
|
|
351
|
-
type: 'text' as const
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
await adapter.sendMessage(options)
|
|
355
|
-
|
|
356
|
-
expect(handlers).toEqual(['handler1', 'handler2'])
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
it('should log message sending', async () => {
|
|
360
|
-
const config = [{ id: 'bot1' }]
|
|
361
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
362
|
-
await adapter.start()
|
|
363
|
-
|
|
364
|
-
const logSpy = vi.spyOn(adapter.logger, 'info')
|
|
365
|
-
|
|
366
|
-
const options = {
|
|
367
|
-
context: 'test',
|
|
368
|
-
bot: 'bot1',
|
|
369
|
-
content: [{ type: 'text', data: { text: 'Hello' } }],
|
|
370
|
-
id: 'channel-id',
|
|
371
|
-
type: 'private' as const
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
await adapter.sendMessage(options)
|
|
375
|
-
|
|
376
|
-
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('bot1 send private(channel-id):Hello'))
|
|
377
|
-
})
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
describe('message.receive', () => {
|
|
381
|
-
it('should still dispatch plugin message.receive when dispatcher is missing (no middleware fallback)', async () => {
|
|
382
|
-
const config = [{ id: 'bot1' }]
|
|
383
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
384
|
-
await adapter.start()
|
|
385
|
-
|
|
386
|
-
let middlewareCalled = false
|
|
387
|
-
plugin.addMiddleware(async (_message, next) => {
|
|
388
|
-
middlewareCalled = true
|
|
389
|
-
await next()
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
let lifecycleCalled = false
|
|
393
|
-
plugin.on('message.receive', () => {
|
|
394
|
-
lifecycleCalled = true
|
|
395
|
-
})
|
|
396
|
-
|
|
397
|
-
const message = {
|
|
398
|
-
$bot: 'bot1',
|
|
399
|
-
$adapter: 'test',
|
|
400
|
-
$channel: { id: 'channel-id', type: 'text' },
|
|
401
|
-
$content: 'Hello',
|
|
402
|
-
} as any
|
|
403
|
-
|
|
404
|
-
adapter.emit('message.receive', message)
|
|
405
|
-
await new Promise((r) => setTimeout(r, 20))
|
|
406
|
-
expect(middlewareCalled).toBe(false)
|
|
407
|
-
expect(lifecycleCalled).toBe(true)
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
it('should await MessageDispatcher then plugin lifecycle', async () => {
|
|
411
|
-
const config = [{ id: 'bot1' }]
|
|
412
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
413
|
-
await adapter.start()
|
|
414
|
-
|
|
415
|
-
const order: string[] = []
|
|
416
|
-
plugin.$contexts.set('dispatcher', {
|
|
417
|
-
name: 'dispatcher',
|
|
418
|
-
description: 'mock dispatcher',
|
|
419
|
-
value: {
|
|
420
|
-
dispatch: async (_msg: any) => {
|
|
421
|
-
order.push('dispatcher')
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
} as any)
|
|
425
|
-
|
|
426
|
-
plugin.on('message.receive', () => {
|
|
427
|
-
order.push('lifecycle')
|
|
428
|
-
})
|
|
429
|
-
|
|
430
|
-
const message = {
|
|
431
|
-
$bot: 'bot1',
|
|
432
|
-
$adapter: 'test',
|
|
433
|
-
$channel: { id: 'channel-id', type: 'text' },
|
|
434
|
-
$content: 'Hello',
|
|
435
|
-
} as any
|
|
436
|
-
|
|
437
|
-
adapter.emit('message.receive', message)
|
|
438
|
-
await new Promise((r) => setTimeout(r, 20))
|
|
439
|
-
expect(order).toEqual(['dispatcher', 'lifecycle'])
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
it('should call adapter.on observers after plugin lifecycle', async () => {
|
|
443
|
-
const config = [{ id: 'bot1' }]
|
|
444
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
445
|
-
await adapter.start()
|
|
446
|
-
|
|
447
|
-
plugin.$contexts.set('dispatcher', {
|
|
448
|
-
name: 'dispatcher',
|
|
449
|
-
description: 'mock dispatcher',
|
|
450
|
-
value: { dispatch: async () => {} },
|
|
451
|
-
} as any)
|
|
452
|
-
|
|
453
|
-
const order: string[] = []
|
|
454
|
-
plugin.on('message.receive', () => order.push('lifecycle'))
|
|
455
|
-
adapter.on('message.receive', () => order.push('adapterObserver'))
|
|
456
|
-
|
|
457
|
-
const message = {
|
|
458
|
-
$bot: 'bot1',
|
|
459
|
-
$adapter: 'test',
|
|
460
|
-
$channel: { id: 'channel-id', type: 'text' },
|
|
461
|
-
$content: 'Hello',
|
|
462
|
-
} as any
|
|
463
|
-
|
|
464
|
-
adapter.emit('message.receive', message)
|
|
465
|
-
await new Promise((r) => setTimeout(r, 20))
|
|
466
|
-
expect(order).toEqual(['lifecycle', 'adapterObserver'])
|
|
467
|
-
})
|
|
468
|
-
})
|
|
469
|
-
})
|
|
470
|
-
|
|
471
|
-
describe('Adapter createBot', () => {
|
|
472
|
-
it('should be abstract method', () => {
|
|
473
|
-
expect(typeof adapter.createBot).toBe('function')
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
it('should create bot with config', () => {
|
|
477
|
-
const config = { id: 'test-bot' }
|
|
478
|
-
const bot = adapter.createBot(config)
|
|
479
|
-
|
|
480
|
-
expect(bot).toBeInstanceOf(MockBot)
|
|
481
|
-
expect(bot.$id).toBe('test-bot')
|
|
482
|
-
})
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
describe('Adapter Bots Management', () => {
|
|
486
|
-
it('should manage multiple bots', async () => {
|
|
487
|
-
const config = [
|
|
488
|
-
{ id: 'bot1' },
|
|
489
|
-
{ id: 'bot2' },
|
|
490
|
-
{ id: 'bot3' }
|
|
491
|
-
]
|
|
492
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
493
|
-
|
|
494
|
-
await adapter.start()
|
|
495
|
-
|
|
496
|
-
expect(adapter.bots.size).toBe(3)
|
|
497
|
-
expect(Array.from(adapter.bots.keys())).toEqual(['bot1', 'bot2', 'bot3'])
|
|
498
|
-
})
|
|
499
|
-
|
|
500
|
-
it('should access bot by id', async () => {
|
|
501
|
-
const config = [{ id: 'bot1' }]
|
|
502
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
503
|
-
|
|
504
|
-
await adapter.start()
|
|
505
|
-
|
|
506
|
-
const bot = adapter.bots.get('bot1')
|
|
507
|
-
expect(bot).toBeDefined()
|
|
508
|
-
expect(bot!.$id).toBe('bot1')
|
|
509
|
-
})
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
describe('Message $reply', () => {
|
|
513
|
-
it('should send reply through adapter.sendMessage', async () => {
|
|
514
|
-
const config = [{ id: 'bot1' }]
|
|
515
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
516
|
-
await adapter.start()
|
|
517
|
-
|
|
518
|
-
const bot = adapter.bots.get('bot1')!
|
|
519
|
-
const message = bot.$formatMessage({ id: 'msg-1', raw: 'Hello' })
|
|
520
|
-
|
|
521
|
-
const sendSpy = vi.spyOn(adapter, 'sendMessage')
|
|
522
|
-
|
|
523
|
-
await message.$reply('Reply content')
|
|
524
|
-
|
|
525
|
-
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
526
|
-
bot: 'bot1',
|
|
527
|
-
content: expect.arrayContaining([
|
|
528
|
-
expect.objectContaining({ type: 'text', data: { text: 'Reply content' } })
|
|
529
|
-
])
|
|
530
|
-
}))
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
it('should support quote reply', async () => {
|
|
534
|
-
const config = [{ id: 'bot1' }]
|
|
535
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
536
|
-
await adapter.start()
|
|
537
|
-
|
|
538
|
-
const bot = adapter.bots.get('bot1')!
|
|
539
|
-
const message = bot.$formatMessage({ id: 'msg-1', raw: 'Hello' })
|
|
540
|
-
|
|
541
|
-
const sendSpy = vi.spyOn(adapter, 'sendMessage')
|
|
542
|
-
|
|
543
|
-
await message.$reply('Reply content', true)
|
|
544
|
-
|
|
545
|
-
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
546
|
-
content: expect.arrayContaining([
|
|
547
|
-
expect.objectContaining({ type: 'reply', data: { id: 'msg-1' } }),
|
|
548
|
-
expect.objectContaining({ type: 'text', data: { text: 'Reply content' } })
|
|
549
|
-
])
|
|
550
|
-
}))
|
|
551
|
-
})
|
|
552
|
-
|
|
553
|
-
it('should support custom quote id', async () => {
|
|
554
|
-
const config = [{ id: 'bot1' }]
|
|
555
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
556
|
-
await adapter.start()
|
|
557
|
-
|
|
558
|
-
const bot = adapter.bots.get('bot1')!
|
|
559
|
-
const message = bot.$formatMessage({ id: 'msg-1', raw: 'Hello' })
|
|
560
|
-
|
|
561
|
-
const sendSpy = vi.spyOn(adapter, 'sendMessage')
|
|
562
|
-
|
|
563
|
-
await message.$reply('Reply content', 'custom-msg-id')
|
|
564
|
-
|
|
565
|
-
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
566
|
-
content: expect.arrayContaining([
|
|
567
|
-
expect.objectContaining({ type: 'reply', data: { id: 'custom-msg-id' } })
|
|
568
|
-
])
|
|
569
|
-
}))
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
it('should handle array content', async () => {
|
|
573
|
-
const config = [{ id: 'bot1' }]
|
|
574
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
575
|
-
await adapter.start()
|
|
576
|
-
|
|
577
|
-
const bot = adapter.bots.get('bot1')!
|
|
578
|
-
const message = bot.$formatMessage({ id: 'msg-1', raw: 'Hello' })
|
|
579
|
-
|
|
580
|
-
const sendSpy = vi.spyOn(adapter, 'sendMessage')
|
|
581
|
-
|
|
582
|
-
await message.$reply([
|
|
583
|
-
{ type: 'text', data: { text: 'Part 1' } },
|
|
584
|
-
{ type: 'text', data: { text: 'Part 2' } }
|
|
585
|
-
])
|
|
586
|
-
|
|
587
|
-
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
588
|
-
content: expect.arrayContaining([
|
|
589
|
-
expect.objectContaining({ type: 'text', data: { text: 'Part 1' } }),
|
|
590
|
-
expect.objectContaining({ type: 'text', data: { text: 'Part 2' } })
|
|
591
|
-
])
|
|
592
|
-
}))
|
|
593
|
-
})
|
|
594
|
-
|
|
595
|
-
it('should trigger before.sendMessage hooks', async () => {
|
|
596
|
-
const config = [{ id: 'bot1' }]
|
|
597
|
-
const adapter = new MockAdapter(plugin, 'test', config)
|
|
598
|
-
await adapter.start()
|
|
599
|
-
|
|
600
|
-
let hookCalled = false
|
|
601
|
-
plugin.root.on('before.sendMessage', (options) => {
|
|
602
|
-
hookCalled = true
|
|
603
|
-
return options
|
|
604
|
-
})
|
|
605
|
-
|
|
606
|
-
const bot = adapter.bots.get('bot1')!
|
|
607
|
-
const message = bot.$formatMessage({ id: 'msg-1', raw: 'Hello' })
|
|
608
|
-
|
|
609
|
-
await message.$reply('Reply content')
|
|
610
|
-
|
|
611
|
-
expect(hookCalled).toBe(true)
|
|
612
|
-
})
|
|
613
|
-
})
|
|
614
|
-
})
|
|
615
|
-
|
|
616
|
-
describe('Adapter Registry', () => {
|
|
617
|
-
it('should have a Registry Map', () => {
|
|
618
|
-
expect(Adapter.Registry).toBeInstanceOf(Map)
|
|
619
|
-
})
|
|
620
|
-
|
|
621
|
-
it('should register adapter factory', () => {
|
|
622
|
-
const factory = MockAdapter as any
|
|
623
|
-
Adapter.register('mock', factory)
|
|
624
|
-
|
|
625
|
-
expect(Adapter.Registry.has('mock')).toBe(true)
|
|
626
|
-
expect(Adapter.Registry.get('mock')).toBe(factory)
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
it('should allow multiple adapter registrations', () => {
|
|
630
|
-
const factory1 = MockAdapter as any
|
|
631
|
-
const factory2 = MockAdapter as any
|
|
632
|
-
|
|
633
|
-
Adapter.register('adapter1', factory1)
|
|
634
|
-
Adapter.register('adapter2', factory2)
|
|
635
|
-
|
|
636
|
-
expect(Adapter.Registry.size).toBeGreaterThanOrEqual(2)
|
|
637
|
-
})
|
|
638
|
-
})
|