@juzi/wechaty-puppet-service 1.0.129 → 1.0.131
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.
- package/dist/cjs/src/auth/call-cred.d.ts +2 -2
- package/dist/cjs/src/auth/call-cred.d.ts.map +1 -1
- package/dist/cjs/src/auth/call-cred.js.map +1 -1
- package/dist/cjs/src/client/puppet-service.d.ts +27 -2
- package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +431 -124
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/client/puppet-service.spec.js +107 -0
- package/dist/cjs/src/client/puppet-service.spec.js.map +1 -1
- package/dist/cjs/src/client/room-cache-coordinator.d.ts +14 -0
- package/dist/cjs/src/client/room-cache-coordinator.d.ts.map +1 -0
- package/dist/cjs/src/client/room-cache-coordinator.js +53 -0
- package/dist/cjs/src/client/room-cache-coordinator.js.map +1 -0
- package/dist/cjs/src/client/room-member-cache.spec.d.ts +2 -0
- package/dist/cjs/src/client/room-member-cache.spec.d.ts.map +1 -0
- package/dist/cjs/src/client/room-member-cache.spec.js +417 -0
- package/dist/cjs/src/client/room-member-cache.spec.js.map +1 -0
- package/dist/cjs/src/client/room-member-load.spec.d.ts +2 -0
- package/dist/cjs/src/client/room-member-load.spec.d.ts.map +1 -0
- package/dist/cjs/src/client/room-member-load.spec.js +324 -0
- package/dist/cjs/src/client/room-member-load.spec.js.map +1 -0
- package/dist/cjs/src/package-json.js +4 -4
- package/dist/cjs/src/server/event-stream-manager.d.ts.map +1 -1
- package/dist/cjs/src/server/event-stream-manager.js +30 -0
- package/dist/cjs/src/server/event-stream-manager.js.map +1 -1
- package/dist/cjs/src/server/event-stream-manager.spec.d.ts +3 -0
- package/dist/cjs/src/server/event-stream-manager.spec.d.ts.map +1 -0
- package/dist/cjs/src/server/event-stream-manager.spec.js +22 -0
- package/dist/cjs/src/server/event-stream-manager.spec.js.map +1 -0
- package/dist/cjs/src/server/puppet-implementation.d.ts.map +1 -1
- package/dist/cjs/src/server/puppet-implementation.js +135 -28
- package/dist/cjs/src/server/puppet-implementation.js.map +1 -1
- package/dist/cjs/src/server/puppet-implementation.spec.js +98 -0
- package/dist/cjs/src/server/puppet-implementation.spec.js.map +1 -1
- package/dist/cjs/src/utils/pb-payload-helper.d.ts +10 -0
- package/dist/cjs/src/utils/pb-payload-helper.d.ts.map +1 -1
- package/dist/cjs/src/utils/pb-payload-helper.js +180 -1
- package/dist/cjs/src/utils/pb-payload-helper.js.map +1 -1
- package/dist/cjs/src/utils/room-member-load.spec.d.ts +2 -0
- package/dist/cjs/src/utils/room-member-load.spec.d.ts.map +1 -0
- package/dist/cjs/src/utils/room-member-load.spec.js +83 -0
- package/dist/cjs/src/utils/room-member-load.spec.js.map +1 -0
- package/dist/esm/src/auth/call-cred.d.ts +2 -2
- package/dist/esm/src/auth/call-cred.d.ts.map +1 -1
- package/dist/esm/src/auth/call-cred.js.map +1 -1
- package/dist/esm/src/client/puppet-service.d.ts +27 -2
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +432 -125
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/client/puppet-service.spec.js +107 -0
- package/dist/esm/src/client/puppet-service.spec.js.map +1 -1
- package/dist/esm/src/client/room-cache-coordinator.d.ts +14 -0
- package/dist/esm/src/client/room-cache-coordinator.d.ts.map +1 -0
- package/dist/esm/src/client/room-cache-coordinator.js +49 -0
- package/dist/esm/src/client/room-cache-coordinator.js.map +1 -0
- package/dist/esm/src/client/room-member-cache.spec.d.ts +2 -0
- package/dist/esm/src/client/room-member-cache.spec.d.ts.map +1 -0
- package/dist/esm/src/client/room-member-cache.spec.js +415 -0
- package/dist/esm/src/client/room-member-cache.spec.js.map +1 -0
- package/dist/esm/src/client/room-member-load.spec.d.ts +2 -0
- package/dist/esm/src/client/room-member-load.spec.d.ts.map +1 -0
- package/dist/esm/src/client/room-member-load.spec.js +296 -0
- package/dist/esm/src/client/room-member-load.spec.js.map +1 -0
- package/dist/esm/src/package-json.js +4 -4
- package/dist/esm/src/server/event-stream-manager.d.ts.map +1 -1
- package/dist/esm/src/server/event-stream-manager.js +30 -0
- package/dist/esm/src/server/event-stream-manager.js.map +1 -1
- package/dist/esm/src/server/event-stream-manager.spec.d.ts +3 -0
- package/dist/esm/src/server/event-stream-manager.spec.d.ts.map +1 -0
- package/dist/esm/src/server/event-stream-manager.spec.js +20 -0
- package/dist/esm/src/server/event-stream-manager.spec.js.map +1 -0
- package/dist/esm/src/server/puppet-implementation.d.ts.map +1 -1
- package/dist/esm/src/server/puppet-implementation.js +136 -29
- package/dist/esm/src/server/puppet-implementation.js.map +1 -1
- package/dist/esm/src/server/puppet-implementation.spec.js +98 -0
- package/dist/esm/src/server/puppet-implementation.spec.js.map +1 -1
- package/dist/esm/src/utils/pb-payload-helper.d.ts +10 -0
- package/dist/esm/src/utils/pb-payload-helper.d.ts.map +1 -1
- package/dist/esm/src/utils/pb-payload-helper.js +169 -0
- package/dist/esm/src/utils/pb-payload-helper.js.map +1 -1
- package/dist/esm/src/utils/room-member-load.spec.d.ts +2 -0
- package/dist/esm/src/utils/room-member-load.spec.d.ts.map +1 -0
- package/dist/esm/src/utils/room-member-load.spec.js +58 -0
- package/dist/esm/src/utils/room-member-load.spec.js.map +1 -0
- package/package.json +4 -4
- package/src/auth/call-cred.ts +1 -1
- package/src/client/puppet-service.spec.ts +147 -0
- package/src/client/puppet-service.ts +378 -138
- package/src/client/room-cache-coordinator.ts +50 -0
- package/src/client/room-member-cache.spec.ts +448 -0
- package/src/client/room-member-load.spec.ts +319 -0
- package/src/package-json.ts +4 -4
- package/src/server/event-stream-manager.spec.ts +30 -0
- package/src/server/event-stream-manager.ts +29 -0
- package/src/server/puppet-implementation.spec.ts +127 -0
- package/src/server/puppet-implementation.ts +144 -29
- package/src/utils/pb-payload-helper.ts +150 -0
- package/src/utils/room-member-load.spec.ts +67 -0
|
@@ -28,7 +28,8 @@ import { EventTypeRev, } from '../event-type-rev.js';
|
|
|
28
28
|
import { packageJson } from '../package-json.js';
|
|
29
29
|
import { GrpcManager } from './grpc-manager.js';
|
|
30
30
|
import { PayloadStore } from './payload-store.js';
|
|
31
|
-
import {
|
|
31
|
+
import { RoomCacheCoordinator, RoomCacheOperationError } from './room-cache-coordinator.js';
|
|
32
|
+
import { OptionalBooleanUnwrapper, OptionalBooleanWrapper, callRecordPbToPayload, channelPayloadToPb, channelPbToPayload, chatHistoryPbToPayload, contactPbToPayload, postPayloadToPb, roomMemberPbToPayload, urlLinkPbToPayload, channelCardPayloadToPb, channelCardPbToPayload, miniProgramPayloadToPb, urlLinkPayloadToPb, locationPayloadToPb, roomPbToPayload, roomMemberLoadStatePbToPayload, roomMemberSnapshotPbToPayload, validateRoomMemberRevision, validateRoomMemberLoadState } from '../utils/pb-payload-helper.js';
|
|
32
33
|
import { puppetCallMediaTypeToGrpc, grpcCallTypeToPuppetMedia } from '../utils/call-media-mapping.js';
|
|
33
34
|
import { timeoutPromise } from 'gerror';
|
|
34
35
|
import { BooleanIndicator } from 'state-switch';
|
|
@@ -47,6 +48,15 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
47
48
|
options;
|
|
48
49
|
static VERSION = VERSION;
|
|
49
50
|
_payloadStore;
|
|
51
|
+
roomCache = new RoomCacheCoordinator();
|
|
52
|
+
eventQueue = Promise.resolve();
|
|
53
|
+
eventEpoch = 0;
|
|
54
|
+
eventsBlocked = false;
|
|
55
|
+
failedEventSeq;
|
|
56
|
+
activeEventSeq;
|
|
57
|
+
pendingEventSeq;
|
|
58
|
+
checkpointQueue = Promise.resolve();
|
|
59
|
+
pendingCheckpointRollback;
|
|
50
60
|
timeoutMilliseconds;
|
|
51
61
|
_grpcManager;
|
|
52
62
|
get grpcManager() {
|
|
@@ -100,6 +110,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
100
110
|
this.log.verbose('PuppetService', 'onStart()');
|
|
101
111
|
this.waitingForLogin = false;
|
|
102
112
|
this.waitingForReady = false;
|
|
113
|
+
this.suspendEventProcessing();
|
|
114
|
+
this.eventsBlocked = false;
|
|
103
115
|
if (this._grpcManager) {
|
|
104
116
|
this.log.warn('PuppetService', 'onStart() found this.grpc is already existed. dropped.');
|
|
105
117
|
this._grpcManager = undefined;
|
|
@@ -116,7 +128,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
116
128
|
this.bridgeGrpcEventStream(grpcManager);
|
|
117
129
|
this.log.info('PuppetService', 'start() setting up bridge grpc event stream ... done');
|
|
118
130
|
this.log.info('PuppetService', 'start() starting grpc manager...');
|
|
119
|
-
const { lastEventSeq, accountId } = await this.
|
|
131
|
+
const { lastEventSeq, accountId } = await this.eventResumePosition();
|
|
120
132
|
await grpcManager.start(lastEventSeq, accountId);
|
|
121
133
|
this.log.info('PuppetService', 'start() starting grpc manager... done');
|
|
122
134
|
this.log.info('PuppetService', 'start healthCheck');
|
|
@@ -125,6 +137,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
125
137
|
}
|
|
126
138
|
async onStop() {
|
|
127
139
|
this.log.info('PuppetService', 'onStop()');
|
|
140
|
+
this.suspendEventProcessing();
|
|
141
|
+
await this.checkpointQueue;
|
|
128
142
|
if (this._grpcManager) {
|
|
129
143
|
this.log.info('PuppetService', 'onStop() stopping grpc manager ...');
|
|
130
144
|
const grpcManager = this._grpcManager;
|
|
@@ -160,7 +174,41 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
160
174
|
bridgeGrpcEventStream(client) {
|
|
161
175
|
this.log.verbose('PuppetService', 'bridgeGrpcEventStream(client)');
|
|
162
176
|
client
|
|
163
|
-
.on('data',
|
|
177
|
+
.on('data', (event) => {
|
|
178
|
+
if (client !== this._grpcManager || this.eventsBlocked) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const epoch = this.eventEpoch;
|
|
182
|
+
this.eventQueue = this.eventQueue.then(async () => {
|
|
183
|
+
if (!this.ownsEventWorker(client, epoch)) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
this.activeEventSeq = event.getSeq();
|
|
187
|
+
try {
|
|
188
|
+
if (this.failedEventSeq !== undefined && event.getSeq() > this.failedEventSeq) {
|
|
189
|
+
throw new Error(`Replay skipped failed event ${this.failedEventSeq}; stream halted until reset`);
|
|
190
|
+
}
|
|
191
|
+
await this.onGrpcStreamEvent(event, epoch);
|
|
192
|
+
if (!this.ownsEventWorker(client, epoch)) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
if (event.getSeq() === this.failedEventSeq) {
|
|
196
|
+
this.failedEventSeq = undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (!this.ownsEventWorker(client, epoch)) {
|
|
201
|
+
this.log.error('PuppetService', 'superseded event worker failed: %s', error);
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
this.haltEventStream(client, event, error);
|
|
205
|
+
}
|
|
206
|
+
if (epoch === this.eventEpoch) {
|
|
207
|
+
this.activeEventSeq = undefined;
|
|
208
|
+
}
|
|
209
|
+
return undefined;
|
|
210
|
+
}).catch((error) => { this.reportStreamError(error); });
|
|
211
|
+
})
|
|
164
212
|
.on('end', () => {
|
|
165
213
|
this.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(end)');
|
|
166
214
|
})
|
|
@@ -185,7 +233,128 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
185
233
|
this.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(cancel), %s', JSON.stringify(args));
|
|
186
234
|
});
|
|
187
235
|
}
|
|
188
|
-
|
|
236
|
+
suspendEventProcessing() {
|
|
237
|
+
if (this.activeEventSeq) {
|
|
238
|
+
this.pendingEventSeq ??= this.activeEventSeq;
|
|
239
|
+
}
|
|
240
|
+
this.eventEpoch++;
|
|
241
|
+
this.eventsBlocked = true;
|
|
242
|
+
this.activeEventSeq = undefined;
|
|
243
|
+
this.eventQueue = Promise.resolve();
|
|
244
|
+
}
|
|
245
|
+
ownsEventWorker(manager, epoch) {
|
|
246
|
+
return manager === this._grpcManager && this.isEventEpochCurrent(epoch);
|
|
247
|
+
}
|
|
248
|
+
isEventEpochCurrent(epoch) {
|
|
249
|
+
return epoch === this.eventEpoch && !this.eventsBlocked;
|
|
250
|
+
}
|
|
251
|
+
async eventResumePosition(epoch = this.eventEpoch) {
|
|
252
|
+
return this.withEventCheckpoint(async () => {
|
|
253
|
+
if (this.pendingCheckpointRollback) {
|
|
254
|
+
await this.restoreEventMetadata(this.pendingCheckpointRollback);
|
|
255
|
+
this.pendingCheckpointRollback = undefined;
|
|
256
|
+
}
|
|
257
|
+
const lastEventSeq = await this._payloadStore.miscellaneous?.get('lastEventSeq');
|
|
258
|
+
if (epoch !== this.eventEpoch) {
|
|
259
|
+
return { lastEventSeq };
|
|
260
|
+
}
|
|
261
|
+
if (this.pendingEventSeq !== undefined && this.pendingEventSeq > Number(lastEventSeq ?? 0)) {
|
|
262
|
+
this.failedEventSeq ??= this.pendingEventSeq;
|
|
263
|
+
}
|
|
264
|
+
this.pendingEventSeq = undefined;
|
|
265
|
+
return { lastEventSeq, accountId: await this._payloadStore.miscellaneous?.get('accountId') };
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async withEventCheckpoint(operation) {
|
|
269
|
+
const result = this.checkpointQueue.then(operation);
|
|
270
|
+
this.checkpointQueue = result.then(() => undefined, () => undefined);
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
async restoreEventMetadata(previous) {
|
|
274
|
+
for (const [key, value] of previous) {
|
|
275
|
+
if (value === undefined) {
|
|
276
|
+
await this._payloadStore.miscellaneous?.delete(key);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
await this._payloadStore.miscellaneous?.set(key, value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async writeEventMetadata(changes, epoch) {
|
|
284
|
+
const isCurrent = () => epoch === undefined || this.isEventEpochCurrent(epoch);
|
|
285
|
+
if (!isCurrent()) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const previous = new Map();
|
|
289
|
+
for (const key of changes.keys()) {
|
|
290
|
+
previous.set(key, await this._payloadStore.miscellaneous?.get(key));
|
|
291
|
+
}
|
|
292
|
+
if (!isCurrent()) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
for (const [key, value] of changes) {
|
|
297
|
+
if (value === undefined) {
|
|
298
|
+
await this._payloadStore.miscellaneous?.delete(key);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
await this._payloadStore.miscellaneous?.set(key, value);
|
|
302
|
+
}
|
|
303
|
+
if (!isCurrent()) {
|
|
304
|
+
await this.restoreEventMetadata(previous);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
try {
|
|
311
|
+
await this.restoreEventMetadata(previous);
|
|
312
|
+
}
|
|
313
|
+
catch (rollbackError) {
|
|
314
|
+
this.pendingCheckpointRollback = previous;
|
|
315
|
+
this.log.error('PuppetService', 'checkpoint rollback failed: %s', rollbackError);
|
|
316
|
+
}
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async commitEventCheckpoint(seq, timestamp, epoch) {
|
|
321
|
+
await this.withEventCheckpoint(async () => {
|
|
322
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const lastEventSeq = await this._payloadStore.miscellaneous?.get('lastEventSeq');
|
|
326
|
+
if (lastEventSeq && seq <= Number(lastEventSeq) && seq !== 1) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
await this.writeEventMetadata(new Map([
|
|
330
|
+
['lastEventTimestamp', timestamp], ['lastEventSeq', String(seq)],
|
|
331
|
+
]), epoch);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
reportStreamError(error) {
|
|
335
|
+
this.log.error('PuppetService', 'event processing failed: %s', error);
|
|
336
|
+
try {
|
|
337
|
+
this.emit('error', error);
|
|
338
|
+
}
|
|
339
|
+
catch (listenerError) {
|
|
340
|
+
this.log.error('PuppetService', 'error listener failed: %s', listenerError);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
haltEventStream(manager, event, error) {
|
|
344
|
+
this.failedEventSeq ??= event.getSeq();
|
|
345
|
+
this.suspendEventProcessing();
|
|
346
|
+
try {
|
|
347
|
+
manager.stopStream();
|
|
348
|
+
}
|
|
349
|
+
catch (stopError) {
|
|
350
|
+
this.log.error('PuppetService', 'stopping failed event stream failed: %s', stopError);
|
|
351
|
+
}
|
|
352
|
+
this.reportStreamError(error);
|
|
353
|
+
}
|
|
354
|
+
async onGrpcStreamEvent(event, epoch = this.eventEpoch) {
|
|
355
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
189
358
|
const type = event.getType();
|
|
190
359
|
const payload = event.getPayload();
|
|
191
360
|
const seq = event.getSeq();
|
|
@@ -199,15 +368,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
199
368
|
data: `onGrpcStreamEvent(${EventTypeRev[type]})`,
|
|
200
369
|
});
|
|
201
370
|
}
|
|
202
|
-
if (seq && !envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
203
|
-
const { lastEventSeq } = await this.getMiscellaneousStoreData();
|
|
204
|
-
if (!lastEventSeq || (seq > Number(lastEventSeq) || seq === 1)) {
|
|
205
|
-
await this.setMiscellaneousStoreData({
|
|
206
|
-
lastEventSeq: seq.toString(),
|
|
207
|
-
lastEventTimestamp: timestamp,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
371
|
switch (type) {
|
|
212
372
|
case grpcPuppet.EventType.EVENT_TYPE_DONG:
|
|
213
373
|
this.emit('dong', JSON.parse(payload));
|
|
@@ -230,11 +390,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
230
390
|
const loginPayload = JSON.parse(payload);
|
|
231
391
|
if (!envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
232
392
|
const { accountId } = await this.getMiscellaneousStoreData();
|
|
393
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
233
396
|
if (accountId !== loginPayload.contactId) {
|
|
234
|
-
await this.resetMiscellaneousStoreData();
|
|
397
|
+
await this.resetMiscellaneousStoreData(epoch);
|
|
235
398
|
await this.setMiscellaneousStoreData({
|
|
236
399
|
accountId: loginPayload.contactId,
|
|
237
|
-
});
|
|
400
|
+
}, epoch);
|
|
238
401
|
}
|
|
239
402
|
}
|
|
240
403
|
(async () => this.login(loginPayload.contactId))().catch(e => this.log.error('PuppetService', 'onGrpcStreamEvent() this.login() rejection %s', e.message));
|
|
@@ -244,7 +407,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
244
407
|
{
|
|
245
408
|
const logoutPayload = JSON.parse(payload);
|
|
246
409
|
if (!envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
247
|
-
await this.resetMiscellaneousStoreData();
|
|
410
|
+
await this.resetMiscellaneousStoreData(epoch);
|
|
248
411
|
}
|
|
249
412
|
;
|
|
250
413
|
(async () => this.logout(logoutPayload.data))().catch(e => this.log.error('PuppetService', 'onGrpcStreamEvent() this.logout() rejection %s', e.message));
|
|
@@ -252,8 +415,25 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
252
415
|
break;
|
|
253
416
|
case grpcPuppet.EventType.EVENT_TYPE_DIRTY:
|
|
254
417
|
await this.fastDirty(JSON.parse(payload));
|
|
418
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
255
421
|
this.emit('dirty', JSON.parse(payload));
|
|
256
422
|
break;
|
|
423
|
+
case grpcPuppet.EventType.EVENT_TYPE_ROOM_MEMBER_LOAD: {
|
|
424
|
+
const loadPayload = JSON.parse(payload);
|
|
425
|
+
validateRoomMemberLoadState(loadPayload);
|
|
426
|
+
this.roomCache.invalidate(loadPayload.roomId);
|
|
427
|
+
await this.roomCache.run(loadPayload.roomId, async () => {
|
|
428
|
+
await this._payloadStore.room?.delete(loadPayload.roomId);
|
|
429
|
+
await this._payloadStore.roomMember?.delete(loadPayload.roomId);
|
|
430
|
+
});
|
|
431
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.emit('room-member-load', loadPayload);
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
257
437
|
case grpcPuppet.EventType.EVENT_TYPE_MESSAGE:
|
|
258
438
|
this.emit('message', JSON.parse(payload));
|
|
259
439
|
break;
|
|
@@ -316,10 +496,19 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
316
496
|
case grpcPuppet.EventType.EVENT_TYPE_CALL:
|
|
317
497
|
this.emit('call', JSON.parse(payload));
|
|
318
498
|
break;
|
|
499
|
+
case grpcPuppet.EventType.EVENT_TYPE_ORG_BROADCAST_CREATED:
|
|
500
|
+
this.emit('org-broadcast-created', JSON.parse(payload));
|
|
501
|
+
break;
|
|
502
|
+
case grpcPuppet.EventType.EVENT_TYPE_ORG_BROADCAST_SENT:
|
|
503
|
+
this.emit('org-broadcast-sent', JSON.parse(payload));
|
|
504
|
+
break;
|
|
319
505
|
default:
|
|
320
506
|
// Huan(202003): in default, the `type` type should be `never`, please check.
|
|
321
507
|
this.log.error(`eventType ${type} unsupported! data: ${payload}`);
|
|
322
508
|
}
|
|
509
|
+
if (seq && this.isEventEpochCurrent(epoch) && !envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
510
|
+
await this.commitEventCheckpoint(seq, timestamp, epoch);
|
|
511
|
+
}
|
|
323
512
|
}
|
|
324
513
|
async logout(reason) {
|
|
325
514
|
this.log.verbose('PuppetService', 'logout(%s)', reason ? `"${reason}"` : '');
|
|
@@ -375,8 +564,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
375
564
|
[PUPPET.types.Dirty.Friendship]: async (_) => { },
|
|
376
565
|
[PUPPET.types.Dirty.Message]: async (_) => { },
|
|
377
566
|
[PUPPET.types.Dirty.Post]: async (_) => { },
|
|
378
|
-
[PUPPET.types.Dirty.Room]: async (id) =>
|
|
379
|
-
|
|
567
|
+
[PUPPET.types.Dirty.Room]: async (id) => {
|
|
568
|
+
this.roomCache.invalidate(id);
|
|
569
|
+
return this.roomCache.run(id, async () => this._payloadStore.room?.delete(id));
|
|
570
|
+
},
|
|
571
|
+
[PUPPET.types.Dirty.RoomMember]: async (id) => {
|
|
572
|
+
this.roomCache.invalidate(id);
|
|
573
|
+
return this.roomCache.run(id, async () => this._payloadStore.roomMember?.delete(id));
|
|
574
|
+
},
|
|
380
575
|
[PUPPET.types.Dirty.Tag]: async (id) => this._payloadStore.tag?.delete(id),
|
|
381
576
|
[PUPPET.types.Dirty.TagGroup]: async (id) => this._payloadStore.tagGroup?.delete(id),
|
|
382
577
|
[PUPPET.types.Dirty.Unspecified]: async (id) => { throw new Error('Unspecified type with id: ' + id); },
|
|
@@ -1545,8 +1740,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1545
1740
|
*/
|
|
1546
1741
|
async roomRawPayload(id) {
|
|
1547
1742
|
this.log.verbose('PuppetService', 'roomRawPayload(%s)', id);
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1743
|
+
const generation = this.roomCache.generation(id);
|
|
1744
|
+
const cachedPayload = await this.roomCache.run(id, async () => this._payloadStore.room?.get(id));
|
|
1745
|
+
if (cachedPayload && generation === this.roomCache.generation(id)) {
|
|
1550
1746
|
this.log.silly('PuppetService', 'roomRawPayload(%s) cache HIT', id);
|
|
1551
1747
|
return cachedPayload;
|
|
1552
1748
|
}
|
|
@@ -1554,26 +1750,52 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1554
1750
|
request.setId(id);
|
|
1555
1751
|
const response = await util.promisify(this.grpcManager.client.roomPayload
|
|
1556
1752
|
.bind(this.grpcManager.client))(request);
|
|
1557
|
-
const payload =
|
|
1558
|
-
|
|
1559
|
-
avatar: response.getAvatar(),
|
|
1560
|
-
handle: response.getHandle(),
|
|
1561
|
-
id: response.getId(),
|
|
1562
|
-
memberIdList: response.getMemberIdsList(),
|
|
1563
|
-
ownerId: response.getOwnerId(),
|
|
1564
|
-
topic: response.getTopic(),
|
|
1565
|
-
additionalInfo: response.getAdditionalInfo(),
|
|
1566
|
-
remark: response.getRoomRemark(),
|
|
1567
|
-
external: response.getExternal(),
|
|
1568
|
-
};
|
|
1569
|
-
const createTime = response.getCreateTime();
|
|
1570
|
-
if (createTime) {
|
|
1571
|
-
payload.createTime = millisecondsFromTimestamp(createTime);
|
|
1572
|
-
}
|
|
1573
|
-
await this._payloadStore.room?.set(id, payload);
|
|
1753
|
+
const payload = roomPbToPayload(response);
|
|
1754
|
+
await this.roomCache.commit(id, generation, async () => this._payloadStore.room?.set(id, payload));
|
|
1574
1755
|
this.log.silly('PuppetService', 'roomRawPayload(%s) cache SET', id);
|
|
1575
1756
|
return payload;
|
|
1576
1757
|
}
|
|
1758
|
+
async roomMemberSync(roomId, options) {
|
|
1759
|
+
if (!roomId) {
|
|
1760
|
+
throw new Error('roomId is required');
|
|
1761
|
+
}
|
|
1762
|
+
const request = new grpcPuppet.RoomMemberSyncRequest();
|
|
1763
|
+
request.setId(roomId);
|
|
1764
|
+
if (options?.force !== undefined) {
|
|
1765
|
+
request.setForce(options.force);
|
|
1766
|
+
}
|
|
1767
|
+
const response = await util.promisify(this.grpcManager.client.roomMemberSync.bind(this.grpcManager.client))(request);
|
|
1768
|
+
return roomMemberLoadStatePbToPayload(response, roomId);
|
|
1769
|
+
}
|
|
1770
|
+
async roomMemberLoadState(roomId) {
|
|
1771
|
+
if (!roomId) {
|
|
1772
|
+
throw new Error('roomId is required');
|
|
1773
|
+
}
|
|
1774
|
+
const request = new grpcPuppet.RoomMemberLoadStateRequest();
|
|
1775
|
+
request.setId(roomId);
|
|
1776
|
+
const response = await util.promisify(this.grpcManager.client.roomMemberLoadState.bind(this.grpcManager.client))(request);
|
|
1777
|
+
return roomMemberLoadStatePbToPayload(response, roomId);
|
|
1778
|
+
}
|
|
1779
|
+
async roomMemberSnapshot(roomId, revision, options) {
|
|
1780
|
+
if (!roomId) {
|
|
1781
|
+
throw new Error('roomId is required');
|
|
1782
|
+
}
|
|
1783
|
+
validateRoomMemberRevision(revision);
|
|
1784
|
+
const request = new grpcPuppet.RoomMemberSnapshotRequest();
|
|
1785
|
+
request.setId(roomId);
|
|
1786
|
+
request.setRevision(revision);
|
|
1787
|
+
if (options?.pageToken !== undefined) {
|
|
1788
|
+
request.setPageToken(options.pageToken);
|
|
1789
|
+
}
|
|
1790
|
+
if (options?.pageSize !== undefined) {
|
|
1791
|
+
if (!Number.isInteger(options.pageSize) || options.pageSize < 0 || options.pageSize > 500) {
|
|
1792
|
+
throw new Error('pageSize must be an integer between 0 and 500');
|
|
1793
|
+
}
|
|
1794
|
+
request.setPageSize(options.pageSize);
|
|
1795
|
+
}
|
|
1796
|
+
const response = await util.promisify(this.grpcManager.client.roomMemberSnapshot.bind(this.grpcManager.client))(request);
|
|
1797
|
+
return roomMemberSnapshotPbToPayload(response, roomId, revision);
|
|
1798
|
+
}
|
|
1577
1799
|
async roomRawPayloadParser(payload) {
|
|
1578
1800
|
// this.log.silly('PuppetService', 'roomRawPayloadParser({id:%s})', payload.id)
|
|
1579
1801
|
// passthrough
|
|
@@ -1583,10 +1805,11 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1583
1805
|
this.log.verbose('PuppetService', 'batchRoomRawPayload(%s)', roomIdList);
|
|
1584
1806
|
const result = new Map();
|
|
1585
1807
|
const roomIdSet = new Set(roomIdList);
|
|
1808
|
+
const generations = new Map(Array.from(roomIdSet, id => [id, this.roomCache.generation(id)]));
|
|
1586
1809
|
const needGetSet = new Set();
|
|
1587
1810
|
for (const roomId of roomIdSet) {
|
|
1588
|
-
const cachedRoomPayload = await this._payloadStore.room?.get(roomId);
|
|
1589
|
-
if (cachedRoomPayload) {
|
|
1811
|
+
const cachedRoomPayload = await this.roomCache.run(roomId, async () => this._payloadStore.room?.get(roomId));
|
|
1812
|
+
if (cachedRoomPayload && generations.get(roomId) === this.roomCache.generation(roomId)) {
|
|
1590
1813
|
result.set(roomId, cachedRoomPayload);
|
|
1591
1814
|
}
|
|
1592
1815
|
else {
|
|
@@ -1602,27 +1825,18 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1602
1825
|
const payloads = response.getRoomPayloadsList();
|
|
1603
1826
|
for (const payload of payloads) {
|
|
1604
1827
|
const roomId = payload.getId();
|
|
1605
|
-
const puppetPayload =
|
|
1606
|
-
adminIdList: payload.getAdminIdsList(),
|
|
1607
|
-
avatar: payload.getAvatar(),
|
|
1608
|
-
handle: payload.getHandle(),
|
|
1609
|
-
id: payload.getId(),
|
|
1610
|
-
memberIdList: payload.getMemberIdsList(),
|
|
1611
|
-
ownerId: payload.getOwnerId(),
|
|
1612
|
-
topic: payload.getTopic(),
|
|
1613
|
-
additionalInfo: payload.getAdditionalInfo(),
|
|
1614
|
-
remark: payload.getRoomRemark(),
|
|
1615
|
-
external: payload.getExternal(),
|
|
1616
|
-
};
|
|
1617
|
-
const createTime = payload.getCreateTime();
|
|
1618
|
-
if (createTime) {
|
|
1619
|
-
puppetPayload.createTime = millisecondsFromTimestamp(createTime);
|
|
1620
|
-
}
|
|
1828
|
+
const puppetPayload = roomPbToPayload(payload);
|
|
1621
1829
|
result.set(roomId, puppetPayload);
|
|
1622
|
-
|
|
1830
|
+
const generation = generations.get(roomId);
|
|
1831
|
+
if (generation !== undefined) {
|
|
1832
|
+
await this.roomCache.commit(roomId, generation, async () => this._payloadStore.room?.set(roomId, puppetPayload));
|
|
1833
|
+
}
|
|
1623
1834
|
}
|
|
1624
1835
|
}
|
|
1625
1836
|
catch (e) {
|
|
1837
|
+
if (e instanceof RoomCacheOperationError) {
|
|
1838
|
+
throw e;
|
|
1839
|
+
}
|
|
1626
1840
|
this.log.error('PuppetService', 'batchRoomRawPayload(%s, %s) error: %s, use one by one method', roomIdList, needGetSet, e);
|
|
1627
1841
|
let fallbackSuccessCount = 0;
|
|
1628
1842
|
for (const roomId of needGetSet) {
|
|
@@ -1808,9 +2022,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1808
2022
|
}
|
|
1809
2023
|
async roomMemberRawPayload(roomId, contactId) {
|
|
1810
2024
|
this.log.verbose('PuppetService', 'roomMemberRawPayload(%s, %s)', roomId, contactId);
|
|
1811
|
-
const
|
|
2025
|
+
const generation = this.roomCache.generation(roomId);
|
|
2026
|
+
const cachedPayload = await this.roomCache.run(roomId, async () => this._payloadStore.roomMember?.get(roomId));
|
|
1812
2027
|
const cachedRoomMemberPayload = cachedPayload && cachedPayload[contactId];
|
|
1813
|
-
if (cachedRoomMemberPayload) {
|
|
2028
|
+
if (cachedRoomMemberPayload && generation === this.roomCache.generation(roomId)) {
|
|
1814
2029
|
this.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache HIT', roomId, contactId);
|
|
1815
2030
|
return cachedRoomMemberPayload;
|
|
1816
2031
|
}
|
|
@@ -1829,9 +2044,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1829
2044
|
joinScene: response.getJoinScene(),
|
|
1830
2045
|
joinTime: response.getJoinTime(),
|
|
1831
2046
|
};
|
|
1832
|
-
await this.
|
|
1833
|
-
|
|
1834
|
-
[contactId]: payload
|
|
2047
|
+
await this.roomCache.commit(roomId, generation, async () => {
|
|
2048
|
+
const current = await this._payloadStore.roomMember?.get(roomId);
|
|
2049
|
+
await this._payloadStore.roomMember?.set(roomId, { ...current, [contactId]: payload });
|
|
1835
2050
|
});
|
|
1836
2051
|
this.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache SET', roomId, contactId);
|
|
1837
2052
|
return payload;
|
|
@@ -1843,10 +2058,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1843
2058
|
}
|
|
1844
2059
|
async batchRoomMemberRawPayload(roomId, contactIdList) {
|
|
1845
2060
|
this.log.verbose('PuppetService', 'batchRoomMemberRawPayload(%s, %s)', roomId, contactIdList);
|
|
2061
|
+
const generation = this.roomCache.generation(roomId);
|
|
1846
2062
|
const result = new Map();
|
|
1847
2063
|
const contactIdSet = new Set(contactIdList);
|
|
1848
2064
|
let needGetSet = new Set();
|
|
1849
|
-
const
|
|
2065
|
+
const cached = await this.roomCache.run(roomId, async () => this._payloadStore.roomMember?.get(roomId));
|
|
2066
|
+
const cachedPayload = generation === this.roomCache.generation(roomId) ? cached || {} : {};
|
|
1850
2067
|
if (Object.keys(cachedPayload).length > 0) {
|
|
1851
2068
|
for (const contactId of contactIdSet) {
|
|
1852
2069
|
const cachedRoomMemberPayload = cachedPayload[contactId];
|
|
@@ -1869,15 +2086,22 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
1869
2086
|
const response = await util.promisify(this.grpcManager.client.batchRoomMemberPayload
|
|
1870
2087
|
.bind(this.grpcManager.client))(request);
|
|
1871
2088
|
const payloads = response.getMemberPayloadsList();
|
|
2089
|
+
const fetched = {};
|
|
1872
2090
|
for (const payload of payloads) {
|
|
1873
2091
|
const contactId = payload.getId();
|
|
1874
2092
|
const puppetPayload = roomMemberPbToPayload(payload);
|
|
1875
2093
|
result.set(contactId, puppetPayload);
|
|
1876
|
-
|
|
2094
|
+
fetched[contactId] = puppetPayload;
|
|
1877
2095
|
}
|
|
1878
|
-
await this.
|
|
2096
|
+
await this.roomCache.commit(roomId, generation, async () => {
|
|
2097
|
+
const current = await this._payloadStore.roomMember?.get(roomId);
|
|
2098
|
+
await this._payloadStore.roomMember?.set(roomId, { ...current, ...fetched });
|
|
2099
|
+
});
|
|
1879
2100
|
}
|
|
1880
2101
|
catch (e) {
|
|
2102
|
+
if (e instanceof RoomCacheOperationError) {
|
|
2103
|
+
throw e;
|
|
2104
|
+
}
|
|
1881
2105
|
this.log.error('PuppetService', 'batchRoomMemberRawPayload(%s, %s) error: %s, use one by one method', roomId, needGetSet, e);
|
|
1882
2106
|
for (const contactId of needGetSet) {
|
|
1883
2107
|
const payload = await this.roomMemberRawPayload(roomId, contactId);
|
|
@@ -2568,6 +2792,42 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2568
2792
|
}
|
|
2569
2793
|
return result;
|
|
2570
2794
|
}
|
|
2795
|
+
async orgBroadcastPayload(orgBroadcastId) {
|
|
2796
|
+
this.log.verbose('PuppetService', 'orgBroadcastPayload(%s)', orgBroadcastId);
|
|
2797
|
+
const request = new grpcPuppet.OrgBroadcastPayloadRequest();
|
|
2798
|
+
request.setId(orgBroadcastId);
|
|
2799
|
+
const response = await util.promisify(this.grpcManager.client.orgBroadcastPayload.bind(this.grpcManager.client))(request);
|
|
2800
|
+
return {
|
|
2801
|
+
id: response.getId(),
|
|
2802
|
+
sendType: response.getSendType(),
|
|
2803
|
+
conversationType: response.getConversationType(),
|
|
2804
|
+
creatorId: response.getCreatorId() || undefined,
|
|
2805
|
+
execTime: response.getExecTime(),
|
|
2806
|
+
status: response.getStatus(),
|
|
2807
|
+
canCancel: response.getCanCancel(),
|
|
2808
|
+
allowSelect: response.getAllowSelect(),
|
|
2809
|
+
sent: response.getSent(),
|
|
2810
|
+
totalCount: response.getTotalCount(),
|
|
2811
|
+
sentCount: response.getSentCount(),
|
|
2812
|
+
contentListJson: response.getContentListJson(),
|
|
2813
|
+
targets: response.getTargetsList().map(targetPb => ({
|
|
2814
|
+
contactId: targetPb.getContactId() || undefined,
|
|
2815
|
+
roomId: targetPb.getRoomId() || undefined,
|
|
2816
|
+
status: targetPb.getStatus(),
|
|
2817
|
+
})),
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
async orgBroadcastExecute(orgBroadcastId, targetIds) {
|
|
2821
|
+
this.log.verbose('PuppetService', 'orgBroadcastExecute(%s, %s)', orgBroadcastId, targetIds?.length);
|
|
2822
|
+
// on the wire an empty target_ids means all targets, so an explicit empty list must not pass through
|
|
2823
|
+
if (targetIds && targetIds.length === 0) {
|
|
2824
|
+
throw new Error('targetIds is empty, omit it to send to all targets of the org broadcast');
|
|
2825
|
+
}
|
|
2826
|
+
const request = new grpcPuppet.OrgBroadcastExecuteRequest();
|
|
2827
|
+
request.setId(orgBroadcastId);
|
|
2828
|
+
request.setTargetIdsList(targetIds ?? []);
|
|
2829
|
+
await util.promisify(this.grpcManager.client.orgBroadcastExecute.bind(this.grpcManager.client))(request);
|
|
2830
|
+
}
|
|
2571
2831
|
healthCheckInterval;
|
|
2572
2832
|
startHealthCheck() {
|
|
2573
2833
|
this.healthCheckInterval = setInterval(() => {
|
|
@@ -2583,23 +2843,40 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2583
2843
|
waitingForReady = false;
|
|
2584
2844
|
reconnectIndicator;
|
|
2585
2845
|
async reset() {
|
|
2586
|
-
if (!this._grpcManager) {
|
|
2587
|
-
this.log.warn('PuppetService', 'grpc manager not constructed, perform regular reset');
|
|
2588
|
-
return super.reset();
|
|
2589
|
-
}
|
|
2590
|
-
if (!this.isLoggedIn) {
|
|
2591
|
-
this.log.warn('PuppetService', 'puppet not logged in, perform regular reset');
|
|
2592
|
-
return super.reset();
|
|
2593
|
-
}
|
|
2594
2846
|
if (this.reconnectIndicator.value()) {
|
|
2595
2847
|
this.log.warn('PuppetService', 'already trying to reconnect, pass this one');
|
|
2596
2848
|
return;
|
|
2597
2849
|
}
|
|
2598
2850
|
this.reconnectIndicator.value(true);
|
|
2599
|
-
this.
|
|
2600
|
-
const
|
|
2851
|
+
this.suspendEventProcessing();
|
|
2852
|
+
const epoch = this.eventEpoch;
|
|
2853
|
+
const manager = this._grpcManager;
|
|
2854
|
+
try {
|
|
2855
|
+
await this.checkpointQueue;
|
|
2856
|
+
if (epoch !== this.eventEpoch) {
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
if (!manager || !this.isLoggedIn) {
|
|
2860
|
+
this.log.warn('PuppetService', 'grpc manager unavailable or puppet logged out, perform regular reset');
|
|
2861
|
+
return await super.reset();
|
|
2862
|
+
}
|
|
2863
|
+
await this.resetEventStream(manager, epoch);
|
|
2864
|
+
}
|
|
2865
|
+
finally {
|
|
2866
|
+
this.reconnectIndicator.value(false);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
async resetEventStream(manager, epoch) {
|
|
2870
|
+
manager.stopStream();
|
|
2871
|
+
const { lastEventSeq, accountId } = await this.eventResumePosition(epoch);
|
|
2872
|
+
if (epoch !== this.eventEpoch || manager !== this._grpcManager) {
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2601
2875
|
const onLoginResolve = (resolve) => {
|
|
2602
2876
|
const onLogin = (event) => {
|
|
2877
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2603
2880
|
const type = event.getType();
|
|
2604
2881
|
const payload = event.getPayload();
|
|
2605
2882
|
if (this.waitingForLogin && type === grpcPuppet.EventType.EVENT_TYPE_LOGIN) {
|
|
@@ -2615,6 +2892,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2615
2892
|
};
|
|
2616
2893
|
const onReadyResolve = (resolve) => {
|
|
2617
2894
|
const onReady = (event) => {
|
|
2895
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2618
2898
|
const type = event.getType();
|
|
2619
2899
|
if (this.waitingForReady && type === grpcPuppet.EventType.EVENT_TYPE_READY) {
|
|
2620
2900
|
this.waitingForReady = false;
|
|
@@ -2623,60 +2903,85 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2623
2903
|
};
|
|
2624
2904
|
return onReady;
|
|
2625
2905
|
};
|
|
2626
|
-
|
|
2627
|
-
let onReady;
|
|
2906
|
+
const removeListeners = [];
|
|
2628
2907
|
const loginFuture = new Promise(resolve => {
|
|
2629
|
-
onLogin = onLoginResolve(resolve);
|
|
2630
|
-
|
|
2908
|
+
const onLogin = onLoginResolve(resolve);
|
|
2909
|
+
manager.on('data', onLogin);
|
|
2910
|
+
removeListeners.push(() => { manager.off('data', onLogin); });
|
|
2631
2911
|
});
|
|
2632
2912
|
const readyFuture = new Promise(resolve => {
|
|
2633
|
-
onReady = onReadyResolve(resolve);
|
|
2634
|
-
|
|
2913
|
+
const onReady = onReadyResolve(resolve);
|
|
2914
|
+
manager.on('data', onReady);
|
|
2915
|
+
removeListeners.push(() => { manager.off('data', onReady); });
|
|
2635
2916
|
});
|
|
2636
2917
|
const startTime = Date.now();
|
|
2637
2918
|
const timeoutMilliseconds = this.timeoutMilliseconds / 10; // 2 min default, 4 min xiaoju-bot
|
|
2638
|
-
|
|
2919
|
+
this.waitingForLogin = true;
|
|
2920
|
+
this.waitingForReady = true;
|
|
2921
|
+
this.eventsBlocked = false;
|
|
2922
|
+
try {
|
|
2923
|
+
while (true) {
|
|
2924
|
+
try {
|
|
2925
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
await timeoutPromise(manager.startStream(lastEventSeq, accountId), 30 * 1000);
|
|
2929
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2930
|
+
return;
|
|
2931
|
+
}
|
|
2932
|
+
break;
|
|
2933
|
+
}
|
|
2934
|
+
catch (e) {
|
|
2935
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
if (Date.now() - startTime < timeoutMilliseconds) {
|
|
2939
|
+
this.log.warn('failed to start stream, will try again in 15 seconds');
|
|
2940
|
+
await new Promise(resolve => {
|
|
2941
|
+
setTimeout(resolve, 5000);
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
else {
|
|
2945
|
+
this.log.warn('failed to start stream and reaches timeout, will perform regular reset');
|
|
2946
|
+
return super.reset();
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2639
2950
|
try {
|
|
2640
|
-
await timeoutPromise(
|
|
2641
|
-
|
|
2951
|
+
await timeoutPromise(loginFuture, ResetLoginTimeout)
|
|
2952
|
+
.finally(() => {
|
|
2953
|
+
if (epoch === this.eventEpoch) {
|
|
2954
|
+
this.waitingForLogin = false;
|
|
2955
|
+
}
|
|
2956
|
+
});
|
|
2642
2957
|
}
|
|
2643
2958
|
catch (e) {
|
|
2644
|
-
if (
|
|
2645
|
-
|
|
2646
|
-
await new Promise(resolve => {
|
|
2647
|
-
setTimeout(resolve, 5000);
|
|
2648
|
-
});
|
|
2649
|
-
}
|
|
2650
|
-
else {
|
|
2651
|
-
this.log.warn('failed to start stream and reaches timeout, will perform regular reset');
|
|
2652
|
-
this.reconnectIndicator.value(false);
|
|
2653
|
-
return super.reset();
|
|
2959
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
2960
|
+
return;
|
|
2654
2961
|
}
|
|
2962
|
+
this.log.warn('PuppetService', 'waiting for event reset login error, will perform regular reset');
|
|
2963
|
+
return super.reset();
|
|
2964
|
+
}
|
|
2965
|
+
try {
|
|
2966
|
+
await timeoutPromise(readyFuture, ResetReadyTimeout)
|
|
2967
|
+
.finally(() => {
|
|
2968
|
+
if (epoch === this.eventEpoch) {
|
|
2969
|
+
this.waitingForReady = false;
|
|
2970
|
+
}
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
catch (e) {
|
|
2974
|
+
this.log.warn('PuppetService', 'waiting for event reset ready error, will do nothing');
|
|
2655
2975
|
}
|
|
2656
2976
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2977
|
+
finally {
|
|
2978
|
+
for (const removeListener of removeListeners) {
|
|
2979
|
+
removeListener();
|
|
2980
|
+
}
|
|
2981
|
+
if (epoch === this.eventEpoch) {
|
|
2662
2982
|
this.waitingForLogin = false;
|
|
2663
|
-
this.reconnectIndicator.value(false);
|
|
2664
|
-
this.grpcManager.off('data', onLogin);
|
|
2665
|
-
});
|
|
2666
|
-
}
|
|
2667
|
-
catch (e) {
|
|
2668
|
-
this.log.warn('PuppetService', 'waiting for event reset login error, will perform regular reset');
|
|
2669
|
-
return super.reset();
|
|
2670
|
-
}
|
|
2671
|
-
try {
|
|
2672
|
-
await timeoutPromise(readyFuture, ResetReadyTimeout)
|
|
2673
|
-
.finally(() => {
|
|
2674
2983
|
this.waitingForReady = false;
|
|
2675
|
-
|
|
2676
|
-
});
|
|
2677
|
-
}
|
|
2678
|
-
catch (e) {
|
|
2679
|
-
this.log.warn('PuppetService', 'waiting for event reset ready error, will do nothing');
|
|
2984
|
+
}
|
|
2680
2985
|
}
|
|
2681
2986
|
}
|
|
2682
2987
|
async getMiscellaneousStoreData() {
|
|
@@ -2700,27 +3005,29 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2700
3005
|
accountId,
|
|
2701
3006
|
};
|
|
2702
3007
|
}
|
|
2703
|
-
async setMiscellaneousStoreData(data) {
|
|
3008
|
+
async setMiscellaneousStoreData(data, epoch) {
|
|
2704
3009
|
if (envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
2705
3010
|
return;
|
|
2706
3011
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
3012
|
+
const changes = new Map();
|
|
3013
|
+
if (data.lastEventTimestamp !== undefined) {
|
|
3014
|
+
changes.set('lastEventTimestamp', data.lastEventTimestamp);
|
|
2709
3015
|
}
|
|
2710
|
-
if (
|
|
2711
|
-
|
|
3016
|
+
if (data.accountId !== undefined) {
|
|
3017
|
+
changes.set('accountId', data.accountId);
|
|
2712
3018
|
}
|
|
2713
|
-
if (
|
|
2714
|
-
|
|
3019
|
+
if (data.lastEventSeq !== undefined) {
|
|
3020
|
+
changes.set('lastEventSeq', data.lastEventSeq);
|
|
2715
3021
|
}
|
|
3022
|
+
await this.withEventCheckpoint(async () => this.writeEventMetadata(changes, epoch));
|
|
2716
3023
|
}
|
|
2717
|
-
async resetMiscellaneousStoreData() {
|
|
3024
|
+
async resetMiscellaneousStoreData(epoch) {
|
|
2718
3025
|
if (envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
2719
3026
|
return;
|
|
2720
3027
|
}
|
|
2721
|
-
await this.
|
|
2722
|
-
|
|
2723
|
-
|
|
3028
|
+
await this.withEventCheckpoint(async () => this.writeEventMetadata(new Map([
|
|
3029
|
+
['lastEventSeq', undefined], ['lastEventTimestamp', undefined], ['accountId', undefined],
|
|
3030
|
+
]), epoch));
|
|
2724
3031
|
}
|
|
2725
3032
|
}
|
|
2726
3033
|
export { PuppetService, };
|