@whanext/core 0.19.16 → 0.19.19
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/CHANGELOG.md +53 -0
- package/README.md +120 -6
- package/dist/index.d.ts +202 -2
- package/dist/index.js +815 -87
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -379,15 +379,37 @@ function isCommandGroup(definition) {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
// src/commands/concurrency.ts
|
|
382
|
+
var DEFAULT_MAX_QUEUE = 10;
|
|
383
|
+
var DEFAULT_QUEUE_TIMEOUT_MS = 6e4;
|
|
382
384
|
var CommandConcurrencyController = class {
|
|
383
385
|
#states = /* @__PURE__ */ new Map();
|
|
384
|
-
|
|
386
|
+
#onEvent;
|
|
387
|
+
#queueTimeouts = 0;
|
|
388
|
+
#queueRejected = 0;
|
|
389
|
+
constructor(onEvent) {
|
|
390
|
+
this.#onEvent = onEvent;
|
|
391
|
+
}
|
|
392
|
+
health() {
|
|
393
|
+
let running = 0;
|
|
394
|
+
let queued = 0;
|
|
395
|
+
for (const state of this.#states.values()) {
|
|
396
|
+
running += state.active;
|
|
397
|
+
queued += state.queue.length;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
running,
|
|
401
|
+
queued,
|
|
402
|
+
queueTimeouts: this.#queueTimeouts,
|
|
403
|
+
queueRejected: this.#queueRejected
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async run(key, options, execute, context) {
|
|
385
407
|
const strategy = options?.strategy ?? "parallel";
|
|
386
408
|
if (strategy === "parallel") {
|
|
387
409
|
await execute(new AbortController().signal);
|
|
388
410
|
return;
|
|
389
411
|
}
|
|
390
|
-
const max =
|
|
412
|
+
const max = normalizePositiveInteger(options?.max, 1);
|
|
391
413
|
const state = this.#states.get(key) ?? { active: 0, queue: [], controllers: /* @__PURE__ */ new Set() };
|
|
392
414
|
this.#states.set(key, state);
|
|
393
415
|
if (strategy === "replace") {
|
|
@@ -399,7 +421,7 @@ var CommandConcurrencyController = class {
|
|
|
399
421
|
recoverable: true
|
|
400
422
|
});
|
|
401
423
|
} else if (strategy === "queue" && state.active >= max) {
|
|
402
|
-
await
|
|
424
|
+
await this.#waitForQueue(key, state, options ?? {}, context);
|
|
403
425
|
}
|
|
404
426
|
const controller = new AbortController();
|
|
405
427
|
state.controllers.add(controller);
|
|
@@ -409,13 +431,97 @@ var CommandConcurrencyController = class {
|
|
|
409
431
|
} finally {
|
|
410
432
|
state.controllers.delete(controller);
|
|
411
433
|
state.active -= 1;
|
|
412
|
-
state
|
|
434
|
+
this.#releaseNext(state);
|
|
413
435
|
if (state.active === 0 && state.queue.length === 0) {
|
|
414
436
|
this.#states.delete(key);
|
|
415
437
|
}
|
|
416
438
|
}
|
|
417
439
|
}
|
|
440
|
+
async #waitForQueue(key, state, options, context) {
|
|
441
|
+
const maxQueue = normalizeQueueLimit(options.maxQueue);
|
|
442
|
+
if (state.queue.length >= maxQueue) {
|
|
443
|
+
this.#queueRejected += 1;
|
|
444
|
+
if (context && Number.isFinite(maxQueue)) {
|
|
445
|
+
this.#onEvent?.({
|
|
446
|
+
type: "commandQueueFull",
|
|
447
|
+
payload: {
|
|
448
|
+
...context,
|
|
449
|
+
key,
|
|
450
|
+
maxQueue,
|
|
451
|
+
queued: state.queue.length
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
throw new WhaNextError("COMMAND_QUEUE_FULL", "This command queue is full.", {
|
|
456
|
+
context: { key, maxQueue, queued: state.queue.length },
|
|
457
|
+
recoverable: true
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
const queueTimeoutMs = normalizeQueueTimeout(options.queueTimeoutMs);
|
|
461
|
+
await new Promise((resolve4, reject) => {
|
|
462
|
+
const entry = {
|
|
463
|
+
resolve: resolve4,
|
|
464
|
+
reject,
|
|
465
|
+
enqueuedAt: Date.now(),
|
|
466
|
+
settled: false
|
|
467
|
+
};
|
|
468
|
+
if (queueTimeoutMs > 0) {
|
|
469
|
+
entry.timer = setTimeout(() => {
|
|
470
|
+
if (entry.settled) return;
|
|
471
|
+
entry.settled = true;
|
|
472
|
+
const index = state.queue.indexOf(entry);
|
|
473
|
+
if (index >= 0) state.queue.splice(index, 1);
|
|
474
|
+
const queuedForMs = Date.now() - entry.enqueuedAt;
|
|
475
|
+
this.#queueTimeouts += 1;
|
|
476
|
+
if (context) {
|
|
477
|
+
this.#onEvent?.({
|
|
478
|
+
type: "commandQueueTimeout",
|
|
479
|
+
payload: {
|
|
480
|
+
...context,
|
|
481
|
+
key,
|
|
482
|
+
queuedForMs,
|
|
483
|
+
queueTimeoutMs,
|
|
484
|
+
queued: state.queue.length
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
reject(new WhaNextError(
|
|
489
|
+
"COMMAND_QUEUE_TIMEOUT",
|
|
490
|
+
"This command waited too long in the execution queue.",
|
|
491
|
+
{
|
|
492
|
+
context: { key, queuedForMs, queueTimeoutMs },
|
|
493
|
+
recoverable: true
|
|
494
|
+
}
|
|
495
|
+
));
|
|
496
|
+
}, queueTimeoutMs);
|
|
497
|
+
}
|
|
498
|
+
state.queue.push(entry);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
#releaseNext(state) {
|
|
502
|
+
while (state.queue.length > 0) {
|
|
503
|
+
const next = state.queue.shift();
|
|
504
|
+
if (!next || next.settled) continue;
|
|
505
|
+
next.settled = true;
|
|
506
|
+
if (next.timer) clearTimeout(next.timer);
|
|
507
|
+
next.resolve();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
418
511
|
};
|
|
512
|
+
function normalizePositiveInteger(value, fallback) {
|
|
513
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
514
|
+
return Math.max(1, Math.floor(value));
|
|
515
|
+
}
|
|
516
|
+
function normalizeQueueLimit(value) {
|
|
517
|
+
if (value === 0) return Number.POSITIVE_INFINITY;
|
|
518
|
+
return normalizePositiveInteger(value, DEFAULT_MAX_QUEUE);
|
|
519
|
+
}
|
|
520
|
+
function normalizeQueueTimeout(value) {
|
|
521
|
+
if (value === 0) return 0;
|
|
522
|
+
if (value === void 0 || !Number.isFinite(value)) return DEFAULT_QUEUE_TIMEOUT_MS;
|
|
523
|
+
return Math.max(1, Math.floor(value));
|
|
524
|
+
}
|
|
419
525
|
|
|
420
526
|
// src/commands/context.ts
|
|
421
527
|
var CommandContextImplementation = class {
|
|
@@ -739,6 +845,24 @@ function invalid(name, received, expected) {
|
|
|
739
845
|
});
|
|
740
846
|
}
|
|
741
847
|
|
|
848
|
+
// src/provider/event-emitter.ts
|
|
849
|
+
var TypedEventEmitter = class {
|
|
850
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
851
|
+
on(event, listener) {
|
|
852
|
+
const listeners = this.#listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
853
|
+
listeners.add(listener);
|
|
854
|
+
this.#listeners.set(event, listeners);
|
|
855
|
+
return () => listeners.delete(listener);
|
|
856
|
+
}
|
|
857
|
+
async emit(event, payload) {
|
|
858
|
+
const listeners = this.#listeners.get(event);
|
|
859
|
+
if (!listeners) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
await Promise.all([...listeners].map((listener) => listener(payload)));
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
|
|
742
866
|
// src/services/user-service.ts
|
|
743
867
|
var UserService = class {
|
|
744
868
|
#group;
|
|
@@ -781,11 +905,19 @@ var CommandRouter = class {
|
|
|
781
905
|
#globalMiddleware = [];
|
|
782
906
|
#errorHandlers = [];
|
|
783
907
|
#cooldowns = /* @__PURE__ */ new Map();
|
|
784
|
-
#
|
|
908
|
+
#events = new TypedEventEmitter();
|
|
909
|
+
#concurrency;
|
|
785
910
|
#beforeExecute;
|
|
786
911
|
#afterExecute;
|
|
787
912
|
#cooldownOperations = 0;
|
|
788
913
|
constructor(servicesOrGroup, options = {}) {
|
|
914
|
+
this.#concurrency = new CommandConcurrencyController((event) => {
|
|
915
|
+
if (event.type === "commandQueueTimeout") {
|
|
916
|
+
void this.#events.emit("commandQueueTimeout", event.payload).catch(() => void 0);
|
|
917
|
+
} else {
|
|
918
|
+
void this.#events.emit("commandQueueFull", event.payload).catch(() => void 0);
|
|
919
|
+
}
|
|
920
|
+
});
|
|
789
921
|
this.#services = isRuntimeServices(servicesOrGroup) ? servicesOrGroup : createLegacyServices(servicesOrGroup);
|
|
790
922
|
this.setPrefixes(options.prefix ?? "!");
|
|
791
923
|
this.#legacyOnError = options.onError;
|
|
@@ -809,6 +941,12 @@ var CommandRouter = class {
|
|
|
809
941
|
get size() {
|
|
810
942
|
return this.catalog({ includeHidden: true }).length;
|
|
811
943
|
}
|
|
944
|
+
health() {
|
|
945
|
+
return this.#concurrency.health();
|
|
946
|
+
}
|
|
947
|
+
on(event, listener) {
|
|
948
|
+
return this.#events.on(event, listener);
|
|
949
|
+
}
|
|
812
950
|
command(definition) {
|
|
813
951
|
this.#validateTree(definition, []);
|
|
814
952
|
for (const name of commandNames(definition)) {
|
|
@@ -970,6 +1108,12 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
970
1108
|
await this.#authorize(resolved.layers, context);
|
|
971
1109
|
this.#consumeCooldown(resolved, context);
|
|
972
1110
|
await this.#execute(resolved, context);
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
command: resolved.registered.path.join(" "),
|
|
1114
|
+
messageId: message.id,
|
|
1115
|
+
chatId: message.chatId,
|
|
1116
|
+
userId: message.sender.id
|
|
973
1117
|
}
|
|
974
1118
|
);
|
|
975
1119
|
return true;
|
|
@@ -1797,24 +1941,6 @@ var SqliteMuteStore = class {
|
|
|
1797
1941
|
}
|
|
1798
1942
|
};
|
|
1799
1943
|
|
|
1800
|
-
// src/provider/event-emitter.ts
|
|
1801
|
-
var TypedEventEmitter = class {
|
|
1802
|
-
#listeners = /* @__PURE__ */ new Map();
|
|
1803
|
-
on(event, listener) {
|
|
1804
|
-
const listeners = this.#listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
1805
|
-
listeners.add(listener);
|
|
1806
|
-
this.#listeners.set(event, listeners);
|
|
1807
|
-
return () => listeners.delete(listener);
|
|
1808
|
-
}
|
|
1809
|
-
async emit(event, payload) {
|
|
1810
|
-
const listeners = this.#listeners.get(event);
|
|
1811
|
-
if (!listeners) {
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
await Promise.all([...listeners].map((listener) => listener(payload)));
|
|
1815
|
-
}
|
|
1816
|
-
};
|
|
1817
|
-
|
|
1818
1944
|
// src/services/account-service.ts
|
|
1819
1945
|
var AccountService = class {
|
|
1820
1946
|
id;
|
|
@@ -2165,6 +2291,7 @@ var WhaNextApp = class {
|
|
|
2165
2291
|
#router;
|
|
2166
2292
|
#startedAt = Date.now();
|
|
2167
2293
|
#state = "idle";
|
|
2294
|
+
#lastStability = "offline";
|
|
2168
2295
|
constructor(provider, options = {}, logger = new Logger(options.logger)) {
|
|
2169
2296
|
this.#provider = provider;
|
|
2170
2297
|
this.#phone = options.phone;
|
|
@@ -2209,14 +2336,61 @@ var WhaNextApp = class {
|
|
|
2209
2336
|
return this.#state === "connected";
|
|
2210
2337
|
}
|
|
2211
2338
|
health() {
|
|
2339
|
+
const provider = this.#provider.health?.();
|
|
2340
|
+
const connection = provider?.connection ?? {
|
|
2341
|
+
state: this.#state,
|
|
2342
|
+
uptimeMs: this.isReady ? Date.now() - this.#startedAt : 0,
|
|
2343
|
+
reconnects: 0,
|
|
2344
|
+
reconnectAttempt: 0
|
|
2345
|
+
};
|
|
2346
|
+
const messaging = provider?.messaging ?? {
|
|
2347
|
+
sent: 0,
|
|
2348
|
+
received: 0,
|
|
2349
|
+
failed: 0,
|
|
2350
|
+
decryptedPayloads: 0,
|
|
2351
|
+
unavailable: 0,
|
|
2352
|
+
resendRequested: 0,
|
|
2353
|
+
recovered: 0,
|
|
2354
|
+
recoveryFailed: 0,
|
|
2355
|
+
unavailableUnrecoverable: 0,
|
|
2356
|
+
decodeFailures: 0,
|
|
2357
|
+
unhandledStanzas: 0,
|
|
2358
|
+
ignoredOffline: 0,
|
|
2359
|
+
duplicates: 0,
|
|
2360
|
+
normalizationFailures: 0
|
|
2361
|
+
};
|
|
2362
|
+
const crypto = provider?.crypto ?? {
|
|
2363
|
+
backend: "unknown",
|
|
2364
|
+
acceleration: false,
|
|
2365
|
+
decryptFailures: 0,
|
|
2366
|
+
addonDecryptFailures: 0,
|
|
2367
|
+
senderKeyMismatches: 0
|
|
2368
|
+
};
|
|
2369
|
+
const groups = provider?.groups ?? {
|
|
2370
|
+
phashMismatches: 0,
|
|
2371
|
+
metadataRecoveries: 0,
|
|
2372
|
+
metadataRecoveryFailures: 0
|
|
2373
|
+
};
|
|
2374
|
+
const timeouts = provider?.timeouts ?? {
|
|
2375
|
+
connectTimeoutMs: 0,
|
|
2376
|
+
nodeQueryTimeoutMs: 0
|
|
2377
|
+
};
|
|
2378
|
+
const stability = provider?.stability ?? this.#fallbackStability();
|
|
2212
2379
|
return {
|
|
2213
2380
|
status: this.#healthStatus(),
|
|
2381
|
+
stability,
|
|
2214
2382
|
state: this.#state,
|
|
2215
2383
|
ready: this.isReady,
|
|
2216
2384
|
uptimeMs: Date.now() - this.#startedAt,
|
|
2217
2385
|
timestamp: /* @__PURE__ */ new Date(),
|
|
2218
2386
|
muteEnabled: this.mute.enabled,
|
|
2219
|
-
logLevel: this.logger.level
|
|
2387
|
+
logLevel: this.logger.level,
|
|
2388
|
+
connection,
|
|
2389
|
+
messaging,
|
|
2390
|
+
crypto,
|
|
2391
|
+
groups,
|
|
2392
|
+
commands: this.#router.health(),
|
|
2393
|
+
timeouts
|
|
2220
2394
|
};
|
|
2221
2395
|
}
|
|
2222
2396
|
router() {
|
|
@@ -2286,9 +2460,42 @@ var WhaNextApp = class {
|
|
|
2286
2460
|
}
|
|
2287
2461
|
#bind() {
|
|
2288
2462
|
this.#provider.on("connection", async (update) => {
|
|
2463
|
+
const previousState = this.#state;
|
|
2289
2464
|
this.#state = update.state;
|
|
2290
2465
|
this.#logConnection(update);
|
|
2291
2466
|
await this.#events.emit("connection", update);
|
|
2467
|
+
if (update.state === "connected" && previousState === "reconnecting") {
|
|
2468
|
+
const providerHealth = this.#provider.health?.();
|
|
2469
|
+
await this.#events.emit("connectionRecovered", {
|
|
2470
|
+
recoveredAt: /* @__PURE__ */ new Date(),
|
|
2471
|
+
reconnects: providerHealth?.connection.reconnects ?? 1
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
await this.#refreshHealthState();
|
|
2475
|
+
});
|
|
2476
|
+
this.#provider.on("stability", async (event) => {
|
|
2477
|
+
if (event.type === "groupMetadataRecovered") {
|
|
2478
|
+
await this.#events.emit("groupMetadataRecovered", event.payload);
|
|
2479
|
+
} else if (event.type === "cryptoDegraded") {
|
|
2480
|
+
await this.#events.emit("cryptoDegraded", event.payload);
|
|
2481
|
+
} else if (event.type === "messageUnavailable") {
|
|
2482
|
+
await this.#events.emit("messageUnavailable", event.payload);
|
|
2483
|
+
} else if (event.type === "messageRecovered") {
|
|
2484
|
+
await this.#events.emit("messageRecovered", event.payload);
|
|
2485
|
+
} else if (event.type === "messageRecoveryFailed") {
|
|
2486
|
+
await this.#events.emit("messageRecoveryFailed", event.payload);
|
|
2487
|
+
} else if (event.type === "messageDecodeFailure") {
|
|
2488
|
+
await this.#events.emit("messageDecodeFailure", event.payload);
|
|
2489
|
+
} else if (event.type === "messageDiscarded") {
|
|
2490
|
+
await this.#events.emit("messageDiscarded", event.payload);
|
|
2491
|
+
}
|
|
2492
|
+
await this.#refreshHealthState();
|
|
2493
|
+
});
|
|
2494
|
+
this.#router.on("commandQueueTimeout", async (event) => {
|
|
2495
|
+
await this.#events.emit("commandQueueTimeout", event);
|
|
2496
|
+
});
|
|
2497
|
+
this.#router.on("commandQueueFull", async (event) => {
|
|
2498
|
+
await this.#events.emit("commandQueueFull", event);
|
|
2292
2499
|
});
|
|
2293
2500
|
this.#provider.on("groupChanged", ({ groupId }) => this.group.invalidate(groupId));
|
|
2294
2501
|
this.#provider.on("groupParticipantsChanged", async (change) => {
|
|
@@ -2356,6 +2563,22 @@ var WhaNextApp = class {
|
|
|
2356
2563
|
}
|
|
2357
2564
|
});
|
|
2358
2565
|
}
|
|
2566
|
+
#fallbackStability() {
|
|
2567
|
+
if (this.#state === "connected") return "healthy";
|
|
2568
|
+
if (this.#state === "connecting" || this.#state === "reconnecting") return "reconnecting";
|
|
2569
|
+
return "offline";
|
|
2570
|
+
}
|
|
2571
|
+
async #refreshHealthState() {
|
|
2572
|
+
const health = this.health();
|
|
2573
|
+
if (health.stability === this.#lastStability) return;
|
|
2574
|
+
const previous = this.#lastStability;
|
|
2575
|
+
this.#lastStability = health.stability;
|
|
2576
|
+
await this.#events.emit("healthChanged", {
|
|
2577
|
+
previous,
|
|
2578
|
+
current: health.stability,
|
|
2579
|
+
health
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2359
2582
|
#healthStatus() {
|
|
2360
2583
|
if (this.#state === "connected") {
|
|
2361
2584
|
return "ready";
|
|
@@ -2932,6 +3155,15 @@ var fatalDisconnectReasons = /* @__PURE__ */ new Set([
|
|
|
2932
3155
|
var fatalDisconnectCodes = /* @__PURE__ */ new Set([401, 403, 405, 406, 409, 516]);
|
|
2933
3156
|
var sharedMediaProcessor = createMediaProcessor();
|
|
2934
3157
|
var REMOTE_MEDIA_TIMEOUT_MS = 12e4;
|
|
3158
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
|
|
3159
|
+
var DEFAULT_NODE_QUERY_TIMEOUT_MS = 3e4;
|
|
3160
|
+
var PROVIDER_DEGRADED_WINDOW_MS = 12e4;
|
|
3161
|
+
var GROUP_METADATA_CACHE_TTL_MS = 18e4;
|
|
3162
|
+
var DEVICE_LIST_CACHE_TTL_MS = 18e4;
|
|
3163
|
+
var GROUP_METADATA_RECOVERY_COOLDOWN_MS = 6e4;
|
|
3164
|
+
var GROUP_METADATA_MISMATCH_WARNING = "group message publish acknowledged with mismatch metadata";
|
|
3165
|
+
var MESSAGE_RECOVERY_TIMEOUT_MS = 3e4;
|
|
3166
|
+
var DECRYPT_CORRELATION_TTL_MS = 15e3;
|
|
2935
3167
|
var messageSnapshotRetentionSeconds = 7 * 24 * 60 * 60;
|
|
2936
3168
|
var messageSnapshotMaxPerSession = 2e4;
|
|
2937
3169
|
var messageSnapshotPruneInterval = 256;
|
|
@@ -2942,10 +3174,18 @@ var ZapoProvider = class {
|
|
|
2942
3174
|
#messageStore = /* @__PURE__ */ new Map();
|
|
2943
3175
|
#messageKeyStore = /* @__PURE__ */ new WeakMap();
|
|
2944
3176
|
#deliveredMessageStore = /* @__PURE__ */ new Set();
|
|
3177
|
+
#pendingUnavailableRecoveries = /* @__PURE__ */ new Map();
|
|
3178
|
+
#recentDecryptedPayloads = /* @__PURE__ */ new Map();
|
|
2945
3179
|
#handledProtocolStore = /* @__PURE__ */ new Set();
|
|
2946
3180
|
#callCreatorStore = /* @__PURE__ */ new Map();
|
|
3181
|
+
#groupMetadataRecoveryAt = /* @__PURE__ */ new Map();
|
|
3182
|
+
#groupMetadataRecoveryInFlight = /* @__PURE__ */ new Set();
|
|
2947
3183
|
#messageCacheSize;
|
|
3184
|
+
#cryptoBackend;
|
|
3185
|
+
#connectTimeoutMs;
|
|
3186
|
+
#nodeQueryTimeoutMs;
|
|
2948
3187
|
#protocolMutationQueue = Promise.resolve();
|
|
3188
|
+
#state = "idle";
|
|
2949
3189
|
#client;
|
|
2950
3190
|
#storeEntry;
|
|
2951
3191
|
#storePath;
|
|
@@ -2960,14 +3200,95 @@ var ZapoProvider = class {
|
|
|
2960
3200
|
#messageSnapshotsSincePrune = 0;
|
|
2961
3201
|
#pairingReady = Promise.resolve();
|
|
2962
3202
|
#resolvePairingReady;
|
|
3203
|
+
#lastConnectedAt;
|
|
3204
|
+
#lastDisconnectedAt;
|
|
3205
|
+
#reconnects = 0;
|
|
3206
|
+
#sentMessages = 0;
|
|
3207
|
+
#receivedMessages = 0;
|
|
3208
|
+
#failedMessages = 0;
|
|
3209
|
+
#decryptedPayloads = 0;
|
|
3210
|
+
#unavailableMessages = 0;
|
|
3211
|
+
#resendRequestedMessages = 0;
|
|
3212
|
+
#recoveredMessages = 0;
|
|
3213
|
+
#recoveryFailedMessages = 0;
|
|
3214
|
+
#unavailableUnrecoverableMessages = 0;
|
|
3215
|
+
#decodeFailures = 0;
|
|
3216
|
+
#unhandledStanzas = 0;
|
|
3217
|
+
#ignoredOfflineMessages = 0;
|
|
3218
|
+
#duplicateMessages = 0;
|
|
3219
|
+
#normalizationFailures = 0;
|
|
3220
|
+
#lastIncomingAt;
|
|
3221
|
+
#lastOutgoingAt;
|
|
3222
|
+
#decryptFailures = 0;
|
|
3223
|
+
#addonDecryptFailures = 0;
|
|
3224
|
+
#senderKeyMismatches = 0;
|
|
3225
|
+
#phashMismatches = 0;
|
|
3226
|
+
#metadataRecoveries = 0;
|
|
3227
|
+
#metadataRecoveryFailures = 0;
|
|
3228
|
+
#degradedUntil = 0;
|
|
3229
|
+
#healthRefreshTimer;
|
|
3230
|
+
#cryptoBackendLogged = false;
|
|
2963
3231
|
constructor(options) {
|
|
2964
3232
|
this.#options = options;
|
|
2965
3233
|
this.#logger = options.logger ?? new Logger("silent");
|
|
2966
3234
|
this.#messageCacheSize = Math.max(1, options.messageCacheSize ?? 1e3);
|
|
3235
|
+
this.#connectTimeoutMs = normalizeProviderTimeout(options.connectTimeoutMs, DEFAULT_CONNECT_TIMEOUT_MS);
|
|
3236
|
+
this.#nodeQueryTimeoutMs = normalizeProviderTimeout(options.nodeQueryTimeoutMs, DEFAULT_NODE_QUERY_TIMEOUT_MS);
|
|
3237
|
+
this.#cryptoBackend = detectCryptoBackend();
|
|
2967
3238
|
}
|
|
2968
3239
|
on(event, listener) {
|
|
2969
3240
|
return this.#events.on(event, listener);
|
|
2970
3241
|
}
|
|
3242
|
+
health() {
|
|
3243
|
+
const now = Date.now();
|
|
3244
|
+
const state = this.#state;
|
|
3245
|
+
const stability = state === "connected" ? now < this.#degradedUntil ? "degraded" : "healthy" : state === "reconnecting" || state === "connecting" ? "reconnecting" : "offline";
|
|
3246
|
+
return {
|
|
3247
|
+
stability,
|
|
3248
|
+
connection: {
|
|
3249
|
+
state,
|
|
3250
|
+
uptimeMs: this.#connectedAtSeconds > 0 && this.#connected ? Math.max(0, now - this.#connectedAtSeconds * 1e3) : 0,
|
|
3251
|
+
reconnects: this.#reconnects,
|
|
3252
|
+
reconnectAttempt: this.#reconnectAttempt,
|
|
3253
|
+
...this.#lastConnectedAt ? { lastConnectedAt: new Date(this.#lastConnectedAt) } : {},
|
|
3254
|
+
...this.#lastDisconnectedAt ? { lastDisconnectedAt: new Date(this.#lastDisconnectedAt) } : {}
|
|
3255
|
+
},
|
|
3256
|
+
messaging: {
|
|
3257
|
+
sent: this.#sentMessages,
|
|
3258
|
+
received: this.#receivedMessages,
|
|
3259
|
+
failed: this.#failedMessages,
|
|
3260
|
+
decryptedPayloads: this.#decryptedPayloads,
|
|
3261
|
+
unavailable: this.#unavailableMessages,
|
|
3262
|
+
resendRequested: this.#resendRequestedMessages,
|
|
3263
|
+
recovered: this.#recoveredMessages,
|
|
3264
|
+
recoveryFailed: this.#recoveryFailedMessages,
|
|
3265
|
+
unavailableUnrecoverable: this.#unavailableUnrecoverableMessages,
|
|
3266
|
+
decodeFailures: this.#decodeFailures,
|
|
3267
|
+
unhandledStanzas: this.#unhandledStanzas,
|
|
3268
|
+
ignoredOffline: this.#ignoredOfflineMessages,
|
|
3269
|
+
duplicates: this.#duplicateMessages,
|
|
3270
|
+
normalizationFailures: this.#normalizationFailures,
|
|
3271
|
+
...this.#lastIncomingAt ? { lastIncomingAt: new Date(this.#lastIncomingAt) } : {},
|
|
3272
|
+
...this.#lastOutgoingAt ? { lastOutgoingAt: new Date(this.#lastOutgoingAt) } : {}
|
|
3273
|
+
},
|
|
3274
|
+
crypto: {
|
|
3275
|
+
backend: this.#cryptoBackend,
|
|
3276
|
+
acceleration: this.#cryptoBackend === "napi" || this.#cryptoBackend === "wasm",
|
|
3277
|
+
decryptFailures: this.#decryptFailures,
|
|
3278
|
+
addonDecryptFailures: this.#addonDecryptFailures,
|
|
3279
|
+
senderKeyMismatches: this.#senderKeyMismatches
|
|
3280
|
+
},
|
|
3281
|
+
groups: {
|
|
3282
|
+
phashMismatches: this.#phashMismatches,
|
|
3283
|
+
metadataRecoveries: this.#metadataRecoveries,
|
|
3284
|
+
metadataRecoveryFailures: this.#metadataRecoveryFailures
|
|
3285
|
+
},
|
|
3286
|
+
timeouts: {
|
|
3287
|
+
connectTimeoutMs: this.#connectTimeoutMs,
|
|
3288
|
+
nodeQueryTimeoutMs: this.#nodeQueryTimeoutMs
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
2971
3292
|
async connect() {
|
|
2972
3293
|
this.#intentionalClose = false;
|
|
2973
3294
|
this.#closedNotified = false;
|
|
@@ -2976,8 +3297,9 @@ var ZapoProvider = class {
|
|
|
2976
3297
|
return;
|
|
2977
3298
|
}
|
|
2978
3299
|
this.#preparePairingGate();
|
|
3300
|
+
this.#state = this.#reconnectAttempt > 0 ? "reconnecting" : "connecting";
|
|
2979
3301
|
await this.#events.emit("connection", {
|
|
2980
|
-
state: this.#
|
|
3302
|
+
state: this.#state,
|
|
2981
3303
|
attempt: this.#reconnectAttempt
|
|
2982
3304
|
});
|
|
2983
3305
|
this.#startConnect(client);
|
|
@@ -2988,6 +3310,15 @@ var ZapoProvider = class {
|
|
|
2988
3310
|
clearTimeout(this.#reconnectTimer);
|
|
2989
3311
|
this.#reconnectTimer = void 0;
|
|
2990
3312
|
}
|
|
3313
|
+
if (this.#healthRefreshTimer) {
|
|
3314
|
+
clearTimeout(this.#healthRefreshTimer);
|
|
3315
|
+
this.#healthRefreshTimer = void 0;
|
|
3316
|
+
}
|
|
3317
|
+
for (const pending of this.#pendingUnavailableRecoveries.values()) {
|
|
3318
|
+
clearTimeout(pending.timer);
|
|
3319
|
+
}
|
|
3320
|
+
this.#pendingUnavailableRecoveries.clear();
|
|
3321
|
+
this.#recentDecryptedPayloads.clear();
|
|
2991
3322
|
const client = this.#client;
|
|
2992
3323
|
this.#connectPromise = void 0;
|
|
2993
3324
|
try {
|
|
@@ -2998,6 +3329,8 @@ var ZapoProvider = class {
|
|
|
2998
3329
|
}
|
|
2999
3330
|
} finally {
|
|
3000
3331
|
this.#connected = false;
|
|
3332
|
+
this.#state = "closed";
|
|
3333
|
+
this.#lastDisconnectedAt = /* @__PURE__ */ new Date();
|
|
3001
3334
|
await this.#releaseStore();
|
|
3002
3335
|
}
|
|
3003
3336
|
}
|
|
@@ -3045,50 +3378,56 @@ var ZapoProvider = class {
|
|
|
3045
3378
|
}
|
|
3046
3379
|
}
|
|
3047
3380
|
async sendMessage(chatId, content, replyTo) {
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3381
|
+
return this.#trackOutgoing(async () => {
|
|
3382
|
+
if ("buttons" in content) {
|
|
3383
|
+
return this.#sendButtons(chatId, content, replyTo);
|
|
3384
|
+
}
|
|
3385
|
+
if ("list" in content) {
|
|
3386
|
+
return this.#sendList(chatId, content, replyTo);
|
|
3387
|
+
}
|
|
3388
|
+
const client = this.#requireClient();
|
|
3389
|
+
const { value, mentions, viewOnce } = await this.#toContent(content);
|
|
3390
|
+
const result = await client.message.send(chatId, value, {
|
|
3391
|
+
...replyTo ? { quote: this.#toZapoKey(replyTo) } : {},
|
|
3392
|
+
...mentions.length > 0 ? { mentions } : {},
|
|
3393
|
+
...viewOnce !== void 0 ? { viewOnce } : {}
|
|
3394
|
+
});
|
|
3395
|
+
return this.#sent(result, chatId);
|
|
3060
3396
|
});
|
|
3061
|
-
return this.#sent(result, chatId);
|
|
3062
3397
|
}
|
|
3063
3398
|
async repostMessage(source, chatId, options = {}) {
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3399
|
+
return this.#trackOutgoing(async () => {
|
|
3400
|
+
const original = await this.#findStoredMessage(this.#toZapoKey(source));
|
|
3401
|
+
if (!original?.message) {
|
|
3402
|
+
throw new WhaNextError(
|
|
3403
|
+
"MESSAGE_NOT_FOUND",
|
|
3404
|
+
"The source message is no longer available in the recent-message cache.",
|
|
3405
|
+
{
|
|
3406
|
+
context: { messageId: source.id, chatId: source.chatId },
|
|
3407
|
+
recoverable: true
|
|
3408
|
+
}
|
|
3409
|
+
);
|
|
3410
|
+
}
|
|
3411
|
+
const result = await this.#requireClient().message.send(
|
|
3412
|
+
chatId,
|
|
3413
|
+
original.message,
|
|
3069
3414
|
{
|
|
3070
|
-
|
|
3071
|
-
|
|
3415
|
+
forward: true,
|
|
3416
|
+
...options.mentions && options.mentions.length > 0 ? { mentions: this.#mentions(options.mentions) } : {}
|
|
3072
3417
|
}
|
|
3073
3418
|
);
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
chatId,
|
|
3077
|
-
original.message,
|
|
3078
|
-
{
|
|
3079
|
-
forward: true,
|
|
3080
|
-
...options.mentions && options.mentions.length > 0 ? { mentions: this.#mentions(options.mentions) } : {}
|
|
3081
|
-
}
|
|
3082
|
-
);
|
|
3083
|
-
return this.#sent(result, chatId);
|
|
3419
|
+
return this.#sent(result, chatId);
|
|
3420
|
+
});
|
|
3084
3421
|
}
|
|
3085
3422
|
async reactToMessage(key, emoji) {
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3423
|
+
return this.#trackOutgoing(async () => {
|
|
3424
|
+
const result = await this.#requireClient().message.send(key.chatId, {
|
|
3425
|
+
type: "reaction",
|
|
3426
|
+
emoji: emoji ?? "",
|
|
3427
|
+
target: this.#toZapoKey(key)
|
|
3428
|
+
});
|
|
3429
|
+
return this.#sent(result, key.chatId);
|
|
3090
3430
|
});
|
|
3091
|
-
return this.#sent(result, key.chatId);
|
|
3092
3431
|
}
|
|
3093
3432
|
async downloadMedia(key) {
|
|
3094
3433
|
const message = this.#messageKeyStore.get(key) ?? await this.#findStoredMessage(this.#toZapoKey(key));
|
|
@@ -3124,17 +3463,21 @@ var ZapoProvider = class {
|
|
|
3124
3463
|
}
|
|
3125
3464
|
}
|
|
3126
3465
|
async editMessage(key, content) {
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3466
|
+
return this.#trackOutgoing(async () => {
|
|
3467
|
+
const result = await this.#requireClient().message.send(
|
|
3468
|
+
key.chatId,
|
|
3469
|
+
content,
|
|
3470
|
+
{ editKey: this.#toZapoKey(key) }
|
|
3471
|
+
);
|
|
3472
|
+
return this.#sent(result, key.chatId);
|
|
3473
|
+
});
|
|
3133
3474
|
}
|
|
3134
3475
|
async deleteMessage(key) {
|
|
3135
|
-
await this.#
|
|
3136
|
-
|
|
3137
|
-
|
|
3476
|
+
await this.#trackOutgoing(async () => {
|
|
3477
|
+
await this.#requireClient().message.send(key.chatId, {
|
|
3478
|
+
type: "revoke",
|
|
3479
|
+
target: this.#toZapoKey(key)
|
|
3480
|
+
});
|
|
3138
3481
|
});
|
|
3139
3482
|
}
|
|
3140
3483
|
async getGroup(groupId) {
|
|
@@ -3186,13 +3529,15 @@ var ZapoProvider = class {
|
|
|
3186
3529
|
return result.code;
|
|
3187
3530
|
}
|
|
3188
3531
|
async setMessagePin(groupId, key, pinned) {
|
|
3189
|
-
await this.#
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3532
|
+
await this.#trackOutgoing(async () => {
|
|
3533
|
+
await this.#requireClient().message.send(groupId, pinned ? {
|
|
3534
|
+
type: "pin",
|
|
3535
|
+
target: this.#toZapoKey(key),
|
|
3536
|
+
durationSecs: 604800
|
|
3537
|
+
} : {
|
|
3538
|
+
type: "unpin",
|
|
3539
|
+
target: this.#toZapoKey(key)
|
|
3540
|
+
});
|
|
3196
3541
|
});
|
|
3197
3542
|
}
|
|
3198
3543
|
async updateParticipant(groupId, memberId, action) {
|
|
@@ -3233,6 +3578,14 @@ var ZapoProvider = class {
|
|
|
3233
3578
|
]
|
|
3234
3579
|
});
|
|
3235
3580
|
}
|
|
3581
|
+
async #trackOutgoing(operation) {
|
|
3582
|
+
try {
|
|
3583
|
+
return await operation();
|
|
3584
|
+
} catch (error) {
|
|
3585
|
+
this.#failedMessages += 1;
|
|
3586
|
+
throw error;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3236
3589
|
async #ensureClient() {
|
|
3237
3590
|
if (this.#client) return this.#client;
|
|
3238
3591
|
const authPath = resolve2(this.#options.auth);
|
|
@@ -3256,13 +3609,29 @@ var ZapoProvider = class {
|
|
|
3256
3609
|
markOnlineOnConnect: false,
|
|
3257
3610
|
deviceBrowser: this.#deviceBrowser(),
|
|
3258
3611
|
deviceOsDisplayName: this.#deviceOsDisplayName(),
|
|
3612
|
+
connectTimeoutMs: this.#connectTimeoutMs,
|
|
3613
|
+
nodeQueryTimeoutMs: this.#nodeQueryTimeoutMs,
|
|
3259
3614
|
history: { enabled: true, requireFullSync: true },
|
|
3260
3615
|
addons: {
|
|
3261
3616
|
autoDecrypt: true,
|
|
3262
3617
|
persistAllSecrets: true
|
|
3263
3618
|
},
|
|
3264
3619
|
media: { processor: sharedMediaProcessor }
|
|
3265
|
-
}, new WhaNextZapoLogger(
|
|
3620
|
+
}, new WhaNextZapoLogger(
|
|
3621
|
+
this.#logger,
|
|
3622
|
+
{},
|
|
3623
|
+
(message, context) => this.#observeZapoWarning(message, context)
|
|
3624
|
+
));
|
|
3625
|
+
if (!this.#cryptoBackendLogged) {
|
|
3626
|
+
this.#cryptoBackendLogged = true;
|
|
3627
|
+
if (this.#cryptoBackend === "napi" || this.#cryptoBackend === "wasm") {
|
|
3628
|
+
this.#logger.info("Crypto acceleration enabled.", { backend: this.#cryptoBackend });
|
|
3629
|
+
} else {
|
|
3630
|
+
this.#logger.debug("Crypto acceleration unavailable; using JavaScript backend.", {
|
|
3631
|
+
backend: this.#cryptoBackend
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3266
3635
|
this.#storeEntry = storeEntry;
|
|
3267
3636
|
this.#storePath = storePath;
|
|
3268
3637
|
this.#client = client;
|
|
@@ -3311,8 +3680,19 @@ var ZapoProvider = class {
|
|
|
3311
3680
|
}
|
|
3312
3681
|
this.#handleMessage(event);
|
|
3313
3682
|
});
|
|
3683
|
+
client.on("message_unavailable", (event) => {
|
|
3684
|
+
this.#handleUnavailableMessage(event);
|
|
3685
|
+
});
|
|
3686
|
+
client.on("debug_decrypted_payload", (event) => {
|
|
3687
|
+
this.#handleDecryptedPayload(event);
|
|
3688
|
+
});
|
|
3689
|
+
client.on("debug_unhandled_stanza", (event) => {
|
|
3690
|
+
this.#handleUnhandledStanza(event);
|
|
3691
|
+
});
|
|
3314
3692
|
client.on("message_send", (event) => {
|
|
3315
3693
|
if (!event.id || !event.message) return;
|
|
3694
|
+
this.#sentMessages += 1;
|
|
3695
|
+
this.#lastOutgoingAt = /* @__PURE__ */ new Date();
|
|
3316
3696
|
this.#remember({
|
|
3317
3697
|
key: {
|
|
3318
3698
|
id: event.id,
|
|
@@ -3347,7 +3727,16 @@ var ZapoProvider = class {
|
|
|
3347
3727
|
if (quoted?.key.id && quoted.message) {
|
|
3348
3728
|
this.#remember(quoted);
|
|
3349
3729
|
}
|
|
3730
|
+
const deliveryKey = this.#messageDeliveryKey(stored.key);
|
|
3731
|
+
this.#resolveUnavailableRecovery(deliveryKey, stored.key);
|
|
3732
|
+
const decryptedCorrelationKey = this.#stanzaCorrelationKey(
|
|
3733
|
+
stored.key.remoteJid ?? void 0,
|
|
3734
|
+
stored.key.id ?? void 0
|
|
3735
|
+
);
|
|
3736
|
+
if (decryptedCorrelationKey) this.#recentDecryptedPayloads.delete(decryptedCorrelationKey);
|
|
3350
3737
|
if (this.#isOfflineMessage(stored)) {
|
|
3738
|
+
this.#ignoredOfflineMessages += 1;
|
|
3739
|
+
this.#emitMessageDiscarded("offline", stored.key);
|
|
3351
3740
|
this.#logger.debug("Ignored message queued before the current live connection.", {
|
|
3352
3741
|
messageId: stored.key.id ?? void 0,
|
|
3353
3742
|
chatId: stored.key.remoteJid ?? void 0,
|
|
@@ -3355,8 +3744,9 @@ var ZapoProvider = class {
|
|
|
3355
3744
|
});
|
|
3356
3745
|
return;
|
|
3357
3746
|
}
|
|
3358
|
-
const deliveryKey = this.#messageDeliveryKey(stored.key);
|
|
3359
3747
|
if (stored.key.id && this.#deliveredMessageStore.has(deliveryKey)) {
|
|
3748
|
+
this.#duplicateMessages += 1;
|
|
3749
|
+
this.#emitMessageDiscarded("duplicate", stored.key);
|
|
3360
3750
|
this.#logger.debug("Ignored duplicate Zapo message event.", {
|
|
3361
3751
|
messageId: stored.key.id,
|
|
3362
3752
|
chatId: stored.key.remoteJid ?? void 0
|
|
@@ -3364,8 +3754,18 @@ var ZapoProvider = class {
|
|
|
3364
3754
|
return;
|
|
3365
3755
|
}
|
|
3366
3756
|
const message = normalizeZapoMessage(event);
|
|
3367
|
-
if (!message)
|
|
3757
|
+
if (!message) {
|
|
3758
|
+
this.#normalizationFailures += 1;
|
|
3759
|
+
this.#emitMessageDiscarded("normalization_failed", stored.key);
|
|
3760
|
+
this.#logger.warn("Could not normalize incoming Zapo message.", {
|
|
3761
|
+
messageId: stored.key.id ?? void 0,
|
|
3762
|
+
chatId: stored.key.remoteJid ?? void 0
|
|
3763
|
+
});
|
|
3764
|
+
return;
|
|
3765
|
+
}
|
|
3368
3766
|
if (stored.key.id) this.#rememberDeliveredMessage(deliveryKey);
|
|
3767
|
+
this.#receivedMessages += 1;
|
|
3768
|
+
this.#lastIncomingAt = /* @__PURE__ */ new Date();
|
|
3369
3769
|
this.#messageKeyStore.set(message.keys, stored);
|
|
3370
3770
|
if (message.quoted && quoted?.message) {
|
|
3371
3771
|
this.#messageKeyStore.set(
|
|
@@ -3375,6 +3775,162 @@ var ZapoProvider = class {
|
|
|
3375
3775
|
}
|
|
3376
3776
|
void this.#events.emit("message", message);
|
|
3377
3777
|
}
|
|
3778
|
+
#handleUnavailableMessage(event) {
|
|
3779
|
+
this.#unavailableMessages += 1;
|
|
3780
|
+
const occurredAt = /* @__PURE__ */ new Date();
|
|
3781
|
+
const messageId = event.key.id || void 0;
|
|
3782
|
+
const chatId = event.key.remoteJid || void 0;
|
|
3783
|
+
const participantId = event.key.participant ?? event.key.participantAlt ?? void 0;
|
|
3784
|
+
this.#emitStability({
|
|
3785
|
+
type: "messageUnavailable",
|
|
3786
|
+
payload: {
|
|
3787
|
+
kind: event.kind,
|
|
3788
|
+
resendRequested: event.resendRequested,
|
|
3789
|
+
occurredAt,
|
|
3790
|
+
...messageId ? { messageId } : {},
|
|
3791
|
+
...chatId ? { chatId } : {},
|
|
3792
|
+
...participantId ? { participantId } : {}
|
|
3793
|
+
}
|
|
3794
|
+
});
|
|
3795
|
+
if (!event.resendRequested) {
|
|
3796
|
+
this.#unavailableUnrecoverableMessages += 1;
|
|
3797
|
+
const log = event.kind === "other" ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
|
|
3798
|
+
log("Incoming message is unavailable and was not queued for recovery.", {
|
|
3799
|
+
kind: event.kind,
|
|
3800
|
+
...messageId ? { messageId } : {},
|
|
3801
|
+
...chatId ? { chatId } : {},
|
|
3802
|
+
...participantId ? { participantId } : {}
|
|
3803
|
+
});
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
this.#resendRequestedMessages += 1;
|
|
3807
|
+
const deliveryKey = this.#messageDeliveryKey(event.key);
|
|
3808
|
+
const previous = this.#pendingUnavailableRecoveries.get(deliveryKey);
|
|
3809
|
+
if (previous) clearTimeout(previous.timer);
|
|
3810
|
+
const requestedAt = Date.now();
|
|
3811
|
+
const timer = setTimeout(() => {
|
|
3812
|
+
const pending = this.#pendingUnavailableRecoveries.get(deliveryKey);
|
|
3813
|
+
if (!pending || pending.requestedAt !== requestedAt) return;
|
|
3814
|
+
this.#pendingUnavailableRecoveries.delete(deliveryKey);
|
|
3815
|
+
this.#recoveryFailedMessages += 1;
|
|
3816
|
+
this.#markDegraded();
|
|
3817
|
+
const failedAt = /* @__PURE__ */ new Date();
|
|
3818
|
+
this.#emitStability({
|
|
3819
|
+
type: "messageRecoveryFailed",
|
|
3820
|
+
payload: {
|
|
3821
|
+
failedAt,
|
|
3822
|
+
waitedMs: failedAt.getTime() - pending.requestedAt,
|
|
3823
|
+
...pending.messageId ? { messageId: pending.messageId } : {},
|
|
3824
|
+
...pending.chatId ? { chatId: pending.chatId } : {},
|
|
3825
|
+
...pending.participantId ? { participantId: pending.participantId } : {}
|
|
3826
|
+
}
|
|
3827
|
+
});
|
|
3828
|
+
this.#logger.warn("Unavailable message recovery did not arrive in time.", {
|
|
3829
|
+
...pending.messageId ? { messageId: pending.messageId } : {},
|
|
3830
|
+
...pending.chatId ? { chatId: pending.chatId } : {},
|
|
3831
|
+
waitedMs: failedAt.getTime() - pending.requestedAt
|
|
3832
|
+
});
|
|
3833
|
+
}, MESSAGE_RECOVERY_TIMEOUT_MS);
|
|
3834
|
+
this.#pendingUnavailableRecoveries.set(deliveryKey, {
|
|
3835
|
+
requestedAt,
|
|
3836
|
+
timer,
|
|
3837
|
+
...messageId ? { messageId } : {},
|
|
3838
|
+
...chatId ? { chatId } : {},
|
|
3839
|
+
...participantId ? { participantId } : {}
|
|
3840
|
+
});
|
|
3841
|
+
this.#logger.info("Incoming message unavailable; primary-device resend requested.", {
|
|
3842
|
+
...messageId ? { messageId } : {},
|
|
3843
|
+
...chatId ? { chatId } : {},
|
|
3844
|
+
...participantId ? { participantId } : {}
|
|
3845
|
+
});
|
|
3846
|
+
}
|
|
3847
|
+
#resolveUnavailableRecovery(deliveryKey, key) {
|
|
3848
|
+
const pending = this.#pendingUnavailableRecoveries.get(deliveryKey);
|
|
3849
|
+
if (!pending) return;
|
|
3850
|
+
clearTimeout(pending.timer);
|
|
3851
|
+
this.#pendingUnavailableRecoveries.delete(deliveryKey);
|
|
3852
|
+
this.#recoveredMessages += 1;
|
|
3853
|
+
const recoveredAt = /* @__PURE__ */ new Date();
|
|
3854
|
+
const participantId = key.participant ?? key.participantAlt ?? pending.participantId;
|
|
3855
|
+
this.#emitStability({
|
|
3856
|
+
type: "messageRecovered",
|
|
3857
|
+
payload: {
|
|
3858
|
+
recoveredAt,
|
|
3859
|
+
recoveryMs: recoveredAt.getTime() - pending.requestedAt,
|
|
3860
|
+
...key.id ? { messageId: key.id } : pending.messageId ? { messageId: pending.messageId } : {},
|
|
3861
|
+
...key.remoteJid ? { chatId: key.remoteJid } : pending.chatId ? { chatId: pending.chatId } : {},
|
|
3862
|
+
...participantId ? { participantId } : {}
|
|
3863
|
+
}
|
|
3864
|
+
});
|
|
3865
|
+
this.#logger.info("Recovered unavailable message from the primary device.", {
|
|
3866
|
+
...key.id ? { messageId: key.id } : {},
|
|
3867
|
+
...key.remoteJid ? { chatId: key.remoteJid } : {},
|
|
3868
|
+
recoveryMs: recoveredAt.getTime() - pending.requestedAt
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
#handleDecryptedPayload(event) {
|
|
3872
|
+
this.#decryptedPayloads += 1;
|
|
3873
|
+
const correlationKey = this.#stanzaCorrelationKey(event.chatJid, event.stanzaId);
|
|
3874
|
+
if (!correlationKey) return;
|
|
3875
|
+
const now = Date.now();
|
|
3876
|
+
this.#recentDecryptedPayloads.set(correlationKey, {
|
|
3877
|
+
observedAt: now,
|
|
3878
|
+
encType: event.encType
|
|
3879
|
+
});
|
|
3880
|
+
for (const [key, value] of this.#recentDecryptedPayloads) {
|
|
3881
|
+
if (now - value.observedAt > DECRYPT_CORRELATION_TTL_MS) {
|
|
3882
|
+
this.#recentDecryptedPayloads.delete(key);
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
#handleUnhandledStanza(event) {
|
|
3887
|
+
this.#unhandledStanzas += 1;
|
|
3888
|
+
const correlationKey = this.#stanzaCorrelationKey(event.chatJid, event.stanzaId);
|
|
3889
|
+
const decrypted = correlationKey ? this.#recentDecryptedPayloads.get(correlationKey) : void 0;
|
|
3890
|
+
const now = Date.now();
|
|
3891
|
+
if (decrypted && now - decrypted.observedAt <= DECRYPT_CORRELATION_TTL_MS) {
|
|
3892
|
+
this.#decodeFailures += 1;
|
|
3893
|
+
this.#markDegraded();
|
|
3894
|
+
this.#emitStability({
|
|
3895
|
+
type: "messageDecodeFailure",
|
|
3896
|
+
payload: {
|
|
3897
|
+
occurredAt: new Date(now),
|
|
3898
|
+
reason: event.reason,
|
|
3899
|
+
encType: decrypted.encType,
|
|
3900
|
+
...event.stanzaId ? { stanzaId: event.stanzaId } : {},
|
|
3901
|
+
...event.chatJid ? { chatId: event.chatJid } : {}
|
|
3902
|
+
}
|
|
3903
|
+
});
|
|
3904
|
+
this.#logger.warn("Decrypted incoming payload could not be decoded into a supported stanza.", {
|
|
3905
|
+
reason: event.reason,
|
|
3906
|
+
encType: decrypted.encType,
|
|
3907
|
+
...event.stanzaId ? { stanzaId: event.stanzaId } : {},
|
|
3908
|
+
...event.chatJid ? { chatId: event.chatJid } : {}
|
|
3909
|
+
});
|
|
3910
|
+
if (correlationKey) this.#recentDecryptedPayloads.delete(correlationKey);
|
|
3911
|
+
return;
|
|
3912
|
+
}
|
|
3913
|
+
this.#logger.debug("Incoming stanza was not handled by Zapo.", {
|
|
3914
|
+
reason: event.reason,
|
|
3915
|
+
...event.stanzaId ? { stanzaId: event.stanzaId } : {},
|
|
3916
|
+
...event.chatJid ? { chatId: event.chatJid } : {}
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
#emitMessageDiscarded(reason, key) {
|
|
3920
|
+
this.#emitStability({
|
|
3921
|
+
type: "messageDiscarded",
|
|
3922
|
+
payload: {
|
|
3923
|
+
reason,
|
|
3924
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
3925
|
+
...key.id ? { messageId: key.id } : {},
|
|
3926
|
+
...key.remoteJid ? { chatId: key.remoteJid } : {}
|
|
3927
|
+
}
|
|
3928
|
+
});
|
|
3929
|
+
}
|
|
3930
|
+
#stanzaCorrelationKey(chatJid, stanzaId) {
|
|
3931
|
+
if (!stanzaId) return void 0;
|
|
3932
|
+
return `${chatJid ?? ""}:${stanzaId}`;
|
|
3933
|
+
}
|
|
3378
3934
|
#handleAddonEvent(event) {
|
|
3379
3935
|
const decrypted = this.#addonRecord(event.decrypted);
|
|
3380
3936
|
const protocol = this.#addonProtocolMessage(decrypted);
|
|
@@ -3622,21 +4178,121 @@ var ZapoProvider = class {
|
|
|
3622
4178
|
}
|
|
3623
4179
|
void this.#events.emit("groupChanged", { groupId });
|
|
3624
4180
|
}
|
|
4181
|
+
#observeZapoWarning(message, context) {
|
|
4182
|
+
if (message === GROUP_METADATA_MISMATCH_WARNING) {
|
|
4183
|
+
this.#phashMismatches += 1;
|
|
4184
|
+
this.#markDegraded();
|
|
4185
|
+
const groupId = stringValue(context.groupJid);
|
|
4186
|
+
if (groupId?.endsWith("@g.us")) void this.#recoverGroupMetadata(groupId);
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
if (message === "failed to decrypt incoming message") {
|
|
4190
|
+
this.#decryptFailures += 1;
|
|
4191
|
+
const detail = stringValue(context.message);
|
|
4192
|
+
const kind = detail === "sender key id mismatch" ? "sender_key_mismatch" : "decrypt_failure";
|
|
4193
|
+
if (kind === "sender_key_mismatch") this.#senderKeyMismatches += 1;
|
|
4194
|
+
this.#markDegraded();
|
|
4195
|
+
this.#emitCryptoDegraded(kind, context);
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4198
|
+
if (message === "addon auto-decrypt failed") {
|
|
4199
|
+
this.#addonDecryptFailures += 1;
|
|
4200
|
+
this.#markDegraded();
|
|
4201
|
+
this.#emitCryptoDegraded("addon_decrypt_failure", context);
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
#emitCryptoDegraded(kind, context) {
|
|
4205
|
+
const messageId = stringValue(context.id);
|
|
4206
|
+
const chatId = stringValue(context.from) ?? stringValue(context.groupJid);
|
|
4207
|
+
const participantId = stringValue(context.participant);
|
|
4208
|
+
this.#emitStability({
|
|
4209
|
+
type: "cryptoDegraded",
|
|
4210
|
+
payload: {
|
|
4211
|
+
kind,
|
|
4212
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
4213
|
+
...messageId ? { messageId } : {},
|
|
4214
|
+
...chatId ? { chatId } : {},
|
|
4215
|
+
...participantId ? { participantId } : {}
|
|
4216
|
+
}
|
|
4217
|
+
});
|
|
4218
|
+
}
|
|
4219
|
+
#markDegraded() {
|
|
4220
|
+
this.#degradedUntil = Math.max(this.#degradedUntil, Date.now() + PROVIDER_DEGRADED_WINDOW_MS);
|
|
4221
|
+
if (this.#healthRefreshTimer) clearTimeout(this.#healthRefreshTimer);
|
|
4222
|
+
const delay = Math.max(1, this.#degradedUntil - Date.now() + 5);
|
|
4223
|
+
this.#healthRefreshTimer = setTimeout(() => {
|
|
4224
|
+
this.#healthRefreshTimer = void 0;
|
|
4225
|
+
this.#emitStability({
|
|
4226
|
+
type: "healthRefresh",
|
|
4227
|
+
payload: { occurredAt: /* @__PURE__ */ new Date() }
|
|
4228
|
+
});
|
|
4229
|
+
}, delay);
|
|
4230
|
+
}
|
|
4231
|
+
#emitStability(event) {
|
|
4232
|
+
void this.#events.emit("stability", event).catch((error) => {
|
|
4233
|
+
this.#logger.warn("Stability event listener failed.", {
|
|
4234
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4235
|
+
});
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
async #recoverGroupMetadata(groupId) {
|
|
4239
|
+
const client = this.#client;
|
|
4240
|
+
const storeEntry = this.#storeEntry;
|
|
4241
|
+
if (!client || !storeEntry || !this.#connected) return;
|
|
4242
|
+
const now = Date.now();
|
|
4243
|
+
const lastRecoveryAt = this.#groupMetadataRecoveryAt.get(groupId);
|
|
4244
|
+
if (lastRecoveryAt !== void 0 && now - lastRecoveryAt < GROUP_METADATA_RECOVERY_COOLDOWN_MS) {
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
if (this.#groupMetadataRecoveryInFlight.has(groupId)) return;
|
|
4248
|
+
this.#groupMetadataRecoveryAt.set(groupId, now);
|
|
4249
|
+
this.#groupMetadataRecoveryInFlight.add(groupId);
|
|
4250
|
+
try {
|
|
4251
|
+
const sessionStore = storeEntry.store.session(this.#sessionId());
|
|
4252
|
+
await sessionStore.groupMetadata.deleteGroupMetadata(groupId);
|
|
4253
|
+
await client.group.queryGroupMetadata(groupId);
|
|
4254
|
+
await this.#events.emit("groupChanged", { groupId });
|
|
4255
|
+
this.#metadataRecoveries += 1;
|
|
4256
|
+
const recoveredAt = /* @__PURE__ */ new Date();
|
|
4257
|
+
this.#emitStability({
|
|
4258
|
+
type: "groupMetadataRecovered",
|
|
4259
|
+
payload: { groupId, recoveredAt }
|
|
4260
|
+
});
|
|
4261
|
+
this.#logger.info("Refreshed group metadata after participant-hash mismatch.", {
|
|
4262
|
+
groupId
|
|
4263
|
+
});
|
|
4264
|
+
} catch (error) {
|
|
4265
|
+
this.#metadataRecoveryFailures += 1;
|
|
4266
|
+
this.#logger.warn("Could not refresh group metadata after participant-hash mismatch.", {
|
|
4267
|
+
groupId,
|
|
4268
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4269
|
+
});
|
|
4270
|
+
} finally {
|
|
4271
|
+
this.#groupMetadataRecoveryInFlight.delete(groupId);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
3625
4274
|
async #handleConnectionEvent(client, event) {
|
|
3626
4275
|
if (client !== this.#client) return;
|
|
3627
4276
|
if (event.status === "open") {
|
|
4277
|
+
const wasReconnect = this.#reconnectAttempt > 0;
|
|
3628
4278
|
this.#connectPromise = void 0;
|
|
3629
4279
|
this.#connected = true;
|
|
4280
|
+
this.#state = "connected";
|
|
3630
4281
|
this.#resolvePairingReady?.();
|
|
3631
4282
|
this.#resolvePairingReady = void 0;
|
|
3632
4283
|
this.#connectedAtSeconds = Math.floor(Date.now() / 1e3);
|
|
4284
|
+
this.#lastConnectedAt = /* @__PURE__ */ new Date();
|
|
4285
|
+
if (wasReconnect) this.#reconnects += 1;
|
|
3633
4286
|
this.#reconnectAttempt = 0;
|
|
3634
4287
|
this.#closedNotified = false;
|
|
4288
|
+
this.#groupMetadataRecoveryAt.clear();
|
|
4289
|
+
this.#groupMetadataRecoveryInFlight.clear();
|
|
3635
4290
|
await this.#events.emit("connection", { state: "connected" });
|
|
3636
4291
|
return;
|
|
3637
4292
|
}
|
|
3638
4293
|
this.#connectPromise = void 0;
|
|
3639
4294
|
this.#connected = false;
|
|
4295
|
+
this.#lastDisconnectedAt = /* @__PURE__ */ new Date();
|
|
3640
4296
|
const details = disconnectDetails(event.reason, event.code);
|
|
3641
4297
|
const error = connectionError(event.reason, details);
|
|
3642
4298
|
if (this.#intentionalClose || event.isLogout === true || shouldStopAutomaticReconnect(details)) {
|
|
@@ -3678,6 +4334,8 @@ var ZapoProvider = class {
|
|
|
3678
4334
|
}
|
|
3679
4335
|
this.#connectPromise = void 0;
|
|
3680
4336
|
this.#connected = false;
|
|
4337
|
+
this.#state = "closed";
|
|
4338
|
+
this.#lastDisconnectedAt = /* @__PURE__ */ new Date();
|
|
3681
4339
|
await this.#emitClosedOnce(error);
|
|
3682
4340
|
this.#client = void 0;
|
|
3683
4341
|
try {
|
|
@@ -3693,6 +4351,7 @@ var ZapoProvider = class {
|
|
|
3693
4351
|
async #emitClosedOnce(error) {
|
|
3694
4352
|
if (this.#closedNotified) return;
|
|
3695
4353
|
this.#closedNotified = true;
|
|
4354
|
+
this.#state = "closed";
|
|
3696
4355
|
await this.#events.emit("connection", {
|
|
3697
4356
|
state: "closed",
|
|
3698
4357
|
...error ? { error } : {}
|
|
@@ -3714,15 +4373,16 @@ var ZapoProvider = class {
|
|
|
3714
4373
|
async #scheduleReconnect(error, delayOverrideMs) {
|
|
3715
4374
|
if (this.#reconnectTimer) return;
|
|
3716
4375
|
const options = this.#options.reconnect;
|
|
3717
|
-
const maxAttempts = options?.maxAttempts ??
|
|
4376
|
+
const maxAttempts = options?.maxAttempts ?? Number.POSITIVE_INFINITY;
|
|
3718
4377
|
if (options?.enabled === false || this.#reconnectAttempt >= maxAttempts) {
|
|
3719
4378
|
await this.#emitClosedOnce(error);
|
|
3720
4379
|
await this.#releaseStore();
|
|
3721
4380
|
return;
|
|
3722
4381
|
}
|
|
3723
4382
|
this.#reconnectAttempt += 1;
|
|
4383
|
+
this.#state = "reconnecting";
|
|
3724
4384
|
await this.#events.emit("connection", {
|
|
3725
|
-
state:
|
|
4385
|
+
state: this.#state,
|
|
3726
4386
|
attempt: this.#reconnectAttempt,
|
|
3727
4387
|
...error ? { error } : {}
|
|
3728
4388
|
});
|
|
@@ -4008,11 +4668,16 @@ var ZapoProvider = class {
|
|
|
4008
4668
|
return mentions.map((mention) => typeof mention === "string" ? mention : mention.mentionId);
|
|
4009
4669
|
}
|
|
4010
4670
|
#toZapoKey(key) {
|
|
4671
|
+
const original = this.#messageKeyStore.get(key)?.key;
|
|
4672
|
+
const participant = original?.participant ?? key.participantId;
|
|
4011
4673
|
return {
|
|
4012
4674
|
id: key.id,
|
|
4013
4675
|
remoteJid: key.chatId,
|
|
4014
4676
|
fromMe: key.fromMe,
|
|
4015
|
-
...
|
|
4677
|
+
...original?.remoteJidAlt ? { remoteJidAlt: original.remoteJidAlt } : {},
|
|
4678
|
+
...participant ? { participant } : {},
|
|
4679
|
+
...original?.participantAlt ? { participantAlt: original.participantAlt } : {},
|
|
4680
|
+
...original?.addressingMode ? { addressingMode: original.addressingMode } : {}
|
|
4016
4681
|
};
|
|
4017
4682
|
}
|
|
4018
4683
|
#sent(result, chatId) {
|
|
@@ -4408,7 +5073,12 @@ function createZapoStoreEntry(storePath) {
|
|
|
4408
5073
|
backends: {
|
|
4409
5074
|
sqlite: createSqliteStore({
|
|
4410
5075
|
path: storePath,
|
|
4411
|
-
driver: "auto"
|
|
5076
|
+
driver: "auto",
|
|
5077
|
+
pragmas: {
|
|
5078
|
+
journal_mode: "WAL",
|
|
5079
|
+
synchronous: "NORMAL",
|
|
5080
|
+
busy_timeout: 5e3
|
|
5081
|
+
}
|
|
4412
5082
|
})
|
|
4413
5083
|
},
|
|
4414
5084
|
providers: {
|
|
@@ -4426,11 +5096,19 @@ function createZapoStoreEntry(storePath) {
|
|
|
4426
5096
|
},
|
|
4427
5097
|
cacheProviders: {
|
|
4428
5098
|
messageSecret: "sqlite"
|
|
5099
|
+
},
|
|
5100
|
+
memory: {
|
|
5101
|
+
cacheTtlMs: {
|
|
5102
|
+
groupMetadataMs: GROUP_METADATA_CACHE_TTL_MS,
|
|
5103
|
+
deviceListMs: DEVICE_LIST_CACHE_TTL_MS
|
|
5104
|
+
}
|
|
4429
5105
|
}
|
|
4430
5106
|
});
|
|
4431
5107
|
const Database = require2("better-sqlite3");
|
|
4432
5108
|
const snapshotDb = new Database(messageSnapshotStorePath(storePath));
|
|
4433
5109
|
snapshotDb.exec(`
|
|
5110
|
+
PRAGMA journal_mode = WAL;
|
|
5111
|
+
PRAGMA synchronous = NORMAL;
|
|
4434
5112
|
PRAGMA busy_timeout = 5000;
|
|
4435
5113
|
CREATE TABLE IF NOT EXISTS whanext_message_snapshots (
|
|
4436
5114
|
session_id TEXT NOT NULL,
|
|
@@ -4671,6 +5349,44 @@ function booleanValue(value) {
|
|
|
4671
5349
|
if (value === false || value === 0) return false;
|
|
4672
5350
|
return void 0;
|
|
4673
5351
|
}
|
|
5352
|
+
function normalizeProviderTimeout(value, fallback) {
|
|
5353
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
5354
|
+
return Math.max(1e3, Math.floor(value));
|
|
5355
|
+
}
|
|
5356
|
+
function detectCryptoBackend() {
|
|
5357
|
+
const requested = process.env.ZAPO_NATIVE_BACKEND?.trim().toLowerCase() ?? "auto";
|
|
5358
|
+
if (requested === "js" || requested === "none") return "js";
|
|
5359
|
+
if ((requested === "auto" || requested === "napi") && nativeNapiAvailable()) {
|
|
5360
|
+
return "napi";
|
|
5361
|
+
}
|
|
5362
|
+
if ((requested === "auto" || requested === "wasm") && supportsZapoWasmRuntime() && moduleResolvable("@zapo-js/native/wasm/pkg/zapo_native_wasm.js") && moduleResolvable("@zapo-js/native/wasm/pkg/zapo_native_wasm_bg.wasm")) {
|
|
5363
|
+
return "wasm";
|
|
5364
|
+
}
|
|
5365
|
+
return "js";
|
|
5366
|
+
}
|
|
5367
|
+
function nativeNapiAvailable() {
|
|
5368
|
+
try {
|
|
5369
|
+
require2("@zapo-js/native");
|
|
5370
|
+
return true;
|
|
5371
|
+
} catch {
|
|
5372
|
+
return false;
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
function moduleResolvable(specifier) {
|
|
5376
|
+
try {
|
|
5377
|
+
require2.resolve(specifier);
|
|
5378
|
+
return true;
|
|
5379
|
+
} catch {
|
|
5380
|
+
return false;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
function supportsZapoWasmRuntime() {
|
|
5384
|
+
const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
|
|
5385
|
+
if (major > 22) return true;
|
|
5386
|
+
if (major === 22) return minor >= 12;
|
|
5387
|
+
if (major === 20) return minor >= 19;
|
|
5388
|
+
return false;
|
|
5389
|
+
}
|
|
4674
5390
|
function bytesField(value) {
|
|
4675
5391
|
if (value instanceof Uint8Array) return value;
|
|
4676
5392
|
return void 0;
|
|
@@ -4679,9 +5395,11 @@ var WhaNextZapoLogger = class _WhaNextZapoLogger {
|
|
|
4679
5395
|
level;
|
|
4680
5396
|
#logger;
|
|
4681
5397
|
#context;
|
|
4682
|
-
|
|
5398
|
+
#onWarn;
|
|
5399
|
+
constructor(logger, context = {}, onWarn) {
|
|
4683
5400
|
this.#logger = logger;
|
|
4684
5401
|
this.#context = context;
|
|
5402
|
+
this.#onWarn = onWarn;
|
|
4685
5403
|
this.level = logger.level === "debug" ? "debug" : logger.level === "warn" ? "warn" : logger.level === "error" || logger.level === "silent" ? "error" : "info";
|
|
4686
5404
|
}
|
|
4687
5405
|
trace(message, context) {
|
|
@@ -4694,7 +5412,9 @@ var WhaNextZapoLogger = class _WhaNextZapoLogger {
|
|
|
4694
5412
|
this.#logger.info(message, this.#merge(context));
|
|
4695
5413
|
}
|
|
4696
5414
|
warn(message, context) {
|
|
4697
|
-
|
|
5415
|
+
const merged = this.#merge(context);
|
|
5416
|
+
this.#logger.warn(message, merged);
|
|
5417
|
+
this.#onWarn?.(message, merged);
|
|
4698
5418
|
}
|
|
4699
5419
|
error(message, context) {
|
|
4700
5420
|
this.#logger.error(message, this.#merge(context));
|
|
@@ -4703,7 +5423,7 @@ var WhaNextZapoLogger = class _WhaNextZapoLogger {
|
|
|
4703
5423
|
return new _WhaNextZapoLogger(this.#logger, {
|
|
4704
5424
|
...this.#context,
|
|
4705
5425
|
...bindings
|
|
4706
|
-
});
|
|
5426
|
+
}, this.#onWarn);
|
|
4707
5427
|
}
|
|
4708
5428
|
#merge(context) {
|
|
4709
5429
|
return context ? { ...this.#context, ...context } : this.#context;
|
|
@@ -4731,7 +5451,9 @@ async function create(options = {}) {
|
|
|
4731
5451
|
...options.accountId ? { sessionId: options.accountId } : {},
|
|
4732
5452
|
...options.messageCacheSize !== void 0 ? { messageCacheSize: options.messageCacheSize } : {},
|
|
4733
5453
|
...options.processOfflineMessages !== void 0 ? { processOfflineMessages: options.processOfflineMessages } : {},
|
|
4734
|
-
...options.reconnect ? { reconnect: options.reconnect } : {}
|
|
5454
|
+
...options.reconnect ? { reconnect: options.reconnect } : {},
|
|
5455
|
+
...options.providerTimeouts?.connectTimeoutMs !== void 0 ? { connectTimeoutMs: options.providerTimeouts.connectTimeoutMs } : {},
|
|
5456
|
+
...options.providerTimeouts?.nodeQueryTimeoutMs !== void 0 ? { nodeQueryTimeoutMs: options.providerTimeouts.nodeQueryTimeoutMs } : {}
|
|
4735
5457
|
});
|
|
4736
5458
|
return new WhaNextApp(provider, {
|
|
4737
5459
|
...options.accountId ? { accountId: options.accountId } : {},
|
|
@@ -4963,6 +5685,12 @@ function mergeCreateOptions(shared, account) {
|
|
|
4963
5685
|
...account.reconnect
|
|
4964
5686
|
};
|
|
4965
5687
|
}
|
|
5688
|
+
if (shared.providerTimeouts || account.providerTimeouts) {
|
|
5689
|
+
merged.providerTimeouts = {
|
|
5690
|
+
...shared.providerTimeouts,
|
|
5691
|
+
...account.providerTimeouts
|
|
5692
|
+
};
|
|
5693
|
+
}
|
|
4966
5694
|
if (shared.logger && account.logger && typeof shared.logger === "object" && typeof account.logger === "object") {
|
|
4967
5695
|
merged.logger = {
|
|
4968
5696
|
...shared.logger,
|