@hybrd/xmtp 1.0.0
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/.cache/tsbuildinfo.json +1 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/README.md +380 -0
- package/biome.jsonc +4 -0
- package/package.json +46 -0
- package/scripts/generate-keys.ts +25 -0
- package/scripts/refresh-identity.ts +119 -0
- package/scripts/register-wallet.ts +95 -0
- package/scripts/revoke-all-installations.ts +91 -0
- package/scripts/revoke-installations.ts +94 -0
- package/src/abi/l2_resolver.ts +699 -0
- package/src/client.ts +940 -0
- package/src/constants.ts +6 -0
- package/src/index.ts +129 -0
- package/src/lib/message-listener.test.ts +369 -0
- package/src/lib/message-listener.ts +343 -0
- package/src/lib/subjects.ts +89 -0
- package/src/localStorage.ts.old +203 -0
- package/src/resolver/address-resolver.ts +221 -0
- package/src/resolver/basename-resolver.ts +585 -0
- package/src/resolver/ens-resolver.ts +324 -0
- package/src/resolver/index.ts +1 -0
- package/src/resolver/resolver.ts +336 -0
- package/src/resolver/xmtp-resolver.ts +436 -0
- package/src/service-client.ts +286 -0
- package/src/transactionMonitor.ts.old +275 -0
- package/src/types.ts +157 -0
- package/tsconfig.json +12 -0
package/src/constants.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ===================================================================
|
|
2
|
+
// Betting Configuration
|
|
3
|
+
// ===================================================================
|
|
4
|
+
export const DEFAULT_OPTIONS = ["yes", "no"]
|
|
5
|
+
export const DEFAULT_AMOUNT = "0.1"
|
|
6
|
+
export const MAX_USDC_AMOUNT = 10 // Maximum allowed USDC transaction amount
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// ===================================================================
|
|
2
|
+
// XMTP Package - Main Entry Point
|
|
3
|
+
// ===================================================================
|
|
4
|
+
// This package provides a clean interface to XMTP functionality
|
|
5
|
+
// Re-exports core XMTP SDK types and utilities
|
|
6
|
+
|
|
7
|
+
export * from "./client"
|
|
8
|
+
export * from "./constants"
|
|
9
|
+
export * from "./lib/message-listener"
|
|
10
|
+
export * from "./lib/subjects"
|
|
11
|
+
export * from "./resolver"
|
|
12
|
+
export * from "./resolver/basename-resolver"
|
|
13
|
+
export * from "./resolver/ens-resolver"
|
|
14
|
+
export * from "./resolver/xmtp-resolver"
|
|
15
|
+
export * from "./service-client"
|
|
16
|
+
export * from "./types"
|
|
17
|
+
|
|
18
|
+
// ===================================================================
|
|
19
|
+
// Enhanced XMTP Client & Connection Management
|
|
20
|
+
// ===================================================================
|
|
21
|
+
export {
|
|
22
|
+
createXMTPConnectionManager,
|
|
23
|
+
// Enhanced connection management
|
|
24
|
+
XMTPConnectionManager,
|
|
25
|
+
type XMTPConnectionConfig,
|
|
26
|
+
type XMTPConnectionHealth
|
|
27
|
+
} from "./client"
|
|
28
|
+
|
|
29
|
+
// ===================================================================
|
|
30
|
+
// XMTP Service Client (for external service communication)
|
|
31
|
+
// ===================================================================
|
|
32
|
+
export {
|
|
33
|
+
createXmtpServiceClient,
|
|
34
|
+
XmtpServiceClient
|
|
35
|
+
} from "./service-client"
|
|
36
|
+
|
|
37
|
+
// Service Client Types
|
|
38
|
+
export type {
|
|
39
|
+
GetMessageParams,
|
|
40
|
+
GetRootMessageParams,
|
|
41
|
+
// Function parameter types
|
|
42
|
+
SendMessageParams,
|
|
43
|
+
// Response types
|
|
44
|
+
SendMessageResponse,
|
|
45
|
+
SendReactionParams,
|
|
46
|
+
SendReactionResponse,
|
|
47
|
+
SendReplyParams,
|
|
48
|
+
SendReplyResponse,
|
|
49
|
+
SendTransactionParams,
|
|
50
|
+
SendTransactionResponse,
|
|
51
|
+
TransactionCall,
|
|
52
|
+
TransactionRequest,
|
|
53
|
+
XmtpRootMessageResponse,
|
|
54
|
+
XmtpServiceClientConfig,
|
|
55
|
+
XmtpServiceMessage,
|
|
56
|
+
XmtpServiceResponse
|
|
57
|
+
} from "./types"
|
|
58
|
+
|
|
59
|
+
// ===================================================================
|
|
60
|
+
// XMTP Core SDK Exports
|
|
61
|
+
// ===================================================================
|
|
62
|
+
export {
|
|
63
|
+
Client,
|
|
64
|
+
IdentifierKind,
|
|
65
|
+
// type Conversation,
|
|
66
|
+
type DecodedMessage,
|
|
67
|
+
type Dm,
|
|
68
|
+
// type Group,
|
|
69
|
+
type LogLevel,
|
|
70
|
+
type Signer,
|
|
71
|
+
type XmtpEnv
|
|
72
|
+
} from "@xmtp/node-sdk"
|
|
73
|
+
|
|
74
|
+
// ===================================================================
|
|
75
|
+
// XMTP Content Types
|
|
76
|
+
// ===================================================================
|
|
77
|
+
export {
|
|
78
|
+
ContentTypeTransactionReference,
|
|
79
|
+
type TransactionReference
|
|
80
|
+
} from "@xmtp/content-type-transaction-reference"
|
|
81
|
+
|
|
82
|
+
export { ContentTypeText, type TextParameters } from "@xmtp/content-type-text"
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
ContentTypeReaction,
|
|
86
|
+
type Reaction
|
|
87
|
+
} from "@xmtp/content-type-reaction"
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
ContentTypeReply,
|
|
91
|
+
ReplyCodec,
|
|
92
|
+
type Reply
|
|
93
|
+
} from "@xmtp/content-type-reply"
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
ContentTypeGroupUpdated,
|
|
97
|
+
GroupUpdatedCodec,
|
|
98
|
+
type GroupUpdated
|
|
99
|
+
} from "@xmtp/content-type-group-updated"
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
ContentTypeWalletSendCalls,
|
|
103
|
+
type WalletSendCallsParams
|
|
104
|
+
} from "@xmtp/content-type-wallet-send-calls"
|
|
105
|
+
|
|
106
|
+
// ===================================================================
|
|
107
|
+
// Local Client Utilities
|
|
108
|
+
// ===================================================================
|
|
109
|
+
export {
|
|
110
|
+
backupDbToPersistentStorage,
|
|
111
|
+
createSigner,
|
|
112
|
+
createUser,
|
|
113
|
+
createXMTPClient,
|
|
114
|
+
diagnoseXMTPIdentityIssue,
|
|
115
|
+
generateEncryptionKeyHex,
|
|
116
|
+
getEncryptionKeyFromHex,
|
|
117
|
+
logAgentDetails,
|
|
118
|
+
startPeriodicBackup,
|
|
119
|
+
validateEnvironment
|
|
120
|
+
} from "./client"
|
|
121
|
+
|
|
122
|
+
// ===================================================================
|
|
123
|
+
// Application Constants
|
|
124
|
+
// ===================================================================
|
|
125
|
+
export {
|
|
126
|
+
DEFAULT_AMOUNT,
|
|
127
|
+
DEFAULT_OPTIONS,
|
|
128
|
+
MAX_USDC_AMOUNT
|
|
129
|
+
} from "./constants"
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events"
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
3
|
+
import { MessageListener } from "./message-listener"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if content contains any of the supported agent mention patterns
|
|
7
|
+
*/
|
|
8
|
+
function hasAgentMention(content: string | undefined): boolean {
|
|
9
|
+
if (!content) return false
|
|
10
|
+
|
|
11
|
+
const lowerContent = content.toLowerCase()
|
|
12
|
+
const mentionPatterns = [
|
|
13
|
+
"@agent",
|
|
14
|
+
"@hybrid",
|
|
15
|
+
"@hybrid.base.eth",
|
|
16
|
+
"@hybrid.eth",
|
|
17
|
+
"@agent.eth",
|
|
18
|
+
"@agent.base.eth"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
return mentionPatterns.some((pattern) => lowerContent.includes(pattern))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Mock the XmtpResolver
|
|
25
|
+
vi.mock("./xmtp-resolver", () => ({
|
|
26
|
+
XmtpResolver: vi.fn().mockImplementation(() => ({
|
|
27
|
+
resolveAddress: vi.fn().mockResolvedValue("0x456789abcdef"),
|
|
28
|
+
findRootMessage: vi.fn().mockResolvedValue(null),
|
|
29
|
+
prePopulateCache: vi.fn().mockResolvedValue(undefined)
|
|
30
|
+
}))
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
// Mock the BasenameResolver
|
|
34
|
+
vi.mock("./basename-resolver", () => ({
|
|
35
|
+
BasenameResolver: vi.fn().mockImplementation(() => ({
|
|
36
|
+
getBasename: vi.fn().mockResolvedValue("testuser.base.eth"),
|
|
37
|
+
getBasenameAddress: vi.fn().mockResolvedValue("0x456789abcdef"),
|
|
38
|
+
resolveBasenameProfile: vi.fn().mockResolvedValue({
|
|
39
|
+
basename: "testuser.base.eth",
|
|
40
|
+
avatar: "https://example.com/avatar.jpg",
|
|
41
|
+
description: "Test user profile",
|
|
42
|
+
twitter: "@testuser",
|
|
43
|
+
github: "testuser",
|
|
44
|
+
url: "https://testuser.com"
|
|
45
|
+
})
|
|
46
|
+
}))
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
// Mock the ENSResolver
|
|
50
|
+
vi.mock("./ens-resolver", () => ({
|
|
51
|
+
ENSResolver: vi.fn().mockImplementation(() => ({
|
|
52
|
+
resolveAddressToENS: vi.fn().mockResolvedValue(null),
|
|
53
|
+
resolveENSName: vi.fn().mockResolvedValue(null),
|
|
54
|
+
isENSName: vi.fn().mockReturnValue(false)
|
|
55
|
+
}))
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
// Mock the subjects
|
|
59
|
+
vi.mock("./subjects", () => ({
|
|
60
|
+
extractSubjects: vi.fn().mockResolvedValue({})
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
// Mock the XMTP client
|
|
64
|
+
const mockClient = {
|
|
65
|
+
inboxId: "test-inbox-id",
|
|
66
|
+
accountIdentifier: { identifier: "0x123" },
|
|
67
|
+
conversations: {
|
|
68
|
+
sync: vi.fn(),
|
|
69
|
+
list: vi.fn().mockResolvedValue([]),
|
|
70
|
+
streamAllMessages: vi.fn(),
|
|
71
|
+
getConversationById: vi.fn()
|
|
72
|
+
},
|
|
73
|
+
preferences: {
|
|
74
|
+
inboxStateFromInboxIds: vi.fn()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe("MessageListener", () => {
|
|
79
|
+
let listener: MessageListener
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
vi.clearAllMocks()
|
|
83
|
+
listener = new MessageListener({
|
|
84
|
+
xmtpClient: mockClient as any,
|
|
85
|
+
publicClient: {} as any,
|
|
86
|
+
filter: ({ message }) => {
|
|
87
|
+
const content = message.content as string
|
|
88
|
+
return hasAgentMention(content)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("should be an instance of EventEmitter", () => {
|
|
94
|
+
expect(listener).toBeInstanceOf(EventEmitter)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("should emit message events with enriched sender information", async () => {
|
|
98
|
+
const mockMessage = {
|
|
99
|
+
id: "test-message-id",
|
|
100
|
+
content: "@agent test message",
|
|
101
|
+
senderInboxId: "sender-inbox-id",
|
|
102
|
+
conversationId: "conversation-id",
|
|
103
|
+
sentAt: new Date(),
|
|
104
|
+
contentType: { typeId: "text" }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Mock the stream to emit our test message
|
|
108
|
+
const mockStream = {
|
|
109
|
+
async *[Symbol.asyncIterator]() {
|
|
110
|
+
yield mockMessage
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
mockClient.conversations.streamAllMessages.mockResolvedValue(mockStream)
|
|
115
|
+
mockClient.conversations.getConversationById.mockResolvedValue({
|
|
116
|
+
id: "conversation-id"
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Set up message event listener
|
|
120
|
+
const messageHandler = vi.fn()
|
|
121
|
+
listener.on("message", messageHandler)
|
|
122
|
+
|
|
123
|
+
// Start the listener (but don't wait for it to complete since it runs indefinitely)
|
|
124
|
+
const startPromise = listener.start()
|
|
125
|
+
|
|
126
|
+
// Give it a moment to process the message
|
|
127
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
128
|
+
|
|
129
|
+
expect(messageHandler).toHaveBeenCalledWith(
|
|
130
|
+
expect.objectContaining({
|
|
131
|
+
message: expect.objectContaining({
|
|
132
|
+
id: "test-message-id",
|
|
133
|
+
content: "@agent test message",
|
|
134
|
+
senderInboxId: "sender-inbox-id",
|
|
135
|
+
conversationId: "conversation-id"
|
|
136
|
+
}),
|
|
137
|
+
sender: expect.objectContaining({
|
|
138
|
+
address: "0x456789abcdef",
|
|
139
|
+
inboxId: "sender-inbox-id",
|
|
140
|
+
basename: "testuser.base.eth",
|
|
141
|
+
name: "testuser.base.eth"
|
|
142
|
+
}),
|
|
143
|
+
subjects: expect.any(Object),
|
|
144
|
+
rootMessage: undefined
|
|
145
|
+
})
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
listener.stop()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("should handle messages without basenames gracefully", async () => {
|
|
152
|
+
// Mock resolvers to return no basename
|
|
153
|
+
const listenerWithoutBasename = new MessageListener({
|
|
154
|
+
xmtpClient: mockClient as any,
|
|
155
|
+
publicClient: {} as any,
|
|
156
|
+
filter: ({ message }) => {
|
|
157
|
+
const content = message.content as string
|
|
158
|
+
return hasAgentMention(content)
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// Mock basename resolver to return null
|
|
163
|
+
const mockBasenameResolver = (listenerWithoutBasename as any)
|
|
164
|
+
.basenameResolver
|
|
165
|
+
mockBasenameResolver.getBasename = vi.fn().mockResolvedValue(null)
|
|
166
|
+
|
|
167
|
+
const mockMessage = {
|
|
168
|
+
id: "test-message-id-2",
|
|
169
|
+
content: "@agent test message 2",
|
|
170
|
+
senderInboxId: "sender-inbox-id-2",
|
|
171
|
+
conversationId: "conversation-id-2",
|
|
172
|
+
sentAt: new Date(),
|
|
173
|
+
contentType: { typeId: "text" }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const mockStream = {
|
|
177
|
+
async *[Symbol.asyncIterator]() {
|
|
178
|
+
yield mockMessage
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
mockClient.conversations.streamAllMessages.mockResolvedValue(mockStream)
|
|
183
|
+
mockClient.conversations.getConversationById.mockResolvedValue({
|
|
184
|
+
id: "conversation-id-2"
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const messageHandler = vi.fn()
|
|
188
|
+
listenerWithoutBasename.on("message", messageHandler)
|
|
189
|
+
|
|
190
|
+
const startPromise = listenerWithoutBasename.start()
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
192
|
+
|
|
193
|
+
expect(messageHandler).toHaveBeenCalledWith(
|
|
194
|
+
expect.objectContaining({
|
|
195
|
+
sender: expect.objectContaining({
|
|
196
|
+
address: "0x456789abcdef",
|
|
197
|
+
inboxId: "sender-inbox-id-2",
|
|
198
|
+
basename: undefined,
|
|
199
|
+
name: expect.stringContaining("0x4567") // Should use truncated address
|
|
200
|
+
}),
|
|
201
|
+
subjects: expect.any(Object),
|
|
202
|
+
rootMessage: undefined
|
|
203
|
+
})
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
listenerWithoutBasename.stop()
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it("should handle all supported agent mention patterns", async () => {
|
|
210
|
+
const mentionPatterns = [
|
|
211
|
+
"@agent test message",
|
|
212
|
+
"@hybrid test message",
|
|
213
|
+
"@hybrid.base.eth test message",
|
|
214
|
+
"@hybrid.eth test message",
|
|
215
|
+
"@agent.eth test message",
|
|
216
|
+
"@agent.base.eth test message"
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
for (const content of mentionPatterns) {
|
|
220
|
+
const testListener = new MessageListener({
|
|
221
|
+
xmtpClient: mockClient as any,
|
|
222
|
+
publicClient: {} as any,
|
|
223
|
+
filter: ({ message }) => {
|
|
224
|
+
const messageContent = message.content as string
|
|
225
|
+
return hasAgentMention(messageContent)
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const mockMessage = {
|
|
230
|
+
id: `test-message-${content}`,
|
|
231
|
+
content: content,
|
|
232
|
+
senderInboxId: "sender-inbox-id",
|
|
233
|
+
conversationId: "conversation-id",
|
|
234
|
+
sentAt: new Date(),
|
|
235
|
+
contentType: { typeId: "text" }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const mockStream = {
|
|
239
|
+
async *[Symbol.asyncIterator]() {
|
|
240
|
+
yield mockMessage
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
mockClient.conversations.streamAllMessages.mockResolvedValue(mockStream)
|
|
245
|
+
mockClient.conversations.getConversationById.mockResolvedValue({
|
|
246
|
+
id: "conversation-id"
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const messageHandler = vi.fn()
|
|
250
|
+
testListener.on("message", messageHandler)
|
|
251
|
+
|
|
252
|
+
const startPromise = testListener.start()
|
|
253
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
254
|
+
|
|
255
|
+
expect(messageHandler).toHaveBeenCalledWith(
|
|
256
|
+
expect.objectContaining({
|
|
257
|
+
message: expect.objectContaining({
|
|
258
|
+
content: content
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
testListener.stop()
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it("should allow replies without mention checking", async () => {
|
|
268
|
+
const testListener = new MessageListener({
|
|
269
|
+
xmtpClient: mockClient as any,
|
|
270
|
+
publicClient: {} as any,
|
|
271
|
+
filter: ({ message }) => {
|
|
272
|
+
const contentTypeId = message.contentType?.typeId
|
|
273
|
+
if (contentTypeId === "reply") {
|
|
274
|
+
return true
|
|
275
|
+
}
|
|
276
|
+
if (contentTypeId === "text") {
|
|
277
|
+
const messageContent = message.content as string
|
|
278
|
+
return hasAgentMention(messageContent)
|
|
279
|
+
}
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
const mockReplyMessage = {
|
|
285
|
+
id: "test-reply-message",
|
|
286
|
+
content: { content: "yes I'm in" },
|
|
287
|
+
senderInboxId: "sender-inbox-id",
|
|
288
|
+
conversationId: "conversation-id",
|
|
289
|
+
sentAt: new Date(),
|
|
290
|
+
contentType: { typeId: "reply" }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const mockStream = {
|
|
294
|
+
async *[Symbol.asyncIterator]() {
|
|
295
|
+
yield mockReplyMessage
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
mockClient.conversations.streamAllMessages.mockResolvedValue(mockStream)
|
|
300
|
+
mockClient.conversations.getConversationById.mockResolvedValue({
|
|
301
|
+
id: "conversation-id"
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const messageHandler = vi.fn()
|
|
305
|
+
testListener.on("message", messageHandler)
|
|
306
|
+
|
|
307
|
+
const startPromise = testListener.start()
|
|
308
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
309
|
+
|
|
310
|
+
expect(messageHandler).toHaveBeenCalledWith(
|
|
311
|
+
expect.objectContaining({
|
|
312
|
+
message: expect.objectContaining({
|
|
313
|
+
content: expect.objectContaining({
|
|
314
|
+
content: "yes I'm in"
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
testListener.stop()
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it("should properly clean up when stopped", () => {
|
|
324
|
+
const removeAllListenersSpy = vi.spyOn(listener, "removeAllListeners")
|
|
325
|
+
|
|
326
|
+
listener.stop()
|
|
327
|
+
|
|
328
|
+
expect(removeAllListenersSpy).toHaveBeenCalled()
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it("should get stats", () => {
|
|
332
|
+
const stats = listener.getStats()
|
|
333
|
+
|
|
334
|
+
expect(stats).toEqual({
|
|
335
|
+
messageCount: 0,
|
|
336
|
+
conversationCount: 0,
|
|
337
|
+
isActive: false
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it("should emit started and stopped events", async () => {
|
|
342
|
+
const startedHandler = vi.fn()
|
|
343
|
+
const stoppedHandler = vi.fn()
|
|
344
|
+
|
|
345
|
+
listener.on("started", startedHandler)
|
|
346
|
+
listener.on("stopped", stoppedHandler)
|
|
347
|
+
|
|
348
|
+
// Mock to prevent infinite stream
|
|
349
|
+
mockClient.conversations.streamAllMessages.mockResolvedValue({
|
|
350
|
+
async *[Symbol.asyncIterator]() {
|
|
351
|
+
// Empty iterator that ends immediately
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
await listener.start()
|
|
356
|
+
|
|
357
|
+
// Give a moment for the events to be processed
|
|
358
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
359
|
+
|
|
360
|
+
expect(startedHandler).toHaveBeenCalled()
|
|
361
|
+
|
|
362
|
+
listener.stop()
|
|
363
|
+
|
|
364
|
+
// Give a moment for the stop event to be processed
|
|
365
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
366
|
+
|
|
367
|
+
expect(stoppedHandler).toHaveBeenCalled()
|
|
368
|
+
})
|
|
369
|
+
})
|