@juzi/wechaty-puppet-service 1.0.113 → 1.0.115

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 (83) hide show
  1. package/dist/cjs/src/client/call-raw-payload-parser.spec.d.ts +3 -0
  2. package/dist/cjs/src/client/call-raw-payload-parser.spec.d.ts.map +1 -0
  3. package/dist/cjs/src/client/call-raw-payload-parser.spec.js +128 -0
  4. package/dist/cjs/src/client/call-raw-payload-parser.spec.js.map +1 -0
  5. package/dist/cjs/src/client/payload-store.js +2 -2
  6. package/dist/cjs/src/client/payload-store.js.map +1 -1
  7. package/dist/cjs/src/client/puppet-service.d.ts +21 -0
  8. package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
  9. package/dist/cjs/src/client/puppet-service.js +230 -1
  10. package/dist/cjs/src/client/puppet-service.js.map +1 -1
  11. package/dist/cjs/src/client/puppet-service.spec.js.map +1 -1
  12. package/dist/cjs/src/package-json.js +4 -4
  13. package/dist/cjs/src/package-json.js.map +1 -1
  14. package/dist/cjs/src/pure-functions/timestamp.d.ts +15 -1
  15. package/dist/cjs/src/pure-functions/timestamp.d.ts.map +1 -1
  16. package/dist/cjs/src/pure-functions/timestamp.js +10 -1
  17. package/dist/cjs/src/pure-functions/timestamp.js.map +1 -1
  18. package/dist/cjs/src/pure-functions/timestamp.spec.d.ts +3 -0
  19. package/dist/cjs/src/pure-functions/timestamp.spec.d.ts.map +1 -0
  20. package/dist/cjs/src/pure-functions/timestamp.spec.js +48 -0
  21. package/dist/cjs/src/pure-functions/timestamp.spec.js.map +1 -0
  22. package/dist/cjs/src/server/event-stream-manager.d.ts.map +1 -1
  23. package/dist/cjs/src/server/event-stream-manager.js +7 -0
  24. package/dist/cjs/src/server/event-stream-manager.js.map +1 -1
  25. package/dist/cjs/src/server/puppet-implementation.d.ts.map +1 -1
  26. package/dist/cjs/src/server/puppet-implementation.js +296 -2
  27. package/dist/cjs/src/server/puppet-implementation.js.map +1 -1
  28. package/dist/cjs/src/utils/call-media-mapping.d.ts +17 -0
  29. package/dist/cjs/src/utils/call-media-mapping.d.ts.map +1 -0
  30. package/dist/cjs/src/utils/call-media-mapping.js +60 -0
  31. package/dist/cjs/src/utils/call-media-mapping.js.map +1 -0
  32. package/dist/cjs/src/utils/call-media-mapping.spec.d.ts +3 -0
  33. package/dist/cjs/src/utils/call-media-mapping.spec.d.ts.map +1 -0
  34. package/dist/cjs/src/utils/call-media-mapping.spec.js +65 -0
  35. package/dist/cjs/src/utils/call-media-mapping.spec.js.map +1 -0
  36. package/dist/esm/src/client/call-raw-payload-parser.spec.d.ts +3 -0
  37. package/dist/esm/src/client/call-raw-payload-parser.spec.d.ts.map +1 -0
  38. package/dist/esm/src/client/call-raw-payload-parser.spec.js +103 -0
  39. package/dist/esm/src/client/call-raw-payload-parser.spec.js.map +1 -0
  40. package/dist/esm/src/client/payload-store.js +2 -2
  41. package/dist/esm/src/client/payload-store.js.map +1 -1
  42. package/dist/esm/src/client/puppet-service.d.ts +21 -0
  43. package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
  44. package/dist/esm/src/client/puppet-service.js +232 -3
  45. package/dist/esm/src/client/puppet-service.js.map +1 -1
  46. package/dist/esm/src/client/puppet-service.spec.js +1 -1
  47. package/dist/esm/src/client/puppet-service.spec.js.map +1 -1
  48. package/dist/esm/src/package-json.js +4 -4
  49. package/dist/esm/src/package-json.js.map +1 -1
  50. package/dist/esm/src/pure-functions/timestamp.d.ts +15 -1
  51. package/dist/esm/src/pure-functions/timestamp.d.ts.map +1 -1
  52. package/dist/esm/src/pure-functions/timestamp.js +9 -1
  53. package/dist/esm/src/pure-functions/timestamp.js.map +1 -1
  54. package/dist/esm/src/pure-functions/timestamp.spec.d.ts +3 -0
  55. package/dist/esm/src/pure-functions/timestamp.spec.d.ts.map +1 -0
  56. package/dist/esm/src/pure-functions/timestamp.spec.js +46 -0
  57. package/dist/esm/src/pure-functions/timestamp.spec.js.map +1 -0
  58. package/dist/esm/src/server/event-stream-manager.d.ts.map +1 -1
  59. package/dist/esm/src/server/event-stream-manager.js +7 -0
  60. package/dist/esm/src/server/event-stream-manager.js.map +1 -1
  61. package/dist/esm/src/server/puppet-implementation.d.ts.map +1 -1
  62. package/dist/esm/src/server/puppet-implementation.js +296 -2
  63. package/dist/esm/src/server/puppet-implementation.js.map +1 -1
  64. package/dist/esm/src/utils/call-media-mapping.d.ts +17 -0
  65. package/dist/esm/src/utils/call-media-mapping.d.ts.map +1 -0
  66. package/dist/esm/src/utils/call-media-mapping.js +32 -0
  67. package/dist/esm/src/utils/call-media-mapping.js.map +1 -0
  68. package/dist/esm/src/utils/call-media-mapping.spec.d.ts +3 -0
  69. package/dist/esm/src/utils/call-media-mapping.spec.d.ts.map +1 -0
  70. package/dist/esm/src/utils/call-media-mapping.spec.js +40 -0
  71. package/dist/esm/src/utils/call-media-mapping.spec.js.map +1 -0
  72. package/package.json +4 -4
  73. package/src/client/call-raw-payload-parser.spec.ts +131 -0
  74. package/src/client/payload-store.ts +2 -2
  75. package/src/client/puppet-service.spec.ts +3 -1
  76. package/src/client/puppet-service.ts +438 -4
  77. package/src/package-json.ts +4 -4
  78. package/src/pure-functions/timestamp.spec.ts +56 -0
  79. package/src/pure-functions/timestamp.ts +19 -0
  80. package/src/server/event-stream-manager.ts +7 -0
  81. package/src/server/puppet-implementation.ts +384 -3
  82. package/src/utils/call-media-mapping.spec.ts +53 -0
  83. package/src/utils/call-media-mapping.ts +35 -0
@@ -0,0 +1,131 @@
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 { puppet as grpcPuppet } from '@juzi/wechaty-grpc'
6
+
7
+ import { PuppetService } from './puppet-service.js'
8
+ import { timestampFromMilliseconds } from '../pure-functions/timestamp.js'
9
+
10
+ /**
11
+ * The parser only reads `rawPayload.*` and a module-level `log` — it never
12
+ * touches `this`. Invoke it via `prototype.call({}, raw)` to avoid building a
13
+ * full PuppetService instance for unit-level tests.
14
+ */
15
+ const callParser = async (raw: grpcPuppet.CallPayloadResponse.AsObject) =>
16
+ PuppetService.prototype.callRawPayloadParser.call({} as PuppetService, raw)
17
+
18
+ /**
19
+ * 1. Full payload with starter, participants, media=VOICE, startTime, endTime
20
+ * parses every field, mapping VOICE -> CallMediaType.Audio.
21
+ */
22
+ test('callRawPayloadParser parses a complete payload', async t => {
23
+ const startMs = 1_700_000_000_000
24
+ const endMs = 1_700_000_030_000
25
+
26
+ const pb = new grpcPuppet.CallPayloadResponse()
27
+ pb.setId('call-1')
28
+ pb.setStarter('alice')
29
+ pb.setParticipantsList([ 'alice', 'bob' ])
30
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_VOICE)
31
+ pb.setStartTime(timestampFromMilliseconds(startMs))
32
+ pb.setEndTime(timestampFromMilliseconds(endMs))
33
+
34
+ const payload = await callParser(pb.toObject())
35
+
36
+ t.equal(payload.id, 'call-1', 'id propagated')
37
+ t.equal(payload.starter, 'alice', 'starter propagated')
38
+ t.same(payload.participants, [ 'alice', 'bob' ], 'participants propagated')
39
+ t.equal(payload.media, PUPPET.types.CallMediaType.Audio, 'VOICE maps to Audio')
40
+ t.equal(payload.startTime, startMs, 'startTime parsed')
41
+ t.equal(payload.endTime, endMs, 'endTime parsed')
42
+ })
43
+
44
+ /**
45
+ * 2. Empty starter string -> parser omits `starter` from the resulting payload.
46
+ * (Mirrors the server, which only `setStarter` when the value is truthy.)
47
+ */
48
+ test('callRawPayloadParser omits starter when starter is an empty string', async t => {
49
+ const pb = new grpcPuppet.CallPayloadResponse()
50
+ pb.setId('call-2')
51
+ pb.setParticipantsList([ 'alice' ])
52
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_VIDEO)
53
+ pb.setStartTime(timestampFromMilliseconds(1_700_000_000_000))
54
+ // no setStarter -> default proto3 string '' on the wire
55
+
56
+ const payload = await callParser(pb.toObject())
57
+
58
+ t.equal(payload.starter, undefined, 'starter is undefined when empty on the wire')
59
+ t.equal(payload.media, PUPPET.types.CallMediaType.Video, 'VIDEO maps to Video')
60
+ })
61
+
62
+ /**
63
+ * 3. endTime not set (no setEndTime) -> payload.endTime === undefined.
64
+ */
65
+ test('callRawPayloadParser leaves endTime undefined when endTime is not set', async t => {
66
+ const pb = new grpcPuppet.CallPayloadResponse()
67
+ pb.setId('call-3')
68
+ pb.setStarter('alice')
69
+ pb.setParticipantsList([ 'alice', 'bob' ])
70
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_VOICE)
71
+ pb.setStartTime(timestampFromMilliseconds(1_700_000_000_000))
72
+ // no setEndTime
73
+
74
+ const payload = await callParser(pb.toObject())
75
+
76
+ t.equal(payload.endTime, undefined, 'endTime is undefined when not set on the wire')
77
+ })
78
+
79
+ /**
80
+ * 4. media = CALL_TYPE_UNKNOWN -> throw, error mentions "media".
81
+ */
82
+ test('callRawPayloadParser throws when media is CALL_TYPE_UNKNOWN', async t => {
83
+ const pb = new grpcPuppet.CallPayloadResponse()
84
+ pb.setId('call-4')
85
+ pb.setParticipantsList([ 'alice' ])
86
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_UNKNOWN)
87
+ pb.setStartTime(timestampFromMilliseconds(1_700_000_000_000))
88
+
89
+ await t.rejects(
90
+ callParser(pb.toObject()),
91
+ /media/,
92
+ 'rejects with a message that mentions media',
93
+ )
94
+ })
95
+
96
+ /**
97
+ * 5. startTime not set -> throw, error mentions "startTime".
98
+ */
99
+ test('callRawPayloadParser throws when startTime is not set', async t => {
100
+ const pb = new grpcPuppet.CallPayloadResponse()
101
+ pb.setId('call-5')
102
+ pb.setParticipantsList([ 'alice' ])
103
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_VOICE)
104
+ // no setStartTime
105
+
106
+ await t.rejects(
107
+ callParser(pb.toObject()),
108
+ /startTime/,
109
+ 'rejects with a message that mentions startTime',
110
+ )
111
+ })
112
+
113
+ /**
114
+ * 6. endTime explicitly set to epoch 0 -> payload.endTime === 0.
115
+ * proto3 `setEndTime(timestamp(0))` is a legal "ended at epoch-0" value
116
+ * (extreme short session / test stub) and must not be swallowed by a
117
+ * truthy guard. Presence is what matters, not the numeric value.
118
+ */
119
+ test('callRawPayloadParser preserves endTime when it is set to epoch zero', async t => {
120
+ const pb = new grpcPuppet.CallPayloadResponse()
121
+ pb.setId('call-6')
122
+ pb.setStarter('alice')
123
+ pb.setParticipantsList([ 'alice', 'bob' ])
124
+ pb.setMedia(grpcPuppet.CallType.CALL_TYPE_VOICE)
125
+ pb.setStartTime(timestampFromMilliseconds(1_700_000_000_000))
126
+ pb.setEndTime(timestampFromMilliseconds(0))
127
+
128
+ const payload = await callParser(pb.toObject())
129
+
130
+ t.equal(payload.endTime, 0, 'endTime is 0 when explicitly set to epoch zero on the wire')
131
+ })
@@ -164,8 +164,8 @@ class PayloadStore {
164
164
  * Huan(202108): `fs.rm` was introduced from Node.js v14.14
165
165
  * https://nodejs.org/api/fs.html#fs_fspromises_rm_path_options
166
166
  */
167
- await fs.promises.rmdir(this.storeDir, {
168
- // force: true,
167
+ await fs.promises.rm(this.storeDir, {
168
+ force: true,
169
169
  recursive: true,
170
170
  })
171
171
  }
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
2
 
3
- import { test } from 'tstest'
3
+ import {
4
+ test,
5
+ } from 'tstest'
4
6
  import getPort from 'get-port'
5
7
 
6
8
  import { PuppetMock } from '@juzi/wechaty-puppet-mock'
@@ -31,7 +31,10 @@ import {
31
31
 
32
32
  // import type { Subscription } from 'rxjs'
33
33
 
34
- import { millisecondsFromTimestamp } from '../pure-functions/timestamp.js'
34
+ import {
35
+ millisecondsFromTimestamp,
36
+ millisecondsFromTimestampObject,
37
+ } from '../pure-functions/timestamp.js'
35
38
 
36
39
  import {
37
40
  uuidifyFileBoxGrpc,
@@ -50,7 +53,8 @@ import { packageJson } from '../package-json.js'
50
53
 
51
54
  import { GrpcManager } from './grpc-manager.js'
52
55
  import { PayloadStore } from './payload-store.js'
53
- import { OptionalBooleanUnwrapper, OptionalBooleanWrapper, callRecordPbToPayload, channelPayloadToPb, channelPbToPayload, chatHistoryPbToPayload, contactPbToPayload, postPayloadToPb, roomMemberPbToPayload, urlLinkPbToPayload, channelCardPayloadToPb, channelCardPbToPayload } from '../utils/pb-payload-helper.js'
56
+ import { OptionalBooleanUnwrapper, OptionalBooleanWrapper, callRecordPbToPayload, channelPayloadToPb, channelPbToPayload, chatHistoryPbToPayload, contactPbToPayload, postPayloadToPb, roomMemberPbToPayload, urlLinkPbToPayload, channelCardPayloadToPb, channelCardPbToPayload, miniProgramPayloadToPb, urlLinkPayloadToPb, locationPayloadToPb } from '../utils/pb-payload-helper.js'
57
+ import { puppetCallMediaTypeToGrpc, grpcCallTypeToPuppetMedia } from '../utils/call-media-mapping.js'
54
58
  import type { MessageBroadcastTargets } from '@juzi/wechaty-puppet/dist/esm/src/schemas/message.js'
55
59
  import { timeoutPromise } from 'gerror'
56
60
  import { BooleanIndicator } from 'state-switch'
@@ -72,6 +76,34 @@ export type PuppetServiceOptions = PUPPET.PuppetOptions & {
72
76
  const ResetLoginTimeout = 30 * 1000
73
77
  const ResetReadyTimeout = 20 * 1000 // normally ready comes 15 seconds after login
74
78
 
79
+ type MessageBatchSendResponse = Awaited<ReturnType<PUPPET.impls.PuppetInterface['messageBatchSendText']>>
80
+
81
+ type GrpcUnaryMethod<Request, Response> = {
82
+ (request: Request, callback: (error: Error | null, response: Response) => void): unknown,
83
+ }
84
+
85
+ type GrpcBatchResult = {
86
+ getConversationId(): string,
87
+ getError(): string,
88
+ getId(): string,
89
+ }
90
+
91
+ type GrpcBatchResponse<Result extends GrpcBatchResult> = {
92
+ getResultsList(): Result[],
93
+ }
94
+
95
+ function grpcBatchResultsToPayload<Result extends GrpcBatchResult> (
96
+ response: GrpcBatchResponse<Result>,
97
+ ): MessageBatchSendResponse {
98
+ return {
99
+ results: response.getResultsList().map(result => ({
100
+ conversationId : result.getConversationId(),
101
+ error : result.getError() || undefined,
102
+ id : result.getId() || undefined,
103
+ })),
104
+ }
105
+ }
106
+
75
107
  class PuppetService extends PUPPET.Puppet {
76
108
 
77
109
  static override readonly VERSION = VERSION
@@ -125,6 +157,17 @@ class PuppetService extends PUPPET.Puppet {
125
157
  return JSON.stringify(normalizedFileBox)
126
158
  }
127
159
 
160
+ private async grpcUnary<Request, Response> (
161
+ method : GrpcUnaryMethod<Request, Response>,
162
+ request : Request,
163
+ ): Promise<Response> {
164
+ const promisified = util.promisify(
165
+ method.bind(this.grpcManager.client),
166
+ ) as (request: Request) => Promise<Response>
167
+
168
+ return promisified(request)
169
+ }
170
+
128
171
  override name () {
129
172
  return packageJson.name || 'wechaty-puppet-service'
130
173
  }
@@ -384,6 +427,9 @@ class PuppetService extends PUPPET.Puppet {
384
427
  case grpcPuppet.EventType.EVENT_TYPE_VERIFY_SLIDE:
385
428
  this.emit('verify-slide', JSON.parse(payload) as PUPPET.payloads.EventVerifySlide)
386
429
  break
430
+ case grpcPuppet.EventType.EVENT_TYPE_CALL:
431
+ this.emit('call', JSON.parse(payload) as PUPPET.payloads.EventCall)
432
+ break
387
433
 
388
434
  default:
389
435
  // Huan(202003): in default, the `type` type should be `never`, please check.
@@ -461,7 +507,7 @@ class PuppetService extends PUPPET.Puppet {
461
507
  ): Promise<void> {
462
508
  log.verbose('PuppetService', 'fastDirty(%s<%s>, %s)', PUPPET.types.Dirty[payloadType], payloadType, payloadId)
463
509
 
464
- const dirtyMap = {
510
+ const dirtyMap: Partial<Record<PUPPET.types.Dirty, (id: string) => Promise<unknown>>> = {
465
511
  [PUPPET.types.Dirty.Contact]: async (id: string) => this._payloadStore.contact?.delete(id),
466
512
  [PUPPET.types.Dirty.Friendship]: async (_: string) => {},
467
513
  [PUPPET.types.Dirty.Message]: async (_: string) => {},
@@ -474,7 +520,7 @@ class PuppetService extends PUPPET.Puppet {
474
520
  }
475
521
 
476
522
  try {
477
- await dirtyMap[payloadType](payloadId)
523
+ await dirtyMap[payloadType]?.(payloadId)
478
524
  } catch (error) {
479
525
  this.emit('error', error)
480
526
  }
@@ -1114,6 +1160,189 @@ class PuppetService extends PUPPET.Puppet {
1114
1160
  return payload
1115
1161
  }
1116
1162
 
1163
+ override async callInvite (
1164
+ contactIds: string[],
1165
+ media: PUPPET.types.CallMediaType,
1166
+ ): Promise<string> {
1167
+ log.verbose('PuppetService', 'callInvite(%s, %s)', contactIds, media)
1168
+
1169
+ const request = new grpcPuppet.CallInviteRequest()
1170
+ request.setContactIdsList(contactIds)
1171
+ request.setMedia(puppetCallMediaTypeToGrpc(media))
1172
+
1173
+ const response = await util.promisify(
1174
+ this.grpcManager.client.callInvite
1175
+ .bind(this.grpcManager.client),
1176
+ )(request)
1177
+
1178
+ const callId = response.getCallId()
1179
+ if (!callId) {
1180
+ throw new Error('callInvite: puppet server returned empty call_id')
1181
+ }
1182
+
1183
+ return callId
1184
+ }
1185
+
1186
+ override async callAdd (
1187
+ callId: string,
1188
+ contactIds: string[],
1189
+ ): Promise<void> {
1190
+ log.verbose('PuppetService', 'callAdd(%s, %s)', callId, contactIds)
1191
+
1192
+ const request = new grpcPuppet.CallAddRequest()
1193
+ request.setCallId(callId)
1194
+ request.setContactIdsList(contactIds)
1195
+
1196
+ await this.grpcUnary(
1197
+ this.grpcManager.client.callAdd,
1198
+ request,
1199
+ )
1200
+ }
1201
+
1202
+ override async callAccept (
1203
+ callId: string,
1204
+ ): Promise<void> {
1205
+ log.verbose('PuppetService', 'callAccept(%s)', callId)
1206
+
1207
+ const request = new grpcPuppet.CallAcceptRequest()
1208
+ request.setCallId(callId)
1209
+
1210
+ await this.grpcUnary(
1211
+ this.grpcManager.client.callAccept,
1212
+ request,
1213
+ )
1214
+ }
1215
+
1216
+ override async callReject (
1217
+ callId: string,
1218
+ reason?: string,
1219
+ ): Promise<void> {
1220
+ log.verbose('PuppetService', 'callReject(%s, %s)', callId, reason)
1221
+
1222
+ const request = new grpcPuppet.CallRejectRequest()
1223
+ request.setCallId(callId)
1224
+ if (reason !== undefined && reason !== '') {
1225
+ request.setReason(reason)
1226
+ }
1227
+
1228
+ await this.grpcUnary(
1229
+ this.grpcManager.client.callReject,
1230
+ request,
1231
+ )
1232
+ }
1233
+
1234
+ override async callCancel (
1235
+ callId: string,
1236
+ ): Promise<void> {
1237
+ log.verbose('PuppetService', 'callCancel(%s)', callId)
1238
+
1239
+ const request = new grpcPuppet.CallCancelRequest()
1240
+ request.setCallId(callId)
1241
+
1242
+ await this.grpcUnary(
1243
+ this.grpcManager.client.callCancel,
1244
+ request,
1245
+ )
1246
+ }
1247
+
1248
+ override async callHangup (
1249
+ callId: string,
1250
+ reason?: string,
1251
+ ): Promise<void> {
1252
+ log.verbose('PuppetService', 'callHangup(%s, %s)', callId, reason)
1253
+
1254
+ const request = new grpcPuppet.CallHangupRequest()
1255
+ request.setCallId(callId)
1256
+ if (reason !== undefined && reason !== '') {
1257
+ request.setReason(reason)
1258
+ }
1259
+
1260
+ await this.grpcUnary(
1261
+ this.grpcManager.client.callHangup,
1262
+ request,
1263
+ )
1264
+ }
1265
+
1266
+ override async callMediaEndpoint (
1267
+ callId: string,
1268
+ ): Promise<PUPPET.payloads.CallMediaEndpoint> {
1269
+ log.verbose('PuppetService', 'callMediaEndpoint(%s)', callId)
1270
+
1271
+ const request = new grpcPuppet.CallMediaEndpointRequest()
1272
+ request.setCallId(callId)
1273
+
1274
+ const response = await util.promisify(
1275
+ this.grpcManager.client.callMediaEndpoint
1276
+ .bind(this.grpcManager.client),
1277
+ )(request)
1278
+
1279
+ const url = response.getUrl()
1280
+ const token = response.getToken()
1281
+ if (!url || !token) {
1282
+ throw new Error('callMediaEndpoint: puppet server returned empty url or token')
1283
+ }
1284
+
1285
+ const payload: PUPPET.payloads.CallMediaEndpoint = {
1286
+ url,
1287
+ token,
1288
+ }
1289
+
1290
+ if (response.hasExpiresAt()) {
1291
+ payload.expiresAt = millisecondsFromTimestamp(response.getExpiresAt()!)
1292
+ }
1293
+
1294
+ const protocol = response.getProtocol()
1295
+ if (protocol) {
1296
+ payload.protocol = protocol
1297
+ }
1298
+
1299
+ return payload
1300
+ }
1301
+
1302
+ override async callRawPayload (callId: string): Promise<grpcPuppet.CallPayloadResponse.AsObject> {
1303
+ log.verbose('PuppetService', 'callRawPayload(%s)', callId)
1304
+
1305
+ const request = new grpcPuppet.CallPayloadRequest()
1306
+ request.setId(callId)
1307
+
1308
+ const response = await util.promisify(
1309
+ this.grpcManager.client.callPayload
1310
+ .bind(this.grpcManager.client),
1311
+ )(request)
1312
+
1313
+ return response.toObject()
1314
+ }
1315
+
1316
+ override async callRawPayloadParser (rawPayload: grpcPuppet.CallPayloadResponse.AsObject): Promise<PUPPET.payloads.Call> {
1317
+ log.verbose('PuppetService', 'callRawPayloadParser({id:%s})', rawPayload.id)
1318
+
1319
+ const media = grpcCallTypeToPuppetMedia(rawPayload.media)
1320
+ if (media === undefined) {
1321
+ throw new Error('callRawPayloadParser: media is required (got CALL_TYPE_UNKNOWN on the wire)')
1322
+ }
1323
+
1324
+ if (rawPayload.startTime === undefined) {
1325
+ throw new Error('callRawPayloadParser: startTime is required')
1326
+ }
1327
+
1328
+ const payload: PUPPET.payloads.Call = {
1329
+ id : rawPayload.id,
1330
+ participants : rawPayload.participantsList,
1331
+ media,
1332
+ startTime : millisecondsFromTimestampObject(rawPayload.startTime),
1333
+ }
1334
+
1335
+ if (rawPayload.starter) {
1336
+ payload.starter = rawPayload.starter
1337
+ }
1338
+
1339
+ if (rawPayload.endTime !== undefined) {
1340
+ payload.endTime = millisecondsFromTimestampObject(rawPayload.endTime)
1341
+ }
1342
+
1343
+ return payload
1344
+ }
1345
+
1117
1346
  override async messageChatHistory (
1118
1347
  messageId: string,
1119
1348
  ): Promise<PUPPET.payloads.ChatHistory[]> {
@@ -1487,6 +1716,211 @@ class PuppetService extends PUPPET.Puppet {
1487
1716
  }
1488
1717
  }
1489
1718
 
1719
+ override async messageBatchSendText (
1720
+ conversationIds : string[],
1721
+ text : string,
1722
+ batchTaskId? : string,
1723
+ ): Promise<MessageBatchSendResponse> {
1724
+ log.verbose('PuppetService', 'messageBatchSendText(%s)', conversationIds.join(','))
1725
+
1726
+ const request = new grpcPuppet.MessageBatchSendTextRequest()
1727
+ request.setConversationIdsList(conversationIds)
1728
+ request.setText(text)
1729
+ if (batchTaskId) {
1730
+ request.setBatchTaskId(batchTaskId)
1731
+ }
1732
+
1733
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendTextRequest, grpcPuppet.MessageBatchSendTextResponse>(
1734
+ this.grpcManager.client.messageBatchSendText,
1735
+ request,
1736
+ )
1737
+
1738
+ return grpcBatchResultsToPayload(response)
1739
+ }
1740
+
1741
+ override async messageBatchSendFile (
1742
+ conversationIds : string[],
1743
+ fileBox : FileBoxInterface,
1744
+ batchTaskId? : string,
1745
+ ): Promise<MessageBatchSendResponse> {
1746
+ log.verbose('PuppetService', 'messageBatchSendFile(%s)', conversationIds.join(','))
1747
+
1748
+ const request = new grpcPuppet.MessageBatchSendFileRequest()
1749
+ request.setConversationIdsList(conversationIds)
1750
+ request.setFileBox(await this.serializeFileBox(fileBox))
1751
+ if (batchTaskId) {
1752
+ request.setBatchTaskId(batchTaskId)
1753
+ }
1754
+
1755
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendFileRequest, grpcPuppet.MessageBatchSendFileResponse>(
1756
+ this.grpcManager.client.messageBatchSendFile,
1757
+ request,
1758
+ )
1759
+
1760
+ return grpcBatchResultsToPayload(response)
1761
+ }
1762
+
1763
+ override async messageBatchForward (
1764
+ conversationIds : string[],
1765
+ messageIds : string | string[],
1766
+ batchTaskId? : string,
1767
+ ): Promise<MessageBatchSendResponse> {
1768
+ log.verbose('PuppetService', 'messageBatchForward(%s, %s)', conversationIds.join(','), messageIds)
1769
+
1770
+ const request = new grpcPuppet.MessageBatchForwardRequest()
1771
+ request.setConversationIdsList(conversationIds)
1772
+ if (Array.isArray(messageIds)) {
1773
+ request.setMessageIdsList(messageIds)
1774
+ if (messageIds.length === 1) {
1775
+ request.setMessageId(messageIds[0] as string)
1776
+ }
1777
+ } else {
1778
+ request.setMessageId(messageIds)
1779
+ }
1780
+ if (batchTaskId) {
1781
+ request.setBatchTaskId(batchTaskId)
1782
+ }
1783
+
1784
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchForwardRequest, grpcPuppet.MessageBatchForwardResponse>(
1785
+ this.grpcManager.client.messageBatchForward,
1786
+ request,
1787
+ )
1788
+
1789
+ return grpcBatchResultsToPayload(response)
1790
+ }
1791
+
1792
+ override async messageBatchSendContact (
1793
+ conversationIds : string[],
1794
+ contactId : string,
1795
+ batchTaskId? : string,
1796
+ ): Promise<MessageBatchSendResponse> {
1797
+ log.verbose('PuppetService', 'messageBatchSendContact(%s, %s)', conversationIds.join(','), contactId)
1798
+
1799
+ const request = new grpcPuppet.MessageBatchSendContactRequest()
1800
+ request.setConversationIdsList(conversationIds)
1801
+ request.setContactId(contactId)
1802
+ if (batchTaskId) {
1803
+ request.setBatchTaskId(batchTaskId)
1804
+ }
1805
+
1806
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendContactRequest, grpcPuppet.MessageBatchSendContactResponse>(
1807
+ this.grpcManager.client.messageBatchSendContact,
1808
+ request,
1809
+ )
1810
+
1811
+ return grpcBatchResultsToPayload(response)
1812
+ }
1813
+
1814
+ override async messageBatchSendUrl (
1815
+ conversationIds : string[],
1816
+ urlLinkPayload : PUPPET.payloads.UrlLink,
1817
+ batchTaskId? : string,
1818
+ ): Promise<MessageBatchSendResponse> {
1819
+ log.verbose('PuppetService', 'messageBatchSendUrl(%s)', conversationIds.join(','))
1820
+
1821
+ const request = new grpcPuppet.MessageBatchSendUrlRequest()
1822
+ request.setConversationIdsList(conversationIds)
1823
+ request.setUrlLink(urlLinkPayloadToPb(grpcPuppet, urlLinkPayload))
1824
+ if (batchTaskId) {
1825
+ request.setBatchTaskId(batchTaskId)
1826
+ }
1827
+
1828
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendUrlRequest, grpcPuppet.MessageBatchSendUrlResponse>(
1829
+ this.grpcManager.client.messageBatchSendUrl,
1830
+ request,
1831
+ )
1832
+
1833
+ return grpcBatchResultsToPayload(response)
1834
+ }
1835
+
1836
+ override async messageBatchSendMiniProgram (
1837
+ conversationIds : string[],
1838
+ miniProgramPayload : PUPPET.payloads.MiniProgram,
1839
+ batchTaskId? : string,
1840
+ ): Promise<MessageBatchSendResponse> {
1841
+ log.verbose('PuppetService', 'messageBatchSendMiniProgram(%s)', conversationIds.join(','))
1842
+
1843
+ const request = new grpcPuppet.MessageBatchSendMiniProgramRequest()
1844
+ request.setConversationIdsList(conversationIds)
1845
+ request.setMiniProgram(miniProgramPayloadToPb(grpcPuppet, miniProgramPayload))
1846
+ if (batchTaskId) {
1847
+ request.setBatchTaskId(batchTaskId)
1848
+ }
1849
+
1850
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendMiniProgramRequest, grpcPuppet.MessageBatchSendMiniProgramResponse>(
1851
+ this.grpcManager.client.messageBatchSendMiniProgram,
1852
+ request,
1853
+ )
1854
+
1855
+ return grpcBatchResultsToPayload(response)
1856
+ }
1857
+
1858
+ override async messageBatchSendLocation (
1859
+ conversationIds : string[],
1860
+ locationPayload : PUPPET.payloads.Location,
1861
+ batchTaskId? : string,
1862
+ ): Promise<MessageBatchSendResponse> {
1863
+ log.verbose('PuppetService', 'messageBatchSendLocation(%s)', conversationIds.join(','))
1864
+
1865
+ const request = new grpcPuppet.MessageBatchSendLocationRequest()
1866
+ request.setConversationIdsList(conversationIds)
1867
+ request.setLocation(locationPayloadToPb(grpcPuppet, locationPayload))
1868
+ if (batchTaskId) {
1869
+ request.setBatchTaskId(batchTaskId)
1870
+ }
1871
+
1872
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendLocationRequest, grpcPuppet.MessageBatchSendLocationResponse>(
1873
+ this.grpcManager.client.messageBatchSendLocation,
1874
+ request,
1875
+ )
1876
+
1877
+ return grpcBatchResultsToPayload(response)
1878
+ }
1879
+
1880
+ override async messageBatchSendChannel (
1881
+ conversationIds : string[],
1882
+ channelPayload : PUPPET.payloads.Channel,
1883
+ batchTaskId? : string,
1884
+ ): Promise<MessageBatchSendResponse> {
1885
+ log.verbose('PuppetService', 'messageBatchSendChannel(%s)', conversationIds.join(','))
1886
+
1887
+ const request = new grpcPuppet.MessageBatchSendChannelRequest()
1888
+ request.setConversationIdsList(conversationIds)
1889
+ request.setChannel(channelPayloadToPb(grpcPuppet, channelPayload))
1890
+ if (batchTaskId) {
1891
+ request.setBatchTaskId(batchTaskId)
1892
+ }
1893
+
1894
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendChannelRequest, grpcPuppet.MessageBatchSendChannelResponse>(
1895
+ this.grpcManager.client.messageBatchSendChannel,
1896
+ request,
1897
+ )
1898
+
1899
+ return grpcBatchResultsToPayload(response)
1900
+ }
1901
+
1902
+ override async messageBatchSendChannelCard (
1903
+ conversationIds : string[],
1904
+ channelCardPayload : PUPPET.payloads.ChannelCard,
1905
+ batchTaskId? : string,
1906
+ ): Promise<MessageBatchSendResponse> {
1907
+ log.verbose('PuppetService', 'messageBatchSendChannelCard(%s)', conversationIds.join(','))
1908
+
1909
+ const request = new grpcPuppet.MessageBatchSendChannelCardRequest()
1910
+ request.setConversationIdsList(conversationIds)
1911
+ request.setChannelCard(channelCardPayloadToPb(grpcPuppet, channelCardPayload))
1912
+ if (batchTaskId) {
1913
+ request.setBatchTaskId(batchTaskId)
1914
+ }
1915
+
1916
+ const response = await this.grpcUnary<grpcPuppet.MessageBatchSendChannelCardRequest, grpcPuppet.MessageBatchSendChannelCardResponse>(
1917
+ this.grpcManager.client.messageBatchSendChannelCard,
1918
+ request,
1919
+ )
1920
+
1921
+ return grpcBatchResultsToPayload(response)
1922
+ }
1923
+
1490
1924
  override async messageSendFile (
1491
1925
  conversationId : string,
1492
1926
  fileBox : FileBoxInterface,
@@ -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.113",
7
+ "version": "1.0.115",
8
8
  "description": "Puppet Service for Wechaty",
9
9
  "type": "module",
10
10
  "exports": {
@@ -55,7 +55,7 @@ export const packageJson: PackageJson = {
55
55
  "@chatie/eslint-config": "^1.0.4",
56
56
  "@chatie/semver": "^0.4.7",
57
57
  "@chatie/tsconfig": "^4.6.3",
58
- "@juzi/wechaty-puppet": "^1.0.125",
58
+ "@juzi/wechaty-puppet": "^1.0.138",
59
59
  "@juzi/wechaty-puppet-mock": "^1.0.1",
60
60
  "@swc/core": "1.3.39",
61
61
  "@types/google-protobuf": "^3.15.5",
@@ -74,10 +74,10 @@ export const packageJson: PackageJson = {
74
74
  "why-is-node-running": "^2.2.1"
75
75
  },
76
76
  "peerDependencies": {
77
- "@juzi/wechaty-puppet": "^1.0.84"
77
+ "@juzi/wechaty-puppet": "^1.0.138"
78
78
  },
79
79
  "dependencies": {
80
- "@juzi/wechaty-grpc": "^1.0.100",
80
+ "@juzi/wechaty-grpc": "^1.0.102",
81
81
  "clone-class": "^1.1.1",
82
82
  "ducks": "^1.0.2",
83
83
  "file-box": "^1.5.5",