@juzi/wechaty 1.0.146 → 1.0.148

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 (131) hide show
  1. package/dist/cjs/src/mods/impls.d.ts +2 -2
  2. package/dist/cjs/src/mods/impls.d.ts.map +1 -1
  3. package/dist/cjs/src/mods/impls.js +2 -1
  4. package/dist/cjs/src/mods/impls.js.map +1 -1
  5. package/dist/cjs/src/mods/users.d.ts +2 -1
  6. package/dist/cjs/src/mods/users.d.ts.map +1 -1
  7. package/dist/cjs/src/package-json.js +4 -4
  8. package/dist/cjs/src/package-json.js.map +1 -1
  9. package/dist/cjs/src/schemas/call-events.d.ts +20 -0
  10. package/dist/cjs/src/schemas/call-events.d.ts.map +1 -0
  11. package/dist/cjs/src/schemas/call-events.js +7 -0
  12. package/dist/cjs/src/schemas/call-events.js.map +1 -0
  13. package/dist/cjs/src/schemas/mod.d.ts +3 -2
  14. package/dist/cjs/src/schemas/mod.d.ts.map +1 -1
  15. package/dist/cjs/src/schemas/mod.js +3 -1
  16. package/dist/cjs/src/schemas/mod.js.map +1 -1
  17. package/dist/cjs/src/schemas/wechaty-events.d.ts +23 -2
  18. package/dist/cjs/src/schemas/wechaty-events.d.ts.map +1 -1
  19. package/dist/cjs/src/schemas/wechaty-events.js +6 -0
  20. package/dist/cjs/src/schemas/wechaty-events.js.map +1 -1
  21. package/dist/cjs/src/user-modules/call.d.ts +158 -0
  22. package/dist/cjs/src/user-modules/call.d.ts.map +1 -1
  23. package/dist/cjs/src/user-modules/call.js +289 -1
  24. package/dist/cjs/src/user-modules/call.js.map +1 -1
  25. package/dist/cjs/src/user-modules/call.spec.d.ts +7 -0
  26. package/dist/cjs/src/user-modules/call.spec.d.ts.map +1 -0
  27. package/dist/cjs/src/user-modules/call.spec.js +759 -0
  28. package/dist/cjs/src/user-modules/call.spec.js.map +1 -0
  29. package/dist/cjs/src/user-modules/contact.d.ts +16 -0
  30. package/dist/cjs/src/user-modules/contact.d.ts.map +1 -1
  31. package/dist/cjs/src/user-modules/contact.js +16 -0
  32. package/dist/cjs/src/user-modules/contact.js.map +1 -1
  33. package/dist/cjs/src/user-modules/mod.d.ts +5 -4
  34. package/dist/cjs/src/user-modules/mod.d.ts.map +1 -1
  35. package/dist/cjs/src/user-modules/mod.js +2 -1
  36. package/dist/cjs/src/user-modules/mod.js.map +1 -1
  37. package/dist/cjs/src/wechaty/wechaty-base.d.ts +66 -7
  38. package/dist/cjs/src/wechaty/wechaty-base.d.ts.map +1 -1
  39. package/dist/cjs/src/wechaty/wechaty-base.js +28 -0
  40. package/dist/cjs/src/wechaty/wechaty-base.js.map +1 -1
  41. package/dist/cjs/src/wechaty/wechaty-impl.spec.js +10 -8
  42. package/dist/cjs/src/wechaty/wechaty-impl.spec.js.map +1 -1
  43. package/dist/cjs/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  44. package/dist/cjs/src/wechaty-mixins/io-mixin.d.ts +2 -2
  45. package/dist/cjs/src/wechaty-mixins/login-mixin.d.ts +15 -5
  46. package/dist/cjs/src/wechaty-mixins/login-mixin.d.ts.map +1 -1
  47. package/dist/cjs/src/wechaty-mixins/misc-mixin.d.ts +15 -5
  48. package/dist/cjs/src/wechaty-mixins/misc-mixin.d.ts.map +1 -1
  49. package/dist/cjs/src/wechaty-mixins/plugin-mixin.d.ts +32 -6
  50. package/dist/cjs/src/wechaty-mixins/plugin-mixin.d.ts.map +1 -1
  51. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts +39 -5
  52. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  53. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js +149 -0
  54. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  55. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +5 -3
  56. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.d.ts.map +1 -1
  57. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.js +3 -0
  58. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.js.map +1 -1
  59. package/dist/esm/src/mods/impls.d.ts +2 -2
  60. package/dist/esm/src/mods/impls.d.ts.map +1 -1
  61. package/dist/esm/src/mods/impls.js +1 -1
  62. package/dist/esm/src/mods/impls.js.map +1 -1
  63. package/dist/esm/src/mods/users.d.ts +2 -1
  64. package/dist/esm/src/mods/users.d.ts.map +1 -1
  65. package/dist/esm/src/package-json.js +4 -4
  66. package/dist/esm/src/package-json.js.map +1 -1
  67. package/dist/esm/src/schemas/call-events.d.ts +20 -0
  68. package/dist/esm/src/schemas/call-events.d.ts.map +1 -0
  69. package/dist/esm/src/schemas/call-events.js +4 -0
  70. package/dist/esm/src/schemas/call-events.js.map +1 -0
  71. package/dist/esm/src/schemas/mod.d.ts +3 -2
  72. package/dist/esm/src/schemas/mod.d.ts.map +1 -1
  73. package/dist/esm/src/schemas/mod.js +2 -1
  74. package/dist/esm/src/schemas/mod.js.map +1 -1
  75. package/dist/esm/src/schemas/wechaty-events.d.ts +23 -2
  76. package/dist/esm/src/schemas/wechaty-events.d.ts.map +1 -1
  77. package/dist/esm/src/schemas/wechaty-events.js +6 -0
  78. package/dist/esm/src/schemas/wechaty-events.js.map +1 -1
  79. package/dist/esm/src/user-modules/call.d.ts +158 -0
  80. package/dist/esm/src/user-modules/call.d.ts.map +1 -1
  81. package/dist/esm/src/user-modules/call.js +289 -1
  82. package/dist/esm/src/user-modules/call.js.map +1 -1
  83. package/dist/esm/src/user-modules/call.spec.d.ts +7 -0
  84. package/dist/esm/src/user-modules/call.spec.d.ts.map +1 -0
  85. package/dist/esm/src/user-modules/call.spec.js +734 -0
  86. package/dist/esm/src/user-modules/call.spec.js.map +1 -0
  87. package/dist/esm/src/user-modules/contact.d.ts +16 -0
  88. package/dist/esm/src/user-modules/contact.d.ts.map +1 -1
  89. package/dist/esm/src/user-modules/contact.js +16 -0
  90. package/dist/esm/src/user-modules/contact.js.map +1 -1
  91. package/dist/esm/src/user-modules/mod.d.ts +5 -4
  92. package/dist/esm/src/user-modules/mod.d.ts.map +1 -1
  93. package/dist/esm/src/user-modules/mod.js +2 -2
  94. package/dist/esm/src/user-modules/mod.js.map +1 -1
  95. package/dist/esm/src/wechaty/wechaty-base.d.ts +66 -7
  96. package/dist/esm/src/wechaty/wechaty-base.d.ts.map +1 -1
  97. package/dist/esm/src/wechaty/wechaty-base.js +28 -0
  98. package/dist/esm/src/wechaty/wechaty-base.js.map +1 -1
  99. package/dist/esm/src/wechaty/wechaty-impl.spec.js +10 -8
  100. package/dist/esm/src/wechaty/wechaty-impl.spec.js.map +1 -1
  101. package/dist/esm/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  102. package/dist/esm/src/wechaty-mixins/io-mixin.d.ts +2 -2
  103. package/dist/esm/src/wechaty-mixins/login-mixin.d.ts +15 -5
  104. package/dist/esm/src/wechaty-mixins/login-mixin.d.ts.map +1 -1
  105. package/dist/esm/src/wechaty-mixins/misc-mixin.d.ts +15 -5
  106. package/dist/esm/src/wechaty-mixins/misc-mixin.d.ts.map +1 -1
  107. package/dist/esm/src/wechaty-mixins/plugin-mixin.d.ts +32 -6
  108. package/dist/esm/src/wechaty-mixins/plugin-mixin.d.ts.map +1 -1
  109. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts +39 -5
  110. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  111. package/dist/esm/src/wechaty-mixins/puppet-mixin.js +149 -0
  112. package/dist/esm/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  113. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +5 -3
  114. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.d.ts.map +1 -1
  115. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.js +4 -1
  116. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.js.map +1 -1
  117. package/package.json +3 -3
  118. package/src/mods/impls.ts +2 -0
  119. package/src/mods/users.ts +6 -0
  120. package/src/package-json.ts +4 -4
  121. package/src/schemas/call-events.ts +35 -0
  122. package/src/schemas/mod.ts +6 -0
  123. package/src/schemas/wechaty-events.ts +28 -0
  124. package/src/user-modules/call.spec.ts +929 -0
  125. package/src/user-modules/call.ts +373 -0
  126. package/src/user-modules/contact.ts +18 -0
  127. package/src/user-modules/mod.ts +11 -0
  128. package/src/wechaty/wechaty-base.ts +40 -1
  129. package/src/wechaty/wechaty-impl.spec.ts +4 -0
  130. package/src/wechaty-mixins/puppet-mixin.ts +184 -0
  131. package/src/wechaty-mixins/wechatify-user-module-mixin.ts +6 -0
@@ -15,6 +15,7 @@ import {
15
15
  import { config, PUPPET_PAYLOAD_SYNC_GAP, PUPPET_PAYLOAD_SYNC_MAX_RETRY } from '../config.js'
16
16
  import { timestampToDate } from '../pure-functions/timestamp-to-date.js'
17
17
  import type {
18
+ CallImpl,
18
19
  ContactImpl,
19
20
  ContactInterface,
20
21
  MessageImpl,
@@ -51,6 +52,9 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
51
52
 
52
53
  __puppet?: PUPPET.impls.PuppetInterface
53
54
 
55
+ /** Registry of active Call instances keyed by callId. */
56
+ __callPool: Map<string, CallImpl> = new Map()
57
+
54
58
  get puppet (): PUPPET.impls.PuppetInterface {
55
59
  if (!this.__puppet) {
56
60
 
@@ -148,6 +152,8 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
148
152
  log.verbose('WechatyPuppetMixin', 'stop() super.stop() ...')
149
153
  await super.stop()
150
154
  log.verbose('WechatyPuppetMixin', 'stop() super.stop() ... done')
155
+
156
+ this.__callPool.clear()
151
157
  }
152
158
 
153
159
  async ready (): Promise<void> {
@@ -745,6 +751,33 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
745
751
  await order?.ready(true)
746
752
  break
747
753
  }
754
+ case PUPPET.types.Payload.Call: {
755
+ /**
756
+ * Invalidate the puppet-side cache so the next callPayload()
757
+ * pull reflects the latest server state, then refresh the
758
+ * user-layer Call payload so getters (media / participants /
759
+ * endTime) reflect the new view immediately. Whether the
760
+ * call is still alive is still decided by the call-event
761
+ * handler via __finalizeIfEnded; we do not touch the pool
762
+ * here, and we do not emit a synthetic user event — the
763
+ * UI is expected to read getters when it needs the value.
764
+ */
765
+ await this.puppet.callPayloadDirty(payloadId)
766
+ const call = this.__callPool.get(payloadId)
767
+ if (call) {
768
+ try {
769
+ await call.ready(true)
770
+ } catch (e) {
771
+ log.warn(
772
+ 'WechatyPuppetMixin',
773
+ 'dirty(Call) ready failed for callId=%s: %s',
774
+ payloadId,
775
+ (e as Error).message,
776
+ )
777
+ }
778
+ }
779
+ break
780
+ }
748
781
 
749
782
  case PUPPET.types.Payload.Unspecified:
750
783
  default:
@@ -791,6 +824,72 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
791
824
  })
792
825
  break
793
826
 
827
+ case 'call':
828
+ puppet.on('call', async payload => {
829
+ try {
830
+ if (payload.signal === PUPPET.types.CallSignal.Invite) {
831
+ if (this.__callPool.has(payload.callId)) {
832
+ log.warn('WechatyPuppetMixin', '__setupPuppetEvents() duplicate Invite for callId=%s, ignoring', payload.callId)
833
+ return
834
+ }
835
+ const call = new (this.Call as any)({
836
+ id : payload.callId,
837
+ direction : 'incoming' as const,
838
+ }) as CallImpl
839
+ await call.ready()
840
+ this.__callPool.set(call.id, call)
841
+ this.emit('call', call as any)
842
+ return
843
+ }
844
+
845
+ const call = this.__callPool.get(payload.callId)
846
+ if (!call) {
847
+ // Under the protocol-mints-id model, signals for an unknown callId
848
+ // are expected within race windows (e.g. a Ringing ack arriving
849
+ // before callInvite returns) or after the Call has been finalized
850
+ // and removed from the pool. Drop silently — log.warn, not error.
851
+ log.warn('WechatyPuppetMixin', '__setupPuppetEvents() call event for unknown callId=%s, dropping', payload.callId)
852
+ return
853
+ }
854
+
855
+ const actor = await (this.Contact as typeof ContactImpl).find({ id: payload.contactId })
856
+ if (!actor) {
857
+ log.warn('WechatyPuppetMixin', '__setupPuppetEvents() actor contact not found for callId=%s contactId=%s, dropping', payload.callId, payload.contactId)
858
+ return
859
+ }
860
+
861
+ call.__handleSignal(payload.signal, actor, payload.reason)
862
+
863
+ switch (payload.signal) {
864
+ case PUPPET.types.CallSignal.Ringing:
865
+ this.emit('call-ringing', call as any)
866
+ break
867
+ case PUPPET.types.CallSignal.Accept:
868
+ this.emit('call-accept', call as any, actor as any)
869
+ break
870
+ case PUPPET.types.CallSignal.Reject:
871
+ this.emit('call-reject', call as any, actor as any, payload.reason)
872
+ await this.__finalizeIfEnded(call)
873
+ break
874
+ case PUPPET.types.CallSignal.Cancel:
875
+ this.emit('call-cancel', call as any, payload.reason)
876
+ // Cancel is a peer-side terminal action on the receiving side:
877
+ // the caller has withdrawn, so the local call necessarily ends.
878
+ await this.__finalizeIfEnded(call, /* force */ true)
879
+ break
880
+ case PUPPET.types.CallSignal.Hangup:
881
+ this.emit('call-hangup', call as any, actor as any, payload.reason)
882
+ await this.__finalizeIfEnded(call)
883
+ break
884
+ default:
885
+ break
886
+ }
887
+ } catch (e) {
888
+ this.emit('error', GError.from(e))
889
+ }
890
+ })
891
+ break
892
+
794
893
  default:
795
894
  /**
796
895
  * Check: The eventName here should have the type `never`
@@ -803,6 +902,89 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
803
902
  log.verbose('WechatyPuppetMixin', '__setupPuppetEvents() ... done')
804
903
  }
805
904
 
905
+ /**
906
+ * Decide whether a call has reached its terminal state and, if so, drive
907
+ * the local lifecycle to ended by invoking {@link CallImpl.__markEnded},
908
+ * which routes through __finalize and is responsible for emitting both
909
+ * the object 'ended' and the bot 'call-ended' lifecycle events as well
910
+ * as evicting the call from the pool. This method does NOT emit
911
+ * 'call-ended' directly — that would risk a double-emit if a local
912
+ * control path (reject/cancel/hangup) finalizes the call before the
913
+ * puppet echo reaches here. The `__endedEmitted` sentinel inside
914
+ * __finalize makes the joint behavior idempotent.
915
+ *
916
+ * Strategy:
917
+ * - `force=true` short-circuits when the caller already knows the call is
918
+ * over (Cancel from the receiver's perspective).
919
+ * - Otherwise, refresh the payload via dirty + ready and check
920
+ * `endTime`, with a short retry budget so transient puppet hiccups do
921
+ * not strand a Call in the pool.
922
+ * - As a last-resort heuristic, an ended-leaning signal (Reject / Hangup)
923
+ * on a strict 1v1 call (participants.length === 2: the starter plus
924
+ * exactly one invitee) is treated as terminal even if sync never
925
+ * recovered: in 1v1 the protocol has no other peer to keep the
926
+ * session alive. Other roster shapes (length 0/1/3+) fall through
927
+ * and wait for the next dirty signal — length<=1 is a stranded-but-
928
+ * recoverable shape (empty roster from a sync hiccup, or a multi-
929
+ * party call whose other members already left).
930
+ */
931
+ async __finalizeIfEnded (call: CallImpl, force = false): Promise<void> {
932
+ let ended = force
933
+
934
+ if (!ended) {
935
+ for (let attempt = 0; attempt < 3 && !ended; attempt++) {
936
+ try {
937
+ await call.sync()
938
+ ended = !!call.endTime()
939
+ } catch (e) {
940
+ log.warn(
941
+ 'WechatyPuppetMixin',
942
+ '__finalizeIfEnded() sync attempt %d failed for callId=%s: %s',
943
+ attempt + 1,
944
+ call.id,
945
+ (e as Error).message,
946
+ )
947
+ }
948
+ if (!ended && attempt < 2) {
949
+ await new Promise(resolve => setTimeout(resolve, 500))
950
+ }
951
+ }
952
+ }
953
+
954
+ if (!ended) {
955
+ try {
956
+ const participants = await call.participants()
957
+ if (participants.length === 2) {
958
+ log.warn(
959
+ 'WechatyPuppetMixin',
960
+ '__finalizeIfEnded() 1v1 fallback after sync failure for callId=%s (rosterLength=%d)',
961
+ call.id,
962
+ participants.length,
963
+ )
964
+ ended = true
965
+ } else {
966
+ log.error(
967
+ 'WechatyPuppetMixin',
968
+ '__finalizeIfEnded() sync failed, leaving callId=%s in pool for next dirty signal (rosterLength=%d)',
969
+ call.id,
970
+ participants.length,
971
+ )
972
+ }
973
+ } catch (e) {
974
+ log.error(
975
+ 'WechatyPuppetMixin',
976
+ '__finalizeIfEnded() participants lookup failed for callId=%s: %s',
977
+ call.id,
978
+ (e as Error).message,
979
+ )
980
+ }
981
+ }
982
+
983
+ if (ended) {
984
+ call.__markEnded()
985
+ }
986
+ }
987
+
806
988
  }
807
989
 
808
990
  return PuppetMixin
@@ -812,8 +994,10 @@ type PuppetMixin = ReturnType<typeof puppetMixin>
812
994
 
813
995
  type ProtectedPropertyPuppetMixin =
814
996
  | '__puppet'
997
+ | '__callPool'
815
998
  | '__readyState'
816
999
  | '__setupPuppetEvents'
1000
+ | '__finalizeIfEnded'
817
1001
  | '__loginIndicator'
818
1002
 
819
1003
  export type {
@@ -1,6 +1,7 @@
1
1
  import { log } from '@juzi/wechaty-puppet'
2
2
 
3
3
  import {
4
+ CallImpl,
4
5
  ContactImpl,
5
6
  ContactSelfImpl,
6
7
  DelayImpl,
@@ -28,6 +29,7 @@ import {
28
29
  WxxdProductImpl,
29
30
  WxxdOrderImpl,
30
31
 
32
+ CallConstructor,
31
33
  ContactConstructor,
32
34
  ContactSelfConstructor,
33
35
  DelayConstructor,
@@ -71,6 +73,7 @@ const wechatifyUserModuleMixin = <MixinBase extends typeof WechatySkeleton> (mix
71
73
  super(...args)
72
74
  }
73
75
 
76
+ __wechatifiedCall? : CallConstructor
74
77
  __wechatifiedContact? : ContactConstructor
75
78
  __wechatifiedContactSelf? : ContactSelfConstructor
76
79
  __wechatifiedDelay? : DelayConstructor
@@ -98,6 +101,7 @@ const wechatifyUserModuleMixin = <MixinBase extends typeof WechatySkeleton> (mix
98
101
  __wechatifiedWxxdProduct? : WxxdProductConstructor
99
102
  __wechatifiedWxxdOrder? : WxxdOrderConstructor
100
103
 
104
+ get Call () : CallConstructor { return guardWechatify(this.__wechatifiedCall) }
101
105
  get Contact () : ContactConstructor { return guardWechatify(this.__wechatifiedContact) }
102
106
  get ContactSelf () : ContactSelfConstructor { return guardWechatify(this.__wechatifiedContactSelf) }
103
107
  get Delay () : DelayConstructor { return guardWechatify(this.__wechatifiedDelay) }
@@ -145,6 +149,7 @@ const wechatifyUserModuleMixin = <MixinBase extends typeof WechatySkeleton> (mix
145
149
  *
146
150
  * Huan(202110): FIXME: remove any
147
151
  */
152
+ this.__wechatifiedCall = wechatifyUserModule(CallImpl)(this as any)
148
153
  this.__wechatifiedContact = wechatifyUserModule(ContactImpl)(this as any)
149
154
  this.__wechatifiedContactSelf = wechatifyUserModule(ContactSelfImpl)(this as any)
150
155
  this.__wechatifiedDelay = wechatifyUserModule(DelayImpl)(this as any)
@@ -193,6 +198,7 @@ function guardWechatify<T extends Function> (userModule?: T): T {
193
198
  type WechatifyUserModuleMixin = ReturnType<typeof wechatifyUserModuleMixin>
194
199
 
195
200
  type ProtectedPropertyWechatifyUserModuleMixin =
201
+ | '__wechatifiedCall'
196
202
  | '__wechatifiedContact'
197
203
  | '__wechatifiedContactSelf'
198
204
  | '__wechatifiedDelay'