agent-messenger 2.26.0 → 2.27.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/.claude-plugin/README.md +16 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +27 -0
- package/dist/package.json +1 -1
- package/dist/src/platforms/discord/listener.d.ts +9 -4
- package/dist/src/platforms/discord/listener.d.ts.map +1 -1
- package/dist/src/platforms/discord/listener.js +110 -16
- package/dist/src/platforms/discord/listener.js.map +1 -1
- package/dist/src/platforms/discordbot/listener.d.ts +6 -0
- package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/listener.js +78 -2
- package/dist/src/platforms/discordbot/listener.js.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.d.ts.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.js +105 -6
- package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
- package/docs/content/docs/sdk/discord.mdx +1 -9
- package/e2e/README.md +3 -1
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +37 -8
- package/skills/agent-discord/references/authentication.md +46 -8
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +7 -4
- package/skills/agent-slack/references/authentication.md +8 -3
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/listener.test.ts +187 -147
- package/src/platforms/discord/listener.ts +122 -18
- package/src/platforms/discordbot/listener.test.ts +186 -184
- package/src/platforms/discordbot/listener.ts +83 -2
- package/src/platforms/slack/qr-http-login.test.ts +47 -0
- package/src/platforms/slack/qr-http-login.ts +112 -7
|
@@ -107,11 +107,40 @@ function createMockClient(overrides: Record<string, any> = {}) {
|
|
|
107
107
|
} as any
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// start() now resolves only once the gateway emits READY, so the handshake must be
|
|
111
|
+
// driven concurrently while start() is pending. This waits for connect() to create the
|
|
112
|
+
// socket (after the awaited gatewayConnect microtask) before the caller drives it.
|
|
113
|
+
async function waitForSocket(): Promise<MockWs> {
|
|
114
|
+
for (let i = 0; i < 50 && !mockWsInstance; i++) {
|
|
115
|
+
await Promise.resolve()
|
|
116
|
+
}
|
|
117
|
+
return mockWsInstance
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function startAndReady(listener: DiscordListener): Promise<void> {
|
|
121
|
+
const started = listener.start()
|
|
122
|
+
const ws = await waitForSocket()
|
|
123
|
+
ws.simulateOpen()
|
|
124
|
+
ws.simulateHello()
|
|
125
|
+
ws.simulateReady()
|
|
126
|
+
await started
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// For tests that exercise pre-READY behavior (Identify/heartbeat/close paths): start() stays
|
|
130
|
+
// pending here. The start promise's rejection is swallowed; afterEach stop() clears its timer.
|
|
131
|
+
async function startConnecting(listener: DiscordListener): Promise<MockWs> {
|
|
132
|
+
listener.start().catch(() => {})
|
|
133
|
+
const ws = await waitForSocket()
|
|
134
|
+
ws.simulateOpen()
|
|
135
|
+
return ws
|
|
136
|
+
}
|
|
137
|
+
|
|
110
138
|
describe('DiscordListener', () => {
|
|
111
139
|
let listener: DiscordListener
|
|
112
140
|
|
|
113
141
|
afterEach(() => {
|
|
114
142
|
listener?.stop()
|
|
143
|
+
mockWsInstance = undefined as unknown as MockWs
|
|
115
144
|
})
|
|
116
145
|
|
|
117
146
|
describe('start', () => {
|
|
@@ -119,21 +148,75 @@ describe('DiscordListener', () => {
|
|
|
119
148
|
const client = createMockClient()
|
|
120
149
|
listener = new DiscordListener(client)
|
|
121
150
|
|
|
122
|
-
await listener
|
|
123
|
-
mockWsInstance.simulateOpen()
|
|
151
|
+
await startAndReady(listener)
|
|
124
152
|
|
|
125
153
|
expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
|
|
126
154
|
})
|
|
127
155
|
|
|
156
|
+
it('does not resolve until a READY dispatch is delivered', async () => {
|
|
157
|
+
const client = createMockClient()
|
|
158
|
+
listener = new DiscordListener(client)
|
|
159
|
+
|
|
160
|
+
let resolved = false
|
|
161
|
+
const started = listener.start().then(() => {
|
|
162
|
+
resolved = true
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const ws = await waitForSocket()
|
|
166
|
+
ws.simulateOpen()
|
|
167
|
+
ws.simulateHello()
|
|
168
|
+
|
|
169
|
+
// given: socket open + Hello (Identify sent) but no READY yet
|
|
170
|
+
await Promise.resolve()
|
|
171
|
+
expect(resolved).toBe(false)
|
|
172
|
+
|
|
173
|
+
// when: READY arrives
|
|
174
|
+
ws.simulateReady()
|
|
175
|
+
await started
|
|
176
|
+
|
|
177
|
+
expect(resolved).toBe(true)
|
|
178
|
+
})
|
|
179
|
+
|
|
128
180
|
it('is idempotent', async () => {
|
|
129
181
|
const client = createMockClient()
|
|
130
182
|
listener = new DiscordListener(client)
|
|
131
183
|
|
|
132
|
-
await listener
|
|
184
|
+
await startAndReady(listener)
|
|
133
185
|
await listener.start()
|
|
134
186
|
|
|
135
187
|
expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
|
|
136
188
|
})
|
|
189
|
+
|
|
190
|
+
it('shares one readiness promise across concurrent start() calls', async () => {
|
|
191
|
+
const client = createMockClient()
|
|
192
|
+
listener = new DiscordListener(client)
|
|
193
|
+
|
|
194
|
+
const first = listener.start()
|
|
195
|
+
const second = listener.start()
|
|
196
|
+
|
|
197
|
+
const ws = await waitForSocket()
|
|
198
|
+
ws.simulateOpen()
|
|
199
|
+
ws.simulateHello()
|
|
200
|
+
ws.simulateReady()
|
|
201
|
+
|
|
202
|
+
await Promise.all([first, second])
|
|
203
|
+
expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('rejects an in-flight start() when stop() is called before READY', async () => {
|
|
207
|
+
const client = createMockClient()
|
|
208
|
+
listener = new DiscordListener(client)
|
|
209
|
+
|
|
210
|
+
const started = listener.start()
|
|
211
|
+
const ws = await waitForSocket()
|
|
212
|
+
ws.simulateOpen()
|
|
213
|
+
ws.simulateHello()
|
|
214
|
+
|
|
215
|
+
// given: start() is still awaiting READY when the consumer stops the listener
|
|
216
|
+
listener.stop()
|
|
217
|
+
|
|
218
|
+
await expect(started).rejects.toThrow(/stopped before becoming ready/)
|
|
219
|
+
})
|
|
137
220
|
})
|
|
138
221
|
|
|
139
222
|
describe('connected event', () => {
|
|
@@ -144,10 +227,7 @@ describe('DiscordListener', () => {
|
|
|
144
227
|
const connected: any[] = []
|
|
145
228
|
listener.on('connected', (info) => connected.push(info))
|
|
146
229
|
|
|
147
|
-
await listener
|
|
148
|
-
mockWsInstance.simulateOpen()
|
|
149
|
-
mockWsInstance.simulateHello()
|
|
150
|
-
mockWsInstance.simulateReady()
|
|
230
|
+
await startAndReady(listener)
|
|
151
231
|
|
|
152
232
|
expect(connected.length).toBe(1)
|
|
153
233
|
expect(connected[0].user.id).toBe('U_SELF')
|
|
@@ -160,9 +240,8 @@ describe('DiscordListener', () => {
|
|
|
160
240
|
const client = createMockClient()
|
|
161
241
|
listener = new DiscordListener(client)
|
|
162
242
|
|
|
163
|
-
await listener
|
|
164
|
-
|
|
165
|
-
mockWsInstance.simulateHello()
|
|
243
|
+
const ws = await startConnecting(listener)
|
|
244
|
+
ws.simulateHello()
|
|
166
245
|
|
|
167
246
|
const sentMessages = mockWsInstance.sent.map((s) => JSON.parse(s))
|
|
168
247
|
const identifyMsg = sentMessages.find((m) => m.op === 2)
|
|
@@ -171,20 +250,27 @@ describe('DiscordListener', () => {
|
|
|
171
250
|
expect(identifyMsg.d.token).toBe('fake-token')
|
|
172
251
|
})
|
|
173
252
|
|
|
174
|
-
it('sends Identify with
|
|
253
|
+
it('sends a user-account Identify with the load-bearing default shape', async () => {
|
|
175
254
|
const client = createMockClient()
|
|
176
|
-
|
|
177
|
-
listener = new DiscordListener(client, { intents: customIntents })
|
|
255
|
+
listener = new DiscordListener(client)
|
|
178
256
|
|
|
179
|
-
await listener
|
|
180
|
-
|
|
181
|
-
mockWsInstance.simulateHello()
|
|
257
|
+
const ws = await startConnecting(listener)
|
|
258
|
+
ws.simulateHello()
|
|
182
259
|
|
|
183
260
|
const sentMessages = mockWsInstance.sent.map((s) => JSON.parse(s))
|
|
184
261
|
const identifyMsg = sentMessages.find((m) => m.op === 2)
|
|
185
262
|
|
|
186
263
|
expect(identifyMsg).toBeDefined()
|
|
187
|
-
expect(identifyMsg.d.
|
|
264
|
+
expect(identifyMsg.d.capabilities).toBe(16381)
|
|
265
|
+
expect(identifyMsg.d.client_state).toEqual({ guild_versions: {} })
|
|
266
|
+
expect(identifyMsg.d.compress).toBe(false)
|
|
267
|
+
expect(identifyMsg.d.presence).toEqual({ status: 'online', since: 0, activities: [], afk: false })
|
|
268
|
+
expect(identifyMsg.d.properties.browser).toBe('Chrome')
|
|
269
|
+
expect(identifyMsg.d.properties.client_build_number).toBe(648814)
|
|
270
|
+
|
|
271
|
+
// All intents incl. MESSAGE_CONTENT (1<<15); user sessions blank other users' content without it
|
|
272
|
+
expect(identifyMsg.d.intents).toBe(33_554_431)
|
|
273
|
+
expect(identifyMsg.d.intents & (1 << 15)).toBe(1 << 15)
|
|
188
274
|
})
|
|
189
275
|
})
|
|
190
276
|
|
|
@@ -196,10 +282,7 @@ describe('DiscordListener', () => {
|
|
|
196
282
|
const messages: DiscordGatewayMessageCreateEvent[] = []
|
|
197
283
|
listener.on('message_create', (event) => messages.push(event))
|
|
198
284
|
|
|
199
|
-
await listener
|
|
200
|
-
mockWsInstance.simulateOpen()
|
|
201
|
-
mockWsInstance.simulateHello()
|
|
202
|
-
mockWsInstance.simulateReady()
|
|
285
|
+
await startAndReady(listener)
|
|
203
286
|
mockWsInstance.simulateDispatch(
|
|
204
287
|
'MESSAGE_CREATE',
|
|
205
288
|
{
|
|
@@ -224,10 +307,7 @@ describe('DiscordListener', () => {
|
|
|
224
307
|
const updates: DiscordGatewayMessageUpdateEvent[] = []
|
|
225
308
|
listener.on('message_update', (event) => updates.push(event))
|
|
226
309
|
|
|
227
|
-
await listener
|
|
228
|
-
mockWsInstance.simulateOpen()
|
|
229
|
-
mockWsInstance.simulateHello()
|
|
230
|
-
mockWsInstance.simulateReady()
|
|
310
|
+
await startAndReady(listener)
|
|
231
311
|
mockWsInstance.simulateDispatch(
|
|
232
312
|
'MESSAGE_UPDATE',
|
|
233
313
|
{ id: 'msg_1', channel_id: 'C123', content: 'edited', edited_timestamp: '2024-01-01T00:01:00Z' },
|
|
@@ -246,10 +326,7 @@ describe('DiscordListener', () => {
|
|
|
246
326
|
const deletes: DiscordGatewayMessageDeleteEvent[] = []
|
|
247
327
|
listener.on('message_delete', (event) => deletes.push(event))
|
|
248
328
|
|
|
249
|
-
await listener
|
|
250
|
-
mockWsInstance.simulateOpen()
|
|
251
|
-
mockWsInstance.simulateHello()
|
|
252
|
-
mockWsInstance.simulateReady()
|
|
329
|
+
await startAndReady(listener)
|
|
253
330
|
mockWsInstance.simulateDispatch('MESSAGE_DELETE', { id: 'msg_1', channel_id: 'C123' }, 2)
|
|
254
331
|
|
|
255
332
|
expect(deletes.length).toBe(1)
|
|
@@ -265,10 +342,7 @@ describe('DiscordListener', () => {
|
|
|
265
342
|
const reactions: DiscordGatewayReactionEvent[] = []
|
|
266
343
|
listener.on('message_reaction_add', (event) => reactions.push(event))
|
|
267
344
|
|
|
268
|
-
await listener
|
|
269
|
-
mockWsInstance.simulateOpen()
|
|
270
|
-
mockWsInstance.simulateHello()
|
|
271
|
-
mockWsInstance.simulateReady()
|
|
345
|
+
await startAndReady(listener)
|
|
272
346
|
mockWsInstance.simulateDispatch(
|
|
273
347
|
'MESSAGE_REACTION_ADD',
|
|
274
348
|
{
|
|
@@ -294,10 +368,7 @@ describe('DiscordListener', () => {
|
|
|
294
368
|
const events: DiscordGatewayGenericEvent[] = []
|
|
295
369
|
listener.on('discord_event', (event) => events.push(event))
|
|
296
370
|
|
|
297
|
-
await listener
|
|
298
|
-
mockWsInstance.simulateOpen()
|
|
299
|
-
mockWsInstance.simulateHello()
|
|
300
|
-
mockWsInstance.simulateReady()
|
|
371
|
+
await startAndReady(listener)
|
|
301
372
|
mockWsInstance.simulateDispatch(
|
|
302
373
|
'MESSAGE_CREATE',
|
|
303
374
|
{ id: 'm1', channel_id: 'C1', content: 'hi', timestamp: 't', author: { id: 'U1', username: 'u' } },
|
|
@@ -320,9 +391,8 @@ describe('DiscordListener', () => {
|
|
|
320
391
|
const client = createMockClient()
|
|
321
392
|
listener = new DiscordListener(client)
|
|
322
393
|
|
|
323
|
-
await listener
|
|
324
|
-
|
|
325
|
-
mockWsInstance.simulateHello(50)
|
|
394
|
+
const ws = await startConnecting(listener)
|
|
395
|
+
ws.simulateHello(50)
|
|
326
396
|
|
|
327
397
|
await new Promise((r) => setTimeout(r, 150))
|
|
328
398
|
|
|
@@ -342,10 +412,7 @@ describe('DiscordListener', () => {
|
|
|
342
412
|
const events: DiscordGatewayGenericEvent[] = []
|
|
343
413
|
listener.on('discord_event', (event) => events.push(event))
|
|
344
414
|
|
|
345
|
-
await listener
|
|
346
|
-
mockWsInstance.simulateOpen()
|
|
347
|
-
mockWsInstance.simulateHello()
|
|
348
|
-
mockWsInstance.simulateReady()
|
|
415
|
+
await startAndReady(listener)
|
|
349
416
|
mockWsInstance.simulateHeartbeatACK()
|
|
350
417
|
|
|
351
418
|
expect(events.length).toBe(0)
|
|
@@ -362,9 +429,8 @@ describe('DiscordListener', () => {
|
|
|
362
429
|
const disconnected: boolean[] = []
|
|
363
430
|
listener.on('disconnected', () => disconnected.push(true))
|
|
364
431
|
|
|
365
|
-
await listener
|
|
366
|
-
|
|
367
|
-
mockWsInstance.simulateHello(50)
|
|
432
|
+
const ws = await startConnecting(listener)
|
|
433
|
+
ws.simulateHello(50)
|
|
368
434
|
|
|
369
435
|
await new Promise((r) => setTimeout(r, 300))
|
|
370
436
|
|
|
@@ -380,8 +446,7 @@ describe('DiscordListener', () => {
|
|
|
380
446
|
const client = createMockClient()
|
|
381
447
|
listener = new DiscordListener(client)
|
|
382
448
|
|
|
383
|
-
await listener
|
|
384
|
-
mockWsInstance.simulateOpen()
|
|
449
|
+
await startConnecting(listener)
|
|
385
450
|
|
|
386
451
|
listener.stop()
|
|
387
452
|
|
|
@@ -398,9 +463,8 @@ describe('DiscordListener', () => {
|
|
|
398
463
|
const disconnected: boolean[] = []
|
|
399
464
|
listener.on('disconnected', () => disconnected.push(true))
|
|
400
465
|
|
|
401
|
-
await listener
|
|
402
|
-
|
|
403
|
-
mockWsInstance.simulateClose()
|
|
466
|
+
const ws = await startConnecting(listener)
|
|
467
|
+
ws.simulateClose()
|
|
404
468
|
|
|
405
469
|
expect(disconnected.length).toBe(1)
|
|
406
470
|
|
|
@@ -408,28 +472,20 @@ describe('DiscordListener', () => {
|
|
|
408
472
|
expect(client.gatewayConnect.mock.calls.length).toBeGreaterThanOrEqual(2)
|
|
409
473
|
})
|
|
410
474
|
|
|
411
|
-
it('
|
|
412
|
-
let callCount = 0
|
|
475
|
+
it('rejects start() and stops on gatewayConnect failure during initial connect', async () => {
|
|
413
476
|
const client = createMockClient({
|
|
414
|
-
gatewayConnect: mock(() =>
|
|
415
|
-
callCount++
|
|
416
|
-
if (callCount === 1) return Promise.reject(new Error('network_error'))
|
|
417
|
-
return Promise.resolve({ token: 'fake-token' })
|
|
418
|
-
}),
|
|
477
|
+
gatewayConnect: mock(() => Promise.reject(new Error('network_error'))),
|
|
419
478
|
})
|
|
420
479
|
|
|
421
480
|
listener = new DiscordListener(client)
|
|
481
|
+
listener.on('error', () => {})
|
|
422
482
|
|
|
423
|
-
|
|
424
|
-
listener.on('error', (err) => errors.push(err))
|
|
425
|
-
|
|
426
|
-
await listener.start()
|
|
483
|
+
await expect(listener.start()).rejects.toThrow('network_error')
|
|
427
484
|
|
|
485
|
+
// given: a pre-READY connect error tears down the listener (no resurrected reconnect loop)
|
|
428
486
|
await new Promise((r) => setTimeout(r, 1500))
|
|
429
|
-
|
|
430
|
-
expect(
|
|
431
|
-
expect(errors[0].message).toBe('network_error')
|
|
432
|
-
expect(client.gatewayConnect.mock.calls.length).toBeGreaterThanOrEqual(2)
|
|
487
|
+
expect((listener as any).running).toBe(false)
|
|
488
|
+
expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
|
|
433
489
|
})
|
|
434
490
|
})
|
|
435
491
|
|
|
@@ -442,10 +498,7 @@ describe('DiscordListener', () => {
|
|
|
442
498
|
const handler = (event: DiscordGatewayMessageCreateEvent) => messages.push(event)
|
|
443
499
|
listener.on('message_create', handler)
|
|
444
500
|
|
|
445
|
-
await listener
|
|
446
|
-
mockWsInstance.simulateOpen()
|
|
447
|
-
mockWsInstance.simulateHello()
|
|
448
|
-
mockWsInstance.simulateReady()
|
|
501
|
+
await startAndReady(listener)
|
|
449
502
|
mockWsInstance.simulateDispatch(
|
|
450
503
|
'MESSAGE_CREATE',
|
|
451
504
|
{ id: 'm1', channel_id: 'C1', content: 'a', timestamp: 't', author: { id: 'U1', username: 'u' } },
|
|
@@ -470,10 +523,7 @@ describe('DiscordListener', () => {
|
|
|
470
523
|
const messages: DiscordGatewayMessageCreateEvent[] = []
|
|
471
524
|
listener.once('message_create', (event) => messages.push(event))
|
|
472
525
|
|
|
473
|
-
await listener
|
|
474
|
-
mockWsInstance.simulateOpen()
|
|
475
|
-
mockWsInstance.simulateHello()
|
|
476
|
-
mockWsInstance.simulateReady()
|
|
526
|
+
await startAndReady(listener)
|
|
477
527
|
mockWsInstance.simulateDispatch(
|
|
478
528
|
'MESSAGE_CREATE',
|
|
479
529
|
{ id: 'm1', channel_id: 'C1', content: 'first', timestamp: 't', author: { id: 'U1', username: 'u' } },
|
|
@@ -495,10 +545,7 @@ describe('DiscordListener', () => {
|
|
|
495
545
|
const client = createMockClient()
|
|
496
546
|
listener = new DiscordListener(client)
|
|
497
547
|
|
|
498
|
-
await listener
|
|
499
|
-
mockWsInstance.simulateOpen()
|
|
500
|
-
mockWsInstance.simulateHello()
|
|
501
|
-
mockWsInstance.simulateReady()
|
|
548
|
+
await startAndReady(listener)
|
|
502
549
|
|
|
503
550
|
;(listener as any).reconnectAttempts = 5
|
|
504
551
|
mockWsInstance.simulateReconnect()
|
|
@@ -512,10 +559,7 @@ describe('DiscordListener', () => {
|
|
|
512
559
|
const client = createMockClient()
|
|
513
560
|
listener = new DiscordListener(client)
|
|
514
561
|
|
|
515
|
-
await listener
|
|
516
|
-
mockWsInstance.simulateOpen()
|
|
517
|
-
mockWsInstance.simulateHello()
|
|
518
|
-
mockWsInstance.simulateReady()
|
|
562
|
+
await startAndReady(listener)
|
|
519
563
|
|
|
520
564
|
expect((listener as any).sessionId).toBe('session_123')
|
|
521
565
|
|
|
@@ -530,10 +574,7 @@ describe('DiscordListener', () => {
|
|
|
530
574
|
const client = createMockClient()
|
|
531
575
|
listener = new DiscordListener(client)
|
|
532
576
|
|
|
533
|
-
await listener
|
|
534
|
-
mockWsInstance.simulateOpen()
|
|
535
|
-
mockWsInstance.simulateHello()
|
|
536
|
-
mockWsInstance.simulateReady()
|
|
577
|
+
await startAndReady(listener)
|
|
537
578
|
|
|
538
579
|
const sessionIdBefore = (listener as any).sessionId
|
|
539
580
|
|
|
@@ -548,10 +589,7 @@ describe('DiscordListener', () => {
|
|
|
548
589
|
const client = createMockClient()
|
|
549
590
|
listener = new DiscordListener(client)
|
|
550
591
|
|
|
551
|
-
await listener
|
|
552
|
-
mockWsInstance.simulateOpen()
|
|
553
|
-
mockWsInstance.simulateHello()
|
|
554
|
-
mockWsInstance.simulateReady()
|
|
592
|
+
await startAndReady(listener)
|
|
555
593
|
|
|
556
594
|
mockWsInstance.simulateClose()
|
|
557
595
|
|
|
@@ -569,22 +607,38 @@ describe('DiscordListener', () => {
|
|
|
569
607
|
})
|
|
570
608
|
|
|
571
609
|
describe('non-recoverable close codes', () => {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
610
|
+
for (const code of [4004, 4013, 4014]) {
|
|
611
|
+
it(`rejects start() and stops on non-recoverable close ${code}`, async () => {
|
|
612
|
+
const client = createMockClient()
|
|
613
|
+
listener = new DiscordListener(client)
|
|
614
|
+
listener.on('error', () => {})
|
|
575
615
|
|
|
576
|
-
|
|
577
|
-
|
|
616
|
+
const started = listener.start()
|
|
617
|
+
const ws = await waitForSocket()
|
|
618
|
+
ws.simulateOpen()
|
|
619
|
+
ws.simulateClose(code)
|
|
578
620
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
621
|
+
await expect(started).rejects.toThrow(String(code))
|
|
622
|
+
expect((listener as any).running).toBe(false)
|
|
623
|
+
expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
})
|
|
582
627
|
|
|
583
|
-
|
|
628
|
+
describe('connect timeout', () => {
|
|
629
|
+
it('rejects start() and tears down when READY never arrives', async () => {
|
|
630
|
+
const client = createMockClient()
|
|
631
|
+
listener = new DiscordListener(client, { connectTimeoutMs: 100 })
|
|
632
|
+
listener.on('error', () => {})
|
|
584
633
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
634
|
+
const started = listener.start()
|
|
635
|
+
const ws = await waitForSocket()
|
|
636
|
+
ws.simulateOpen()
|
|
637
|
+
ws.simulateHello()
|
|
638
|
+
// given: Identify sent but the gateway never delivers READY
|
|
639
|
+
|
|
640
|
+
await expect(started).rejects.toThrow(/did not become ready/)
|
|
641
|
+
expect((listener as any).running).toBe(false)
|
|
588
642
|
})
|
|
589
643
|
})
|
|
590
644
|
|
|
@@ -593,10 +647,7 @@ describe('DiscordListener', () => {
|
|
|
593
647
|
const client = createMockClient()
|
|
594
648
|
listener = new DiscordListener(client)
|
|
595
649
|
|
|
596
|
-
await listener
|
|
597
|
-
mockWsInstance.simulateOpen()
|
|
598
|
-
mockWsInstance.simulateHello()
|
|
599
|
-
mockWsInstance.simulateReady()
|
|
650
|
+
await startAndReady(listener)
|
|
600
651
|
|
|
601
652
|
expect((listener as any).sessionId).toBe('session_123')
|
|
602
653
|
|
|
@@ -623,10 +674,7 @@ describe('DiscordListener', () => {
|
|
|
623
674
|
const client = createMockClient()
|
|
624
675
|
listener = new DiscordListener(client)
|
|
625
676
|
|
|
626
|
-
await listener
|
|
627
|
-
mockWsInstance.simulateOpen()
|
|
628
|
-
mockWsInstance.simulateHello()
|
|
629
|
-
mockWsInstance.simulateReady()
|
|
677
|
+
await startAndReady(listener)
|
|
630
678
|
|
|
631
679
|
mockWsInstance.simulateClose(4009)
|
|
632
680
|
|
|
@@ -643,10 +691,12 @@ describe('DiscordListener', () => {
|
|
|
643
691
|
const client = createMockClient()
|
|
644
692
|
listener = new DiscordListener(client)
|
|
645
693
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
694
|
+
const started = listener.start()
|
|
695
|
+
const ws = await waitForSocket()
|
|
696
|
+
ws.simulateOpen()
|
|
697
|
+
ws.simulateHello(50)
|
|
698
|
+
ws.simulateReady()
|
|
699
|
+
await started
|
|
650
700
|
|
|
651
701
|
mockWsInstance.simulateHello(50)
|
|
652
702
|
|
|
@@ -671,10 +721,7 @@ describe('DiscordListener', () => {
|
|
|
671
721
|
const client = createMockClient()
|
|
672
722
|
listener = new DiscordListener(client)
|
|
673
723
|
|
|
674
|
-
await listener
|
|
675
|
-
mockWsInstance.simulateOpen()
|
|
676
|
-
mockWsInstance.simulateHello()
|
|
677
|
-
mockWsInstance.simulateReady()
|
|
724
|
+
await startAndReady(listener)
|
|
678
725
|
|
|
679
726
|
mockWsInstance.simulateInvalidSession(true)
|
|
680
727
|
|
|
@@ -690,10 +737,7 @@ describe('DiscordListener', () => {
|
|
|
690
737
|
const client = createMockClient()
|
|
691
738
|
listener = new DiscordListener(client)
|
|
692
739
|
|
|
693
|
-
await listener
|
|
694
|
-
mockWsInstance.simulateOpen()
|
|
695
|
-
mockWsInstance.simulateHello()
|
|
696
|
-
mockWsInstance.simulateReady()
|
|
740
|
+
await startAndReady(listener)
|
|
697
741
|
|
|
698
742
|
mockWsInstance.simulateInvalidSession(false)
|
|
699
743
|
|
|
@@ -716,10 +760,7 @@ describe('DiscordListener', () => {
|
|
|
716
760
|
const client = createMockClient()
|
|
717
761
|
listener = new DiscordListener(client)
|
|
718
762
|
|
|
719
|
-
await listener
|
|
720
|
-
mockWsInstance.simulateOpen()
|
|
721
|
-
mockWsInstance.simulateHello()
|
|
722
|
-
mockWsInstance.simulateReady()
|
|
763
|
+
await startAndReady(listener)
|
|
723
764
|
|
|
724
765
|
const sentBefore = mockWsInstance.sent.length
|
|
725
766
|
mockWsInstance.simulateMessage({ op: 1 })
|
|
@@ -736,10 +777,7 @@ describe('DiscordListener', () => {
|
|
|
736
777
|
const client = createMockClient()
|
|
737
778
|
listener = new DiscordListener(client)
|
|
738
779
|
|
|
739
|
-
await listener
|
|
740
|
-
mockWsInstance.simulateOpen()
|
|
741
|
-
mockWsInstance.simulateHello()
|
|
742
|
-
mockWsInstance.simulateReady()
|
|
780
|
+
await startAndReady(listener)
|
|
743
781
|
|
|
744
782
|
mockWsInstance.simulateDispatch(
|
|
745
783
|
'MESSAGE_CREATE',
|
|
@@ -754,10 +792,7 @@ describe('DiscordListener', () => {
|
|
|
754
792
|
const client = createMockClient()
|
|
755
793
|
listener = new DiscordListener(client)
|
|
756
794
|
|
|
757
|
-
await listener
|
|
758
|
-
mockWsInstance.simulateOpen()
|
|
759
|
-
mockWsInstance.simulateHello()
|
|
760
|
-
mockWsInstance.simulateReady()
|
|
795
|
+
await startAndReady(listener)
|
|
761
796
|
|
|
762
797
|
mockWsInstance.simulateDispatch(
|
|
763
798
|
'MESSAGE_CREATE',
|
|
@@ -781,11 +816,11 @@ describe('DiscordListener', () => {
|
|
|
781
816
|
const client = createMockClient()
|
|
782
817
|
listener = new DiscordListener(client)
|
|
783
818
|
|
|
784
|
-
await listener
|
|
819
|
+
await startConnecting(listener)
|
|
785
820
|
;(listener as any).reconnectAttempts = 5
|
|
786
821
|
listener.stop()
|
|
787
822
|
|
|
788
|
-
await listener
|
|
823
|
+
await startConnecting(listener)
|
|
789
824
|
expect((listener as any).reconnectAttempts).toBe(0)
|
|
790
825
|
})
|
|
791
826
|
})
|
|
@@ -795,10 +830,11 @@ describe('DiscordListener', () => {
|
|
|
795
830
|
const client = createMockClient()
|
|
796
831
|
listener = new DiscordListener(client)
|
|
797
832
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
833
|
+
const started = listener.start()
|
|
834
|
+
const ws = await waitForSocket()
|
|
835
|
+
ws.simulateOpen()
|
|
836
|
+
ws.simulateHello()
|
|
837
|
+
ws.simulateMessage({
|
|
802
838
|
op: 0,
|
|
803
839
|
t: 'READY',
|
|
804
840
|
s: 1,
|
|
@@ -808,6 +844,7 @@ describe('DiscordListener', () => {
|
|
|
808
844
|
user: { id: 'U_SELF', username: 'user' },
|
|
809
845
|
},
|
|
810
846
|
})
|
|
847
|
+
await started
|
|
811
848
|
|
|
812
849
|
mockWsInstance.simulateClose()
|
|
813
850
|
await new Promise((r) => setTimeout(r, 1500))
|
|
@@ -821,10 +858,11 @@ describe('DiscordListener', () => {
|
|
|
821
858
|
const client = createMockClient()
|
|
822
859
|
listener = new DiscordListener(client)
|
|
823
860
|
|
|
824
|
-
|
|
861
|
+
listener.start().catch(() => {})
|
|
862
|
+
const ws = await waitForSocket()
|
|
825
863
|
;(listener as any).reconnectAttempts = 5
|
|
826
864
|
|
|
827
|
-
|
|
865
|
+
ws.simulateOpen()
|
|
828
866
|
|
|
829
867
|
expect((listener as any).reconnectAttempts).toBe(5)
|
|
830
868
|
})
|
|
@@ -833,12 +871,14 @@ describe('DiscordListener', () => {
|
|
|
833
871
|
const client = createMockClient()
|
|
834
872
|
listener = new DiscordListener(client)
|
|
835
873
|
|
|
836
|
-
|
|
874
|
+
const started = listener.start()
|
|
875
|
+
const ws = await waitForSocket()
|
|
837
876
|
;(listener as any).reconnectAttempts = 5
|
|
838
877
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
878
|
+
ws.simulateOpen()
|
|
879
|
+
ws.simulateHello()
|
|
880
|
+
ws.simulateReady()
|
|
881
|
+
await started
|
|
842
882
|
|
|
843
883
|
expect((listener as any).reconnectAttempts).toBe(0)
|
|
844
884
|
})
|