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.
Files changed (43) hide show
  1. package/.claude-plugin/README.md +16 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +27 -0
  4. package/dist/package.json +1 -1
  5. package/dist/src/platforms/discord/listener.d.ts +9 -4
  6. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  7. package/dist/src/platforms/discord/listener.js +110 -16
  8. package/dist/src/platforms/discord/listener.js.map +1 -1
  9. package/dist/src/platforms/discordbot/listener.d.ts +6 -0
  10. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
  11. package/dist/src/platforms/discordbot/listener.js +78 -2
  12. package/dist/src/platforms/discordbot/listener.js.map +1 -1
  13. package/dist/src/platforms/slack/qr-http-login.d.ts.map +1 -1
  14. package/dist/src/platforms/slack/qr-http-login.js +105 -6
  15. package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
  16. package/docs/content/docs/sdk/discord.mdx +1 -9
  17. package/e2e/README.md +3 -1
  18. package/package.json +1 -1
  19. package/skills/agent-channeltalk/SKILL.md +1 -1
  20. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  21. package/skills/agent-discord/SKILL.md +37 -8
  22. package/skills/agent-discord/references/authentication.md +46 -8
  23. package/skills/agent-discordbot/SKILL.md +1 -1
  24. package/skills/agent-instagram/SKILL.md +1 -1
  25. package/skills/agent-kakaotalk/SKILL.md +1 -1
  26. package/skills/agent-line/SKILL.md +1 -1
  27. package/skills/agent-slack/SKILL.md +7 -4
  28. package/skills/agent-slack/references/authentication.md +8 -3
  29. package/skills/agent-slackbot/SKILL.md +1 -1
  30. package/skills/agent-teams/SKILL.md +1 -1
  31. package/skills/agent-telegram/SKILL.md +1 -1
  32. package/skills/agent-telegrambot/SKILL.md +1 -1
  33. package/skills/agent-webex/SKILL.md +1 -1
  34. package/skills/agent-webexbot/SKILL.md +1 -1
  35. package/skills/agent-wechatbot/SKILL.md +1 -1
  36. package/skills/agent-whatsapp/SKILL.md +1 -1
  37. package/skills/agent-whatsappbot/SKILL.md +1 -1
  38. package/src/platforms/discord/listener.test.ts +187 -147
  39. package/src/platforms/discord/listener.ts +122 -18
  40. package/src/platforms/discordbot/listener.test.ts +186 -184
  41. package/src/platforms/discordbot/listener.ts +83 -2
  42. package/src/platforms/slack/qr-http-login.test.ts +47 -0
  43. 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.start()
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.start()
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.start()
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.start()
164
- mockWsInstance.simulateOpen()
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 custom intents', async () => {
253
+ it('sends a user-account Identify with the load-bearing default shape', async () => {
175
254
  const client = createMockClient()
176
- const customIntents = 1 << 9
177
- listener = new DiscordListener(client, { intents: customIntents })
255
+ listener = new DiscordListener(client)
178
256
 
179
- await listener.start()
180
- mockWsInstance.simulateOpen()
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.intents).toBe(customIntents)
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.start()
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.start()
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.start()
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.start()
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.start()
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.start()
324
- mockWsInstance.simulateOpen()
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.start()
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.start()
366
- mockWsInstance.simulateOpen()
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.start()
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.start()
402
- mockWsInstance.simulateOpen()
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('emits error and reconnects on gatewayConnect failure', async () => {
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
- const errors: Error[] = []
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(errors.length).toBe(1)
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.start()
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.start()
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.start()
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.start()
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.start()
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.start()
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
- it('emits error and stops on code 4004', async () => {
573
- const client = createMockClient()
574
- listener = new DiscordListener(client)
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
- const errors: Error[] = []
577
- listener.on('error', (err) => errors.push(err))
616
+ const started = listener.start()
617
+ const ws = await waitForSocket()
618
+ ws.simulateOpen()
619
+ ws.simulateClose(code)
578
620
 
579
- await listener.start()
580
- mockWsInstance.simulateOpen()
581
- mockWsInstance.simulateClose(4004)
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
- await new Promise((r) => setTimeout(r, 50))
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
- expect(errors.length).toBe(1)
586
- expect(errors[0].message).toContain('4004')
587
- expect(client.gatewayConnect).toHaveBeenCalledTimes(1)
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.start()
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.start()
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
- await listener.start()
647
- mockWsInstance.simulateOpen()
648
- mockWsInstance.simulateHello(50)
649
- mockWsInstance.simulateReady()
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.start()
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.start()
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.start()
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.start()
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.start()
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.start()
819
+ await startConnecting(listener)
785
820
  ;(listener as any).reconnectAttempts = 5
786
821
  listener.stop()
787
822
 
788
- await listener.start()
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
- await listener.start()
799
- mockWsInstance.simulateOpen()
800
- mockWsInstance.simulateHello()
801
- mockWsInstance.simulateMessage({
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
- await listener.start()
861
+ listener.start().catch(() => {})
862
+ const ws = await waitForSocket()
825
863
  ;(listener as any).reconnectAttempts = 5
826
864
 
827
- mockWsInstance.simulateOpen()
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
- await listener.start()
874
+ const started = listener.start()
875
+ const ws = await waitForSocket()
837
876
  ;(listener as any).reconnectAttempts = 5
838
877
 
839
- mockWsInstance.simulateOpen()
840
- mockWsInstance.simulateHello()
841
- mockWsInstance.simulateReady()
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
  })