@juzi/wechaty-puppet-service 1.0.52 → 1.0.54

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 (33) hide show
  1. package/dist/cjs/src/client/grpc-manager.d.ts +2 -2
  2. package/dist/cjs/src/client/grpc-manager.d.ts.map +1 -1
  3. package/dist/cjs/src/client/grpc-manager.js +1 -1
  4. package/dist/cjs/src/client/grpc-manager.js.map +1 -1
  5. package/dist/cjs/src/client/puppet-service.d.ts +3 -0
  6. package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
  7. package/dist/cjs/src/client/puppet-service.js +90 -0
  8. package/dist/cjs/src/client/puppet-service.js.map +1 -1
  9. package/dist/cjs/src/package-json.js +1 -1
  10. package/dist/cjs/src/server/event-stream-manager.d.ts.map +1 -1
  11. package/dist/cjs/src/server/event-stream-manager.js +15 -1
  12. package/dist/cjs/src/server/event-stream-manager.js.map +1 -1
  13. package/dist/cjs/tests/grpc-stream.spec.js +47 -0
  14. package/dist/cjs/tests/grpc-stream.spec.js.map +1 -1
  15. package/dist/esm/src/client/grpc-manager.d.ts +2 -2
  16. package/dist/esm/src/client/grpc-manager.d.ts.map +1 -1
  17. package/dist/esm/src/client/grpc-manager.js +1 -1
  18. package/dist/esm/src/client/grpc-manager.js.map +1 -1
  19. package/dist/esm/src/client/puppet-service.d.ts +3 -0
  20. package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
  21. package/dist/esm/src/client/puppet-service.js +90 -0
  22. package/dist/esm/src/client/puppet-service.js.map +1 -1
  23. package/dist/esm/src/package-json.js +1 -1
  24. package/dist/esm/src/server/event-stream-manager.d.ts.map +1 -1
  25. package/dist/esm/src/server/event-stream-manager.js +15 -1
  26. package/dist/esm/src/server/event-stream-manager.js.map +1 -1
  27. package/dist/esm/tests/grpc-stream.spec.js +47 -0
  28. package/dist/esm/tests/grpc-stream.spec.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/client/grpc-manager.ts +3 -3
  31. package/src/client/puppet-service.ts +98 -0
  32. package/src/package-json.ts +1 -1
  33. package/src/server/event-stream-manager.ts +15 -1
@@ -60,6 +60,7 @@ import { GrpcManager } from './grpc-manager.js'
60
60
  import { PayloadStore } from './payload-store.js'
61
61
  import { channelPayloadToPb, channelPbToPayload, postPayloadToPb, urlLinkPbToPayload } from '../utils/pb-payload-helper.js'
62
62
  import type { MessageBroadcastTargets } from '@juzi/wechaty-puppet/dist/esm/src/schemas/message.js'
63
+ import { timeoutPromise } from 'gerror'
63
64
 
64
65
  export type PuppetServiceOptions = PUPPET.PuppetOptions & {
65
66
  authority? : string
@@ -74,6 +75,9 @@ export type PuppetServiceOptions = PUPPET.PuppetOptions & {
74
75
  }
75
76
  }
76
77
 
78
+ const ResetLoginTimeout = 30 * 1000
79
+ const ResetReadyTimeout = 20 * 1000 // normally ready comes 15 seconds after login
80
+
77
81
  class PuppetService extends PUPPET.Puppet {
78
82
 
79
83
  static override readonly VERSION = VERSION
@@ -306,7 +310,15 @@ class PuppetService extends PUPPET.Puppet {
306
310
  break
307
311
  case grpcPuppet.EventType.EVENT_TYPE_LOGIN:
308
312
  {
313
+ if (this.waitingForLogin) {
314
+ log.warn('PuppetService', 'this login event is ignored because the it is expected by event stream reconnect')
315
+ return
316
+ }
309
317
  const loginPayload = JSON.parse(payload) as PUPPET.payloads.EventLogin
318
+ const accountId = await this._payloadStore.miscellaneous.get('accountId')
319
+ if (accountId !== loginPayload.contactId) {
320
+ await this._payloadStore.miscellaneous.delete('eventSeq')
321
+ }
310
322
  await this._payloadStore.miscellaneous.set('accountId', loginPayload.contactId)
311
323
  ;(
312
324
  async () => this.login(loginPayload.contactId)
@@ -346,6 +358,10 @@ class PuppetService extends PUPPET.Puppet {
346
358
  this.emit('post-tap', JSON.parse(payload) as PUPPET.payloads.EventPostTap)
347
359
  break
348
360
  case grpcPuppet.EventType.EVENT_TYPE_READY:
361
+ if (this.waitingForLogin) {
362
+ log.warn('PuppetService', 'this ready event is ignored because the it is expected by event stream reconnect')
363
+ return
364
+ }
349
365
  this.emit('ready', JSON.parse(payload) as PUPPET.payloads.EventReady)
350
366
  break
351
367
  case grpcPuppet.EventType.EVENT_TYPE_ROOM_INVITE:
@@ -2522,6 +2538,88 @@ class PuppetService extends PUPPET.Puppet {
2522
2538
  clearInterval(this.healthCheckInterval!)
2523
2539
  }
2524
2540
 
2541
+ // handle watchdog reset
2542
+ // goal: pain free reset of reconnect within threshold (like 30 seconds?)
2543
+
2544
+ private waitingForLogin = false
2545
+ private waitingForReady = false
2546
+ override async reset (): Promise<void> {
2547
+ if (!this._grpcManager) {
2548
+ log.warn('PuppetService', 'grpc manager not constructed, perform regular reset')
2549
+ return super.reset()
2550
+ }
2551
+
2552
+ if (!this.isLoggedIn) {
2553
+ log.warn('PuppetService', 'puppet not logged in, perform regular reset')
2554
+ return super.reset()
2555
+ }
2556
+
2557
+ this.grpcManager.stopStream()
2558
+ const lastEventTimestamp = await this._payloadStore.miscellaneous.get('eventTimestamp')
2559
+ const lastEventSeq = (Date.now() - Number(lastEventTimestamp || 0)) < this.timeoutMilliseconds ? await this._payloadStore.miscellaneous.get('eventSeq') : undefined
2560
+ const accountId = await this._payloadStore.miscellaneous.get('accountId')
2561
+
2562
+ const onLoginResolve = (resolve: () => void) => {
2563
+ const onLogin = (event: grpcPuppet.EventResponse) => {
2564
+ const type = event.getType()
2565
+ const payload = event.getPayload()
2566
+ if (this.waitingForLogin && type === grpcPuppet.EventType.EVENT_TYPE_LOGIN) {
2567
+ const payloadObj = JSON.parse(payload) as PUPPET.payloads.EventLogin
2568
+ this.waitingForLogin = false
2569
+ if (accountId && payloadObj.contactId !== accountId) {
2570
+ throw new Error('login with a different account, perform regular reset')
2571
+ }
2572
+ resolve()
2573
+ }
2574
+ }
2575
+ return onLogin
2576
+ }
2577
+ const onReadyResolve = (resolve: () => void) => {
2578
+ const onReady = (event: grpcPuppet.EventResponse) => {
2579
+ const type = event.getType()
2580
+ if (this.waitingForReady && type === grpcPuppet.EventType.EVENT_TYPE_READY) {
2581
+ this.waitingForReady = false
2582
+ resolve()
2583
+ }
2584
+ }
2585
+ return onReady
2586
+ }
2587
+
2588
+ let onLogin: ReturnType<typeof onLoginResolve>
2589
+ let onReady: ReturnType<typeof onReadyResolve>
2590
+ const loginFuture = new Promise<void>(resolve => {
2591
+ onLogin = onLoginResolve(resolve)
2592
+ this.grpcManager.on('data', onLogin)
2593
+ })
2594
+ const readyFuture = new Promise<void>(resolve => {
2595
+ onReady = onReadyResolve(resolve)
2596
+ this.grpcManager.on('data', onReady)
2597
+ })
2598
+
2599
+ this.waitingForLogin = true
2600
+ this.waitingForReady = true
2601
+
2602
+ // wait for server to clear stream
2603
+ await new Promise(resolve => {
2604
+ setTimeout(resolve, 5000)
2605
+ })
2606
+
2607
+ await this.grpcManager.startStream(lastEventSeq, accountId)
2608
+ try {
2609
+ await timeoutPromise(loginFuture, ResetLoginTimeout)
2610
+ .finally(() => this.grpcManager.off('data', onLogin))
2611
+ } catch (e) {
2612
+ log.warn('PuppetService', 'waiting for event reset login error, will perform regular reset')
2613
+ return super.reset()
2614
+ }
2615
+ try {
2616
+ await timeoutPromise(readyFuture, ResetReadyTimeout)
2617
+ .finally(() => this.grpcManager.off('data', onReady))
2618
+ } catch (e) {
2619
+ log.warn('PuppetService', 'waiting for event reset ready error, will do nothing')
2620
+ }
2621
+ }
2622
+
2525
2623
  }
2526
2624
 
2527
2625
  export {
@@ -4,7 +4,7 @@
4
4
  import type { PackageJson } from 'type-fest'
5
5
  export const packageJson: PackageJson = {
6
6
  "name": "@juzi/wechaty-puppet-service",
7
- "version": "1.0.52",
7
+ "version": "1.0.54",
8
8
  "description": "Puppet Service for Wechaty",
9
9
  "type": "module",
10
10
  "exports": {
@@ -294,13 +294,27 @@ class EventStreamManager {
294
294
  this.offCallbackList.push(off)
295
295
  break
296
296
  }
297
+ case 'post-comment': {
298
+ const listener = (payload: PUPPET.payloads.EventPostComment) => this.grpcEmit(grpcPuppet.EventType.EVENT_TYPE_POST_COMMENT, payload)
299
+ this.puppet.on('post-comment', listener)
300
+ const off = () => this.puppet.off('post-comment', listener)
301
+ this.offCallbackList.push(off)
302
+ break
303
+ }
304
+ case 'post-tap': {
305
+ const listener = (payload: PUPPET.payloads.EventPostTap) => this.grpcEmit(grpcPuppet.EventType.EVENT_TYPE_POST_TAP, payload)
306
+ this.puppet.on('post-tap', listener)
307
+ const off = () => this.puppet.off('post-tap', listener)
308
+ this.offCallbackList.push(off)
309
+ break
310
+ }
297
311
  case 'reset':
298
312
  // the `reset` event should be dealed internally, should not send out
299
313
  break
300
314
 
301
315
  default:
302
316
  // Huan(202003): in default, the `eventName` type should be `never`, please check.
303
- throw new Error('eventName ' + eventName + ' unsupported!')
317
+ log.warn('eventName ' + eventName + ' unsupported!')
304
318
  }
305
319
  }
306
320