@juzi/wechaty-puppet-service 1.0.131 → 1.0.132
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/client/grpc-manager.d.ts +4 -1
- package/dist/cjs/src/client/grpc-manager.d.ts.map +1 -1
- package/dist/cjs/src/client/grpc-manager.js +76 -19
- package/dist/cjs/src/client/grpc-manager.js.map +1 -1
- package/dist/cjs/src/client/payload-store.d.ts +1 -0
- package/dist/cjs/src/client/payload-store.d.ts.map +1 -1
- package/dist/cjs/src/client/payload-store.js +1 -1
- package/dist/cjs/src/client/payload-store.js.map +1 -1
- package/dist/cjs/src/client/puppet-service.d.ts +14 -0
- package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +208 -13
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/client/recovery.d.ts +65 -0
- package/dist/cjs/src/client/recovery.d.ts.map +1 -0
- package/dist/cjs/src/client/recovery.js +339 -0
- package/dist/cjs/src/client/recovery.js.map +1 -0
- package/dist/cjs/src/client/recovery.spec.d.ts +2 -0
- package/dist/cjs/src/client/recovery.spec.d.ts.map +1 -0
- package/dist/cjs/src/client/recovery.spec.js +686 -0
- package/dist/cjs/src/client/recovery.spec.js.map +1 -0
- package/dist/cjs/src/mod.d.ts +3 -0
- package/dist/cjs/src/mod.d.ts.map +1 -1
- package/dist/cjs/src/mod.js +3 -1
- package/dist/cjs/src/mod.js.map +1 -1
- package/dist/cjs/src/package-json.js +2 -2
- package/dist/esm/src/client/grpc-manager.d.ts +4 -1
- package/dist/esm/src/client/grpc-manager.d.ts.map +1 -1
- package/dist/esm/src/client/grpc-manager.js +76 -19
- package/dist/esm/src/client/grpc-manager.js.map +1 -1
- package/dist/esm/src/client/payload-store.d.ts +1 -0
- package/dist/esm/src/client/payload-store.d.ts.map +1 -1
- package/dist/esm/src/client/payload-store.js +1 -1
- package/dist/esm/src/client/payload-store.js.map +1 -1
- package/dist/esm/src/client/puppet-service.d.ts +14 -0
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +208 -13
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/client/recovery.d.ts +65 -0
- package/dist/esm/src/client/recovery.d.ts.map +1 -0
- package/dist/esm/src/client/recovery.js +334 -0
- package/dist/esm/src/client/recovery.js.map +1 -0
- package/dist/esm/src/client/recovery.spec.d.ts +2 -0
- package/dist/esm/src/client/recovery.spec.d.ts.map +1 -0
- package/dist/esm/src/client/recovery.spec.js +681 -0
- package/dist/esm/src/client/recovery.spec.js.map +1 -0
- package/dist/esm/src/mod.d.ts +3 -0
- package/dist/esm/src/mod.d.ts.map +1 -1
- package/dist/esm/src/mod.js +1 -0
- package/dist/esm/src/mod.js.map +1 -1
- package/dist/esm/src/package-json.js +2 -2
- package/package.json +2 -2
- package/src/client/grpc-manager.ts +57 -22
- package/src/client/payload-store.ts +2 -1
- package/src/client/puppet-service.ts +169 -13
- package/src/client/recovery.spec.ts +595 -0
- package/src/client/recovery.ts +301 -0
- package/src/mod.ts +3 -0
- package/src/package-json.ts +2 -2
|
@@ -56,6 +56,7 @@ const config_js_1 = require("../config.js");
|
|
|
56
56
|
const event_type_rev_js_1 = require("../event-type-rev.js");
|
|
57
57
|
const package_json_js_1 = require("../package-json.js");
|
|
58
58
|
const grpc_manager_js_1 = require("./grpc-manager.js");
|
|
59
|
+
const recovery_js_1 = require("./recovery.js");
|
|
59
60
|
const payload_store_js_1 = require("./payload-store.js");
|
|
60
61
|
const room_cache_coordinator_js_1 = require("./room-cache-coordinator.js");
|
|
61
62
|
const pb_payload_helper_js_1 = require("../utils/pb-payload-helper.js");
|
|
@@ -87,6 +88,18 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
87
88
|
checkpointQueue = Promise.resolve();
|
|
88
89
|
pendingCheckpointRollback;
|
|
89
90
|
timeoutMilliseconds;
|
|
91
|
+
recovery;
|
|
92
|
+
recoveryTimer;
|
|
93
|
+
inactivityTimer;
|
|
94
|
+
recoveryStopped = true;
|
|
95
|
+
recoveryProcessedSeq = 0;
|
|
96
|
+
get recoveryState() { return this.recovery.state; }
|
|
97
|
+
get eventMetadataEnabled() {
|
|
98
|
+
return this.recovery.enabled || !config_js_1.envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE();
|
|
99
|
+
}
|
|
100
|
+
withRecoveryRequestContext(context, operation) {
|
|
101
|
+
return (0, recovery_js_1.withRecoveryRequestContext)(context, operation);
|
|
102
|
+
}
|
|
90
103
|
_grpcManager;
|
|
91
104
|
get grpcManager() {
|
|
92
105
|
if (!this._grpcManager) {
|
|
@@ -104,9 +117,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
104
117
|
constructor(options = {}) {
|
|
105
118
|
super(options);
|
|
106
119
|
this.options = options;
|
|
120
|
+
this.recovery = new recovery_js_1.RecoveryGate(options.recovery, () => {
|
|
121
|
+
this.stopRecovery('Recovery budget exhausted');
|
|
122
|
+
this.reportStreamError(new Error('Recovery budget exhausted'));
|
|
123
|
+
});
|
|
107
124
|
this._payloadStore = new payload_store_js_1.PayloadStore({
|
|
108
125
|
token: config_js_1.envVars.WECHATY_PUPPET_SERVICE_TOKEN(this.options.token),
|
|
109
126
|
logger: this.options.logger,
|
|
127
|
+
recoveryCursor: this.recovery.enabled,
|
|
110
128
|
});
|
|
111
129
|
this.hookPayloadStore();
|
|
112
130
|
this.FileBoxUuid = (0, mod_js_1.uuidifyFileBoxGrpc)(() => this.grpcManager.client);
|
|
@@ -146,7 +164,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
146
164
|
this._grpcManager = undefined;
|
|
147
165
|
}
|
|
148
166
|
this.log.info('PuppetService', 'start() instanciating GrpcManager ...');
|
|
149
|
-
const grpcManager = new grpc_manager_js_1.GrpcManager(this.options);
|
|
167
|
+
const grpcManager = new grpc_manager_js_1.GrpcManager(this.options, this.recovery);
|
|
150
168
|
this.log.info('PuppetService', 'start() instanciating GrpcManager ... done');
|
|
151
169
|
/**
|
|
152
170
|
* Huan(202108): when we started the event stream,
|
|
@@ -158,7 +176,19 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
158
176
|
this.log.info('PuppetService', 'start() setting up bridge grpc event stream ... done');
|
|
159
177
|
this.log.info('PuppetService', 'start() starting grpc manager...');
|
|
160
178
|
const { lastEventSeq, accountId } = await this.eventResumePosition();
|
|
161
|
-
|
|
179
|
+
this.recoveryStopped = false;
|
|
180
|
+
this.recovery.begin(accountId, true);
|
|
181
|
+
this.recoveryProcessedSeq = Number(lastEventSeq ?? 0);
|
|
182
|
+
this.touchRecoveryStream();
|
|
183
|
+
try {
|
|
184
|
+
await grpcManager.start(lastEventSeq, accountId);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (!this.recovery.enabled) {
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
this.requestRecovery(grpcManager);
|
|
191
|
+
}
|
|
162
192
|
this.log.info('PuppetService', 'start() starting grpc manager... done');
|
|
163
193
|
this.log.info('PuppetService', 'start healthCheck');
|
|
164
194
|
this.startHealthCheck();
|
|
@@ -166,13 +196,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
166
196
|
}
|
|
167
197
|
async onStop() {
|
|
168
198
|
this.log.info('PuppetService', 'onStop()');
|
|
199
|
+
this.stopRecovery('Puppet stopped');
|
|
169
200
|
this.suspendEventProcessing();
|
|
170
201
|
await this.checkpointQueue;
|
|
171
202
|
if (this._grpcManager) {
|
|
172
203
|
this.log.info('PuppetService', 'onStop() stopping grpc manager ...');
|
|
173
204
|
const grpcManager = this._grpcManager;
|
|
174
205
|
this._grpcManager = undefined;
|
|
175
|
-
await grpcManager.stop();
|
|
206
|
+
await this.recovery.runDetached(async () => grpcManager.stop());
|
|
176
207
|
this.log.info('PuppetService', 'onStop() stopping grpc manager ... done');
|
|
177
208
|
}
|
|
178
209
|
this.log.info('PuppetService', 'onStop() ... done');
|
|
@@ -207,6 +238,25 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
207
238
|
if (client !== this._grpcManager || this.eventsBlocked) {
|
|
208
239
|
return;
|
|
209
240
|
}
|
|
241
|
+
this.touchRecoveryStream();
|
|
242
|
+
if (this.recovery.enabled && event.getType() === wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGIN) {
|
|
243
|
+
try {
|
|
244
|
+
const payload = JSON.parse(event.getPayload());
|
|
245
|
+
if (!this.recovery.checkAccount(payload.contactId)) {
|
|
246
|
+
this.stopRecovery('Account changed or invalid login');
|
|
247
|
+
this.reportStreamError(new Error('Account changed or invalid login'));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
this.stopRecovery('Invalid login payload');
|
|
253
|
+
this.reportStreamError(error);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (this.recovery.enabled && event.getType() === wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGOUT) {
|
|
258
|
+
this.stopRecovery('Account logged out', false);
|
|
259
|
+
}
|
|
210
260
|
const epoch = this.eventEpoch;
|
|
211
261
|
this.eventQueue = this.eventQueue.then(async () => {
|
|
212
262
|
if (!this.ownsEventWorker(client, epoch)) {
|
|
@@ -217,10 +267,13 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
217
267
|
if (this.failedEventSeq !== undefined && event.getSeq() > this.failedEventSeq) {
|
|
218
268
|
throw new Error(`Replay skipped failed event ${this.failedEventSeq}; stream halted until reset`);
|
|
219
269
|
}
|
|
220
|
-
await this.onGrpcStreamEvent(event, epoch);
|
|
270
|
+
await this.recovery.runEvent(() => this.ownsEventWorker(client, epoch), async () => this.onGrpcStreamEvent(event, epoch));
|
|
221
271
|
if (!this.ownsEventWorker(client, epoch)) {
|
|
222
272
|
return undefined;
|
|
223
273
|
}
|
|
274
|
+
if (event.getSeq()) {
|
|
275
|
+
this.recoveryProcessedSeq = event.getSeq();
|
|
276
|
+
}
|
|
224
277
|
if (event.getSeq() === this.failedEventSeq) {
|
|
225
278
|
this.failedEventSeq = undefined;
|
|
226
279
|
}
|
|
@@ -239,9 +292,21 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
239
292
|
}).catch((error) => { this.reportStreamError(error); });
|
|
240
293
|
})
|
|
241
294
|
.on('end', () => {
|
|
295
|
+
if (this.recovery.enabled) {
|
|
296
|
+
this.requestRecovery(client);
|
|
297
|
+
}
|
|
242
298
|
this.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(end)');
|
|
243
299
|
})
|
|
244
300
|
.on('error', (e) => {
|
|
301
|
+
if (this.recovery.enabled) {
|
|
302
|
+
if (this.recoveryStopped || client !== this._grpcManager) {
|
|
303
|
+
this.reportStreamError(e);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.requestRecovery(client);
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
245
310
|
this.emit('error', e);
|
|
246
311
|
// https://github.com/wechaty/wechaty-puppet-service/issues/16
|
|
247
312
|
// this.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(error) %s', e)
|
|
@@ -259,6 +324,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
259
324
|
// }
|
|
260
325
|
})
|
|
261
326
|
.on('cancel', (...args) => {
|
|
327
|
+
if (this.recovery.enabled) {
|
|
328
|
+
this.requestRecovery(client);
|
|
329
|
+
}
|
|
262
330
|
this.log.verbose('PuppetService', 'bridgeGrpcEventStream() eventStream.on(cancel), %s', JSON.stringify(args));
|
|
263
331
|
});
|
|
264
332
|
}
|
|
@@ -279,6 +347,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
279
347
|
}
|
|
280
348
|
async eventResumePosition(epoch = this.eventEpoch) {
|
|
281
349
|
return this.withEventCheckpoint(async () => {
|
|
350
|
+
if (this.recovery.enabled && !this._payloadStore.miscellaneous) {
|
|
351
|
+
throw new Error('Recovery cursor storage is unavailable');
|
|
352
|
+
}
|
|
282
353
|
if (this.pendingCheckpointRollback) {
|
|
283
354
|
await this.restoreEventMetadata(this.pendingCheckpointRollback);
|
|
284
355
|
this.pendingCheckpointRollback = undefined;
|
|
@@ -378,7 +449,13 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
378
449
|
catch (stopError) {
|
|
379
450
|
this.log.error('PuppetService', 'stopping failed event stream failed: %s', stopError);
|
|
380
451
|
}
|
|
381
|
-
this.
|
|
452
|
+
if (this.recovery.enabled) {
|
|
453
|
+
this.log.error('PuppetService', 'recovering after event processing failure: %s', error);
|
|
454
|
+
this.requestRecovery(manager);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.reportStreamError(error);
|
|
458
|
+
}
|
|
382
459
|
}
|
|
383
460
|
async onGrpcStreamEvent(event, epoch = this.eventEpoch) {
|
|
384
461
|
if (!this.isEventEpochCurrent(epoch)) {
|
|
@@ -412,30 +489,50 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
412
489
|
break;
|
|
413
490
|
case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGIN:
|
|
414
491
|
{
|
|
492
|
+
const loginPayload = JSON.parse(payload);
|
|
493
|
+
if (this.recovery.enabled) {
|
|
494
|
+
this.recovery.login(loginPayload.contactId);
|
|
495
|
+
if (this.recovery.state.status === 'offline') {
|
|
496
|
+
this.stopRecovery('Account changed during recovery');
|
|
497
|
+
this.reportStreamError(new Error('Account changed during recovery'));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (this.isLoggedIn) {
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
415
504
|
if (this.waitingForLogin && this.isLoggedIn) {
|
|
416
505
|
this.log.warn('PuppetService', 'this login event is ignored because the it is expected by event stream reconnect and this puppet is already logged in');
|
|
417
506
|
return;
|
|
418
507
|
}
|
|
419
|
-
|
|
420
|
-
if (!config_js_1.envVars.WECHATY_PUPPET_SERVICE_DISABLE_EVENT_CACHE()) {
|
|
508
|
+
if (this.eventMetadataEnabled) {
|
|
421
509
|
const { accountId } = await this.getMiscellaneousStoreData();
|
|
422
510
|
if (!this.isEventEpochCurrent(epoch)) {
|
|
423
511
|
return;
|
|
424
512
|
}
|
|
425
513
|
if (accountId !== loginPayload.contactId) {
|
|
426
514
|
await this.resetMiscellaneousStoreData(epoch);
|
|
515
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
427
518
|
await this.setMiscellaneousStoreData({
|
|
428
519
|
accountId: loginPayload.contactId,
|
|
429
520
|
}, epoch);
|
|
521
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
430
524
|
}
|
|
431
525
|
}
|
|
526
|
+
if (!this.isEventEpochCurrent(epoch)) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
432
529
|
(async () => this.login(loginPayload.contactId))().catch(e => this.log.error('PuppetService', 'onGrpcStreamEvent() this.login() rejection %s', e.message));
|
|
433
530
|
}
|
|
434
531
|
break;
|
|
435
532
|
case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_LOGOUT:
|
|
436
533
|
{
|
|
437
534
|
const logoutPayload = JSON.parse(payload);
|
|
438
|
-
if (
|
|
535
|
+
if (this.eventMetadataEnabled) {
|
|
439
536
|
await this.resetMiscellaneousStoreData(epoch);
|
|
440
537
|
}
|
|
441
538
|
;
|
|
@@ -476,12 +573,40 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
476
573
|
this.emit('post-tap', JSON.parse(payload));
|
|
477
574
|
break;
|
|
478
575
|
case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_READY:
|
|
576
|
+
if (this.recovery.enabled) {
|
|
577
|
+
this.recovery.ready();
|
|
578
|
+
if (this.readyIndicator.value()) {
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
479
582
|
if (this.waitingForReady && this.readyIndicator.value()) {
|
|
480
583
|
this.log.warn('PuppetService', 'this ready event is ignored because the it is expected by event stream reconnect and this puppet is already ready');
|
|
481
584
|
return;
|
|
482
585
|
}
|
|
483
586
|
this.emit('ready', JSON.parse(payload));
|
|
484
587
|
break;
|
|
588
|
+
case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_REPLAY_COMPLETE: {
|
|
589
|
+
if (!this.recovery.enabled) {
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
const barrier = JSON.parse(payload);
|
|
593
|
+
if (seq !== 0 || typeof barrier !== 'object' || barrier === null
|
|
594
|
+
|| !('accountId' in barrier) || !('throughSeq' in barrier)) {
|
|
595
|
+
throw new Error('Invalid replay barrier');
|
|
596
|
+
}
|
|
597
|
+
const value = barrier;
|
|
598
|
+
if (typeof value.accountId !== 'string' || !value.accountId || typeof value.throughSeq !== 'number'
|
|
599
|
+
|| !Number.isSafeInteger(value.throughSeq) || value.throughSeq < 0
|
|
600
|
+
|| value.throughSeq > this.recoveryProcessedSeq || this.failedEventSeq !== undefined) {
|
|
601
|
+
throw new Error('Invalid or incomplete replay barrier');
|
|
602
|
+
}
|
|
603
|
+
this.recovery.barrier(value.accountId);
|
|
604
|
+
if (this.recovery.state.status === 'offline') {
|
|
605
|
+
this.stopRecovery(this.recovery.state.reason);
|
|
606
|
+
this.reportStreamError(new Error(this.recovery.state.reason));
|
|
607
|
+
}
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
485
610
|
case wechaty_grpc_1.puppet.EventType.EVENT_TYPE_ROOM_INVITE:
|
|
486
611
|
this.emit('room-invite', JSON.parse(payload));
|
|
487
612
|
break;
|
|
@@ -535,11 +660,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
535
660
|
// Huan(202003): in default, the `type` type should be `never`, please check.
|
|
536
661
|
this.log.error(`eventType ${type} unsupported! data: ${payload}`);
|
|
537
662
|
}
|
|
538
|
-
if (seq && this.isEventEpochCurrent(epoch) &&
|
|
663
|
+
if (seq && this.isEventEpochCurrent(epoch) && this.eventMetadataEnabled) {
|
|
539
664
|
await this.commitEventCheckpoint(seq, timestamp, epoch);
|
|
540
665
|
}
|
|
541
666
|
}
|
|
542
667
|
async logout(reason) {
|
|
668
|
+
this.stopRecovery(reason ?? 'Account logged out', false);
|
|
543
669
|
this.log.verbose('PuppetService', 'logout(%s)', reason ? `"${reason}"` : '');
|
|
544
670
|
await super.logout(reason);
|
|
545
671
|
try {
|
|
@@ -2859,6 +2985,9 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2859
2985
|
}
|
|
2860
2986
|
healthCheckInterval;
|
|
2861
2987
|
startHealthCheck() {
|
|
2988
|
+
if (this.recovery.enabled) {
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2862
2991
|
this.healthCheckInterval = setInterval(() => {
|
|
2863
2992
|
this.ding('healthCheck');
|
|
2864
2993
|
}, 60 * 1000);
|
|
@@ -2872,6 +3001,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2872
3001
|
waitingForReady = false;
|
|
2873
3002
|
reconnectIndicator;
|
|
2874
3003
|
async reset() {
|
|
3004
|
+
if (this.recovery.enabled) {
|
|
3005
|
+
if (this._grpcManager) {
|
|
3006
|
+
this.requestRecovery(this._grpcManager);
|
|
3007
|
+
}
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
2875
3010
|
if (this.reconnectIndicator.value()) {
|
|
2876
3011
|
this.log.warn('PuppetService', 'already trying to reconnect, pass this one');
|
|
2877
3012
|
return;
|
|
@@ -2895,6 +3030,66 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2895
3030
|
this.reconnectIndicator.value(false);
|
|
2896
3031
|
}
|
|
2897
3032
|
}
|
|
3033
|
+
touchRecoveryStream() {
|
|
3034
|
+
if (!this.recovery.enabled || this.recoveryStopped) {
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
clearTimeout(this.inactivityTimer);
|
|
3038
|
+
this.inactivityTimer = setTimeout(() => {
|
|
3039
|
+
if (this._grpcManager) {
|
|
3040
|
+
this.requestRecovery(this._grpcManager);
|
|
3041
|
+
}
|
|
3042
|
+
}, this.recovery.inactivity);
|
|
3043
|
+
}
|
|
3044
|
+
stopRecovery(reason, stopStream = true) {
|
|
3045
|
+
if (!this.recovery.enabled) {
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
this.recoveryStopped = true;
|
|
3049
|
+
clearTimeout(this.recoveryTimer);
|
|
3050
|
+
clearTimeout(this.inactivityTimer);
|
|
3051
|
+
this.recoveryTimer = undefined;
|
|
3052
|
+
this.recovery.fail(reason);
|
|
3053
|
+
if (stopStream) {
|
|
3054
|
+
this.suspendEventProcessing();
|
|
3055
|
+
this._grpcManager?.stopStream();
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
requestRecovery(manager) {
|
|
3059
|
+
if (this.recoveryStopped || manager !== this._grpcManager || this.recoveryTimer) {
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
this.recovery.begin();
|
|
3063
|
+
this.suspendEventProcessing();
|
|
3064
|
+
manager.stopStream();
|
|
3065
|
+
this.recovery.replaceControls();
|
|
3066
|
+
clearTimeout(this.inactivityTimer);
|
|
3067
|
+
const epoch = this.eventEpoch;
|
|
3068
|
+
this.recoveryTimer = setTimeout(() => {
|
|
3069
|
+
this.recoveryTimer = undefined;
|
|
3070
|
+
void this.recovery.runDetached(async () => this.connectRecoveryStream(manager, epoch)).catch((error) => {
|
|
3071
|
+
this.log.warn('PuppetService', 'recovery connection failed: %s', error);
|
|
3072
|
+
if (epoch === this.eventEpoch) {
|
|
3073
|
+
this.requestRecovery(manager);
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
}, 1000);
|
|
3077
|
+
}
|
|
3078
|
+
async connectRecoveryStream(manager, epoch) {
|
|
3079
|
+
const { lastEventSeq, accountId } = await this.eventResumePosition(epoch);
|
|
3080
|
+
if (epoch !== this.eventEpoch || this.recoveryStopped || manager !== this._grpcManager) {
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
this.recovery.begin(accountId);
|
|
3084
|
+
this.recoveryProcessedSeq = Number(lastEventSeq ?? 0);
|
|
3085
|
+
this.eventsBlocked = false;
|
|
3086
|
+
this.touchRecoveryStream();
|
|
3087
|
+
await manager.startStream(lastEventSeq, accountId);
|
|
3088
|
+
if (!this.ownsEventWorker(manager, epoch)) {
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
await util_1.default.promisify(manager.client.start.bind(manager.client))(new wechaty_grpc_1.puppet.StartRequest());
|
|
3092
|
+
}
|
|
2898
3093
|
async resetEventStream(manager, epoch) {
|
|
2899
3094
|
manager.stopStream();
|
|
2900
3095
|
const { lastEventSeq, accountId } = await this.eventResumePosition(epoch);
|
|
@@ -3014,7 +3209,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3014
3209
|
}
|
|
3015
3210
|
}
|
|
3016
3211
|
async getMiscellaneousStoreData() {
|
|
3017
|
-
if (
|
|
3212
|
+
if (!this.eventMetadataEnabled) {
|
|
3018
3213
|
return {
|
|
3019
3214
|
lastEventSeq: undefined,
|
|
3020
3215
|
lastEventTimestamp: undefined,
|
|
@@ -3023,7 +3218,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3023
3218
|
}
|
|
3024
3219
|
const lastEventTimestamp = await this._payloadStore.miscellaneous?.get('lastEventTimestamp');
|
|
3025
3220
|
let lastEventSeq = await this._payloadStore.miscellaneous?.get('lastEventSeq');
|
|
3026
|
-
if ((Date.now() - Number(lastEventTimestamp || 0)) > this.timeoutMilliseconds) {
|
|
3221
|
+
if (!this.recovery.enabled && (Date.now() - Number(lastEventTimestamp || 0)) > this.timeoutMilliseconds) {
|
|
3027
3222
|
this.log.warn(`last event was ${(Date.now() - Number(lastEventTimestamp || 0)) / 1000} seconds ago, will not request event cache`);
|
|
3028
3223
|
lastEventSeq = undefined;
|
|
3029
3224
|
}
|
|
@@ -3035,7 +3230,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3035
3230
|
};
|
|
3036
3231
|
}
|
|
3037
3232
|
async setMiscellaneousStoreData(data, epoch) {
|
|
3038
|
-
if (
|
|
3233
|
+
if (!this.eventMetadataEnabled) {
|
|
3039
3234
|
return;
|
|
3040
3235
|
}
|
|
3041
3236
|
const changes = new Map();
|
|
@@ -3051,7 +3246,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3051
3246
|
await this.withEventCheckpoint(async () => this.writeEventMetadata(changes, epoch));
|
|
3052
3247
|
}
|
|
3053
3248
|
async resetMiscellaneousStoreData(epoch) {
|
|
3054
|
-
if (
|
|
3249
|
+
if (!this.eventMetadataEnabled) {
|
|
3055
3250
|
return;
|
|
3056
3251
|
}
|
|
3057
3252
|
await this.withEventCheckpoint(async () => this.writeEventMetadata(new Map([
|