@juzi/wechaty 1.0.155 → 1.0.157

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 (62) hide show
  1. package/dist/cjs/src/package-json.js +4 -4
  2. package/dist/cjs/src/user-modules/call.spec.js +5 -1
  3. package/dist/cjs/src/user-modules/call.spec.js.map +1 -1
  4. package/dist/cjs/src/user-modules/contact-alias-polling.spec.d.ts +3 -0
  5. package/dist/cjs/src/user-modules/contact-alias-polling.spec.d.ts.map +1 -0
  6. package/dist/cjs/src/user-modules/contact-alias-polling.spec.js +58 -0
  7. package/dist/cjs/src/user-modules/contact-alias-polling.spec.js.map +1 -0
  8. package/dist/cjs/src/user-modules/contact.d.ts +24 -0
  9. package/dist/cjs/src/user-modules/contact.d.ts.map +1 -1
  10. package/dist/cjs/src/user-modules/contact.js +69 -5
  11. package/dist/cjs/src/user-modules/contact.js.map +1 -1
  12. package/dist/cjs/src/user-modules/contact.spec.js +156 -0
  13. package/dist/cjs/src/user-modules/contact.spec.js.map +1 -1
  14. package/dist/cjs/src/user-modules/room.d.ts +23 -0
  15. package/dist/cjs/src/user-modules/room.d.ts.map +1 -1
  16. package/dist/cjs/src/user-modules/room.js +52 -0
  17. package/dist/cjs/src/user-modules/room.js.map +1 -1
  18. package/dist/cjs/src/user-modules/room.spec.js +156 -0
  19. package/dist/cjs/src/user-modules/room.spec.js.map +1 -1
  20. package/dist/cjs/src/wechaty-mixins/puppet-mixin-dirty-call.spec.d.ts +3 -0
  21. package/dist/cjs/src/wechaty-mixins/puppet-mixin-dirty-call.spec.d.ts.map +1 -0
  22. package/dist/cjs/src/wechaty-mixins/puppet-mixin-dirty-call.spec.js +68 -0
  23. package/dist/cjs/src/wechaty-mixins/puppet-mixin-dirty-call.spec.js.map +1 -0
  24. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  25. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js +14 -9
  26. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  27. package/dist/esm/src/package-json.js +4 -4
  28. package/dist/esm/src/user-modules/call.spec.js +5 -1
  29. package/dist/esm/src/user-modules/call.spec.js.map +1 -1
  30. package/dist/esm/src/user-modules/contact-alias-polling.spec.d.ts +3 -0
  31. package/dist/esm/src/user-modules/contact-alias-polling.spec.d.ts.map +1 -0
  32. package/dist/esm/src/user-modules/contact-alias-polling.spec.js +56 -0
  33. package/dist/esm/src/user-modules/contact-alias-polling.spec.js.map +1 -0
  34. package/dist/esm/src/user-modules/contact.d.ts +24 -0
  35. package/dist/esm/src/user-modules/contact.d.ts.map +1 -1
  36. package/dist/esm/src/user-modules/contact.js +69 -5
  37. package/dist/esm/src/user-modules/contact.js.map +1 -1
  38. package/dist/esm/src/user-modules/contact.spec.js +157 -1
  39. package/dist/esm/src/user-modules/contact.spec.js.map +1 -1
  40. package/dist/esm/src/user-modules/room.d.ts +23 -0
  41. package/dist/esm/src/user-modules/room.d.ts.map +1 -1
  42. package/dist/esm/src/user-modules/room.js +52 -0
  43. package/dist/esm/src/user-modules/room.js.map +1 -1
  44. package/dist/esm/src/user-modules/room.spec.js +156 -0
  45. package/dist/esm/src/user-modules/room.spec.js.map +1 -1
  46. package/dist/esm/src/wechaty-mixins/puppet-mixin-dirty-call.spec.d.ts +3 -0
  47. package/dist/esm/src/wechaty-mixins/puppet-mixin-dirty-call.spec.d.ts.map +1 -0
  48. package/dist/esm/src/wechaty-mixins/puppet-mixin-dirty-call.spec.js +43 -0
  49. package/dist/esm/src/wechaty-mixins/puppet-mixin-dirty-call.spec.js.map +1 -0
  50. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  51. package/dist/esm/src/wechaty-mixins/puppet-mixin.js +14 -9
  52. package/dist/esm/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  53. package/package.json +3 -3
  54. package/src/package-json.ts +4 -4
  55. package/src/user-modules/call.spec.ts +5 -1
  56. package/src/user-modules/contact-alias-polling.spec.ts +70 -0
  57. package/src/user-modules/contact.spec.ts +212 -0
  58. package/src/user-modules/contact.ts +81 -5
  59. package/src/user-modules/room.spec.ts +210 -0
  60. package/src/user-modules/room.ts +63 -0
  61. package/src/wechaty-mixins/puppet-mixin-dirty-call.spec.ts +55 -0
  62. package/src/wechaty-mixins/puppet-mixin.ts +14 -9
@@ -268,6 +268,69 @@ class RoomMixin extends MixinBase implements SayableSayer {
268
268
  return undefined
269
269
  }
270
270
 
271
+ /**
272
+ * Async iterator variant of {@link findAll} that yields rooms in batches.
273
+ *
274
+ * Each batch is materialised with a single `puppet.batchRoomPayload` RPC,
275
+ * so iterating N rooms costs roughly `ceil(N / batch)` round trips instead
276
+ * of N. Uses `puppet.batchRoomPayload` directly (required on the puppet
277
+ * abstract), so callers do not need a per-id fallback path. Callers may
278
+ * `break` between batches to short-circuit.
279
+ *
280
+ * @static
281
+ * @param {PUPPET.filters.Room} [query]
282
+ * @param {{ batch?: number }} [opts] batch size, default 100
283
+ * @returns {AsyncIterable<RoomInterface[]>}
284
+ * @example
285
+ * for await (const chunk of bot.Room.findAllIter()) {
286
+ * for (const room of chunk) {
287
+ * console.log(await room.topic())
288
+ * }
289
+ * }
290
+ */
291
+ static async * findAllIter (
292
+ query? : PUPPET.filters.Room,
293
+ opts? : { batch?: number },
294
+ ): AsyncIterable<RoomInterface[]> {
295
+ const batch = opts?.batch ?? 100
296
+ log.verbose('Room', 'findAllIter(%s, batch=%d)', JSON.stringify(query, stringifyFilter) || '', batch)
297
+
298
+ if (batch <= 0) {
299
+ throw new Error(`Room.findAllIter() batch must be positive, got ${batch}`)
300
+ }
301
+
302
+ const roomIdList: string[] = await this.wechaty.puppet.roomSearch(query)
303
+
304
+ if (!this.wechaty.isLoggedIn) {
305
+ throw new Error('Room.findAllIter() requires logged in')
306
+ }
307
+
308
+ for (let i = 0; i < roomIdList.length; i += batch) {
309
+ const idChunk = roomIdList.slice(i, i + batch)
310
+
311
+ const payloadMap = await this.wechaty.puppet.batchRoomPayload(idChunk)
312
+
313
+ const roomChunk: RoomInterface[] = []
314
+ for (const id of idChunk) {
315
+ const payload = payloadMap.get(id)
316
+ if (!payload) {
317
+ log.silly('Room', 'findAllIter() payload missing for id=%s, skip', id)
318
+ continue
319
+ }
320
+
321
+ const room = (this.wechaty.Room as any as typeof RoomImpl).load(id)
322
+ room.payload = payload
323
+ roomChunk.push(room)
324
+ }
325
+
326
+ if (roomChunk.length > 0) {
327
+ yield roomChunk
328
+ } else {
329
+ log.warn('Room', 'findAllIter() batch all missing from payloadMap, idChunk=%j', idChunk)
330
+ }
331
+ }
332
+ }
333
+
271
334
  static async batchLoadRooms (roomIdList: string[]) {
272
335
  let continuousErrorCount = 0
273
336
  let totalErrorCount = 0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ import { test } from 'tstest'
4
+ import * as PUPPET from '@juzi/wechaty-puppet'
5
+ import { PuppetMock } from '@juzi/wechaty-puppet-mock'
6
+
7
+ import { WechatyBuilder } from '../wechaty-builder.js'
8
+
9
+ /**
10
+ * Regression: the dirty event listener for `Payload.Call` in `puppet-mixin`
11
+ * used to do `await this.puppet.callPayloadDirty(payloadId)` inside the
12
+ * handler itself.
13
+ *
14
+ * Under wechaty-puppet-service, `callPayloadDirty` fires a gRPC
15
+ * `DirtyPayload` to the server, which then re-emits `dirty` back to the
16
+ * client through the event stream. That re-emit triggers the handler
17
+ * again, forming an infinite client⇄server feedback loop.
18
+ *
19
+ * All other dirty branches (Contact / Room / Message / Wxxd*) only call
20
+ * `find(...).ready(true)`; the Call branch was the outlier.
21
+ *
22
+ * The fix removes the in-handler `callPayloadDirty` call so the branch
23
+ * matches the rest of the template. This test pins that contract:
24
+ * emitting a Call-typed dirty event must NOT cause the handler to call
25
+ * `puppet.callPayloadDirty` again.
26
+ */
27
+ test('dirty(Call) handler must NOT call callPayloadDirty (would form a gRPC loop)', async t => {
28
+ const puppet = new PuppetMock() as any
29
+ const wechaty = WechatyBuilder.build({ puppet })
30
+
31
+ await wechaty.start()
32
+
33
+ let dirtyCallCount = 0
34
+ const originalCallPayloadDirty = puppet.callPayloadDirty.bind(puppet)
35
+ puppet.callPayloadDirty = async (id: string) => {
36
+ dirtyCallCount++
37
+ return originalCallPayloadDirty(id)
38
+ }
39
+
40
+ puppet.emit('dirty', {
41
+ payloadType: PUPPET.types.Payload.Call,
42
+ payloadId: 'call-id-x',
43
+ })
44
+
45
+ // Allow the async handler to run.
46
+ await new Promise(resolve => setTimeout(resolve, 50))
47
+
48
+ t.equal(
49
+ dirtyCallCount,
50
+ 0,
51
+ `dirty(Call) handler must not re-invoke callPayloadDirty; got ${dirtyCallCount} call(s)`,
52
+ )
53
+
54
+ await wechaty.stop()
55
+ })
@@ -753,16 +753,21 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
753
753
  }
754
754
  case PUPPET.types.Payload.Call: {
755
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.
756
+ * Refresh the user-layer Call payload so getters (media /
757
+ * participants / endTime) reflect the new view. Whether
758
+ * the call is still alive is still decided by the
759
+ * call-event handler via __finalizeIfEnded; we do not
760
+ * touch the pool here, and we do not emit a synthetic
761
+ * user event the UI is expected to read getters when
762
+ * it needs the value.
763
+ *
764
+ * IMPORTANT: do NOT call puppet.callPayloadDirty here.
765
+ * Under puppet-service that fires a gRPC DirtyPayload
766
+ * which the server bounces back as another `dirty`
767
+ * event, forming an infinite client⇄server feedback
768
+ * loop. Cache invalidation is already handled by the
769
+ * cache-mixin onDirty listener registered alongside.
764
770
  */
765
- await this.puppet.callPayloadDirty(payloadId)
766
771
  const call = this.__callPool.get(payloadId)
767
772
  if (call) {
768
773
  try {