miaw-core 1.13.1 → 1.13.2
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 +43 -0
- package/dist/cli/repl.js +3 -3
- package/dist/cli/repl.js.map +1 -1
- package/dist/client/MiawClient.d.ts +49 -3
- package/dist/client/MiawClient.d.ts.map +1 -1
- package/dist/client/MiawClient.js +177 -32
- package/dist/client/MiawClient.js.map +1 -1
- package/dist/constants/history-sync.d.ts +35 -0
- package/dist/constants/history-sync.d.ts.map +1 -0
- package/dist/constants/history-sync.js +40 -0
- package/dist/constants/history-sync.js.map +1 -0
- package/dist/constants/timeouts.d.ts +7 -0
- package/dist/constants/timeouts.d.ts.map +1 -1
- package/dist/constants/timeouts.js +7 -0
- package/dist/constants/timeouts.js.map +1 -1
- package/dist/types/index.d.ts +8 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ import { AuthHandler } from "../handlers/AuthHandler.js";
|
|
|
9
9
|
import { MessageHandler } from "../handlers/MessageHandler.js";
|
|
10
10
|
import { TIMEOUTS, THRESHOLDS } from "../constants/timeouts.js";
|
|
11
11
|
import { CACHE_CONFIG } from "../constants/cache.js";
|
|
12
|
+
import { LAZY_HISTORY_SYNC_TYPES } from "../constants/history-sync.js";
|
|
12
13
|
import { validatePhoneNumber, validateJID, validateMessageText, validateGroupName, validatePhoneNumbers, } from "../utils/validation.js";
|
|
13
14
|
import { createProxyAgents, maskProxyUrl, validateProxyConfig, } from "../utils/proxy-agent.js";
|
|
14
15
|
/**
|
|
@@ -172,6 +173,20 @@ export class MiawClient extends EventEmitter {
|
|
|
172
173
|
contactsStore = new Map();
|
|
173
174
|
chatsStore = new Map();
|
|
174
175
|
messagesStore = new Map();
|
|
176
|
+
// Message ids per chat, so storeMessage() dedups in O(1) instead of scanning
|
|
177
|
+
// the bucket. Kept in step with every messagesStore mutation.
|
|
178
|
+
messageIdIndex = new Map();
|
|
179
|
+
// Coalesced disk persistence: stores are marked dirty and written together
|
|
180
|
+
// after a quiet period (see schedulePersist).
|
|
181
|
+
dirtyStores = new Set();
|
|
182
|
+
persistTimer = null;
|
|
183
|
+
persistFirstDirtyAt = 0;
|
|
184
|
+
// Set once the session files are wiped (logout / unlink), so a late store
|
|
185
|
+
// update cannot recreate the deleted session dir. Cleared by connect().
|
|
186
|
+
persistSuspended = false;
|
|
187
|
+
// syncFullHistory as captured by the current connect(). enableSync() /
|
|
188
|
+
// disableSync() take effect on the next connection, not mid-sync.
|
|
189
|
+
activeSyncFullHistory = false;
|
|
175
190
|
labelsStore = new Map();
|
|
176
191
|
// Store label-chat associations (labelId -> Set of chatJids)
|
|
177
192
|
labelChatsStore = new Map();
|
|
@@ -201,7 +216,10 @@ export class MiawClient extends EventEmitter {
|
|
|
201
216
|
qrGracePeriod: options.qrGracePeriod || TIMEOUTS.QR_GRACE_PERIOD,
|
|
202
217
|
qrScanTimeout: options.qrScanTimeout || TIMEOUTS.QR_SCAN_TIMEOUT,
|
|
203
218
|
connectionTimeout: options.connectionTimeout || TIMEOUTS.CONNECTION_TIMEOUT,
|
|
204
|
-
|
|
219
|
+
// Default OFF: a fresh pair would otherwise pull the phone's entire
|
|
220
|
+
// history up front. Only the bootstrap is ingested; older history is
|
|
221
|
+
// fetched on demand via loadMoreMessages().
|
|
222
|
+
syncFullHistory: options.syncFullHistory === true,
|
|
205
223
|
// Default OFF. The whole point of the existing gate is that a bot must
|
|
206
224
|
// not react to its own sends, so opting in has to be deliberate.
|
|
207
225
|
emitOwnMessages: options.emitOwnMessages === true,
|
|
@@ -246,6 +264,10 @@ export class MiawClient extends EventEmitter {
|
|
|
246
264
|
// Load persisted stores from disk before connecting
|
|
247
265
|
// This ensures data is available even if Baileys history sync doesn't fire
|
|
248
266
|
// (Known Baileys v7 issue: history sync notification may be received but never processed)
|
|
267
|
+
// Flush first: on a reconnect, writes still in the debounce window are
|
|
268
|
+
// newer than the files and would otherwise be replaced by them.
|
|
269
|
+
this.flushPersist();
|
|
270
|
+
this.persistSuspended = false;
|
|
249
271
|
this.loadLabelsFromFile();
|
|
250
272
|
this.loadContactsFromFile();
|
|
251
273
|
this.loadLidMappingsFromFile(); // Load LID mappings BEFORE chats for resolution
|
|
@@ -280,6 +302,7 @@ export class MiawClient extends EventEmitter {
|
|
|
280
302
|
this.activeProxy = usingCustomAgent ? undefined : this.describeConfiguredProxy();
|
|
281
303
|
this.activeProxyKey = usingCustomAgent ? undefined : this.configuredProxyUrl();
|
|
282
304
|
// Create socket
|
|
305
|
+
this.activeSyncFullHistory = this.options.syncFullHistory;
|
|
283
306
|
const debugMode = this.options.debug;
|
|
284
307
|
const logger = this.logger;
|
|
285
308
|
this.socket = makeWASocket({
|
|
@@ -299,15 +322,15 @@ export class MiawClient extends EventEmitter {
|
|
|
299
322
|
// Auto-recreate signal sessions on retry and migrate PN<->LID sessions
|
|
300
323
|
// (Baileys rc13 default; set explicitly to document the LID reliance).
|
|
301
324
|
enableAutoSessionRecreation: true,
|
|
302
|
-
//
|
|
303
|
-
syncFullHistory: this.
|
|
325
|
+
// Ask the phone for its full history only when opted in
|
|
326
|
+
syncFullHistory: this.activeSyncFullHistory,
|
|
304
327
|
fireInitQueries: true,
|
|
305
|
-
// Debug callback to see if history sync notifications are received
|
|
306
328
|
shouldSyncHistoryMessage: (msg) => {
|
|
329
|
+
const accept = this.shouldProcessHistorySync(msg.syncType);
|
|
307
330
|
if (debugMode) {
|
|
308
|
-
logger.debug(`[shouldSyncHistoryMessage] syncType: ${msg.syncType}`);
|
|
331
|
+
logger.debug(`[shouldSyncHistoryMessage] syncType: ${msg.syncType} -> ${accept ? "accept" : "skip"}`);
|
|
309
332
|
}
|
|
310
|
-
return
|
|
333
|
+
return accept;
|
|
311
334
|
},
|
|
312
335
|
// Proxy support: agent for WebSocket, fetchAgent for media HTTP requests
|
|
313
336
|
...(proxyAgents?.wsAgent ? { agent: proxyAgents.wsAgent } : {}),
|
|
@@ -694,9 +717,10 @@ export class MiawClient extends EventEmitter {
|
|
|
694
717
|
this.updateChatsStore(chats);
|
|
695
718
|
});
|
|
696
719
|
// History sync - populates contacts, chats, and messages from history
|
|
697
|
-
this.socket.ev.on("messaging-history.set", ({ chats, contacts, messages, lidPnMappings, isLatest, peerDataRequestSessionId }) => {
|
|
720
|
+
this.socket.ev.on("messaging-history.set", ({ chats, contacts, messages, lidPnMappings, isLatest, peerDataRequestSessionId, syncType }) => {
|
|
698
721
|
if (this.options.debug) {
|
|
699
722
|
this.logger.debug("\n========== MESSAGING HISTORY SYNC ==========");
|
|
723
|
+
this.logger.debug(`Sync type: ${syncType ?? "unknown"}`);
|
|
700
724
|
this.logger.debug(`Contacts: ${contacts.length}, Chats: ${chats.length}, Messages: ${messages.length}, LID maps: ${lidPnMappings?.length ?? 0}`);
|
|
701
725
|
this.logger.debug(`Is Latest: ${isLatest}, SessionId: ${peerDataRequestSessionId || 'none'}`);
|
|
702
726
|
this.logger.debug("============================================\n");
|
|
@@ -916,9 +940,9 @@ export class MiawClient extends EventEmitter {
|
|
|
916
940
|
};
|
|
917
941
|
this.labelsStore.set(label.id, labelData);
|
|
918
942
|
}
|
|
919
|
-
// Persist labels to disk
|
|
943
|
+
// Persist labels to disk
|
|
920
944
|
// This ensures labels survive reconnections where resyncAppState returns only patches
|
|
921
|
-
this.
|
|
945
|
+
this.schedulePersist("labels");
|
|
922
946
|
}
|
|
923
947
|
});
|
|
924
948
|
// Handle label-chat associations
|
|
@@ -1053,7 +1077,7 @@ export class MiawClient extends EventEmitter {
|
|
|
1053
1077
|
}
|
|
1054
1078
|
// Persist to disk if this is a new mapping
|
|
1055
1079
|
if (isNew) {
|
|
1056
|
-
this.
|
|
1080
|
+
this.schedulePersist("lid");
|
|
1057
1081
|
}
|
|
1058
1082
|
}
|
|
1059
1083
|
/**
|
|
@@ -1366,10 +1390,10 @@ export class MiawClient extends EventEmitter {
|
|
|
1366
1390
|
this.contactsStore.set(contact.id, contactInfo);
|
|
1367
1391
|
}
|
|
1368
1392
|
}
|
|
1369
|
-
// Persist contacts to disk
|
|
1393
|
+
// Persist contacts to disk
|
|
1370
1394
|
// This ensures contacts survive reconnections where history sync may not fire
|
|
1371
1395
|
if (contacts.length > 0) {
|
|
1372
|
-
this.
|
|
1396
|
+
this.schedulePersist("contacts");
|
|
1373
1397
|
}
|
|
1374
1398
|
}
|
|
1375
1399
|
/**
|
|
@@ -1417,10 +1441,10 @@ export class MiawClient extends EventEmitter {
|
|
|
1417
1441
|
this.chatsStore.set(originalId, chatInfo);
|
|
1418
1442
|
}
|
|
1419
1443
|
}
|
|
1420
|
-
// Persist chats to disk
|
|
1444
|
+
// Persist chats to disk
|
|
1421
1445
|
// This ensures chats survive reconnections where history sync may not fire
|
|
1422
1446
|
if (chats.length > 0) {
|
|
1423
|
-
this.
|
|
1447
|
+
this.schedulePersist("chats");
|
|
1424
1448
|
}
|
|
1425
1449
|
}
|
|
1426
1450
|
/**
|
|
@@ -1455,6 +1479,26 @@ export class MiawClient extends EventEmitter {
|
|
|
1455
1479
|
* @returns true if session was cleared, false if no session existed
|
|
1456
1480
|
*/
|
|
1457
1481
|
clearSession() {
|
|
1482
|
+
return this.wipeSessionFiles();
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Delete the session files and everything cached for the account.
|
|
1486
|
+
*
|
|
1487
|
+
* Pending store writes are dropped and further ones refused until the next
|
|
1488
|
+
* connect(), or a late timer would recreate the deleted dir. The in-memory
|
|
1489
|
+
* stores are emptied too, so the old account's chats and messages are not
|
|
1490
|
+
* written into the next pairing's session.
|
|
1491
|
+
*/
|
|
1492
|
+
wipeSessionFiles() {
|
|
1493
|
+
this.cancelPersist();
|
|
1494
|
+
this.persistSuspended = true;
|
|
1495
|
+
this.contactsStore.clear();
|
|
1496
|
+
this.chatsStore.clear();
|
|
1497
|
+
this.messagesStore.clear();
|
|
1498
|
+
this.messageIdIndex.clear();
|
|
1499
|
+
this.labelsStore.clear();
|
|
1500
|
+
this.labelChatsStore.clear();
|
|
1501
|
+
this.lidToJidMap.clear();
|
|
1458
1502
|
return this.authHandler.clearSession();
|
|
1459
1503
|
}
|
|
1460
1504
|
/**
|
|
@@ -1471,6 +1515,8 @@ export class MiawClient extends EventEmitter {
|
|
|
1471
1515
|
}
|
|
1472
1516
|
// Reset logout flag
|
|
1473
1517
|
this.loggingOut = false;
|
|
1518
|
+
// Write anything still pending before the stores are dropped
|
|
1519
|
+
this.flushPersist();
|
|
1474
1520
|
// Clear LID cache
|
|
1475
1521
|
this.lidToJidMap.clear();
|
|
1476
1522
|
// Remove all event listeners
|
|
@@ -1519,7 +1565,7 @@ export class MiawClient extends EventEmitter {
|
|
|
1519
1565
|
// Don't reconnect if logged out - clear session for fresh QR code on next connect
|
|
1520
1566
|
if (statusCode === DisconnectReason.loggedOut) {
|
|
1521
1567
|
this.logger.info("Logged out, clearing session for fresh authentication");
|
|
1522
|
-
this.
|
|
1568
|
+
this.wipeSessionFiles();
|
|
1523
1569
|
return false;
|
|
1524
1570
|
}
|
|
1525
1571
|
// Don't auto-reconnect when replaced by another connection: reconnecting
|
|
@@ -2932,6 +2978,90 @@ export class MiawClient extends EventEmitter {
|
|
|
2932
2978
|
};
|
|
2933
2979
|
}
|
|
2934
2980
|
// =============================================================================
|
|
2981
|
+
// History Sync Policy & Coalesced Persistence
|
|
2982
|
+
// =============================================================================
|
|
2983
|
+
/**
|
|
2984
|
+
* Whether a history-sync notification of this type should be processed.
|
|
2985
|
+
*
|
|
2986
|
+
* Full mode accepts everything. Lazy mode (the default) accepts only the
|
|
2987
|
+
* bootstrap, small metadata payloads and ON_DEMAND replies, and skips the
|
|
2988
|
+
* bulk RECENT / FULL / status payloads. Never reject every type: ON_DEMAND
|
|
2989
|
+
* is what loadMoreMessages() waits for, and Baileys gets LID mappings only
|
|
2990
|
+
* through history sync.
|
|
2991
|
+
*/
|
|
2992
|
+
shouldProcessHistorySync(syncType) {
|
|
2993
|
+
if (this.activeSyncFullHistory)
|
|
2994
|
+
return true;
|
|
2995
|
+
return syncType != null && LAZY_HISTORY_SYNC_TYPES.has(syncType);
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* Mark a store dirty and (re)arm the write timer.
|
|
2999
|
+
*
|
|
3000
|
+
* Writes are trailing-debounced so a burst - e.g. a history batch of
|
|
3001
|
+
* thousands of messages - costs one write per store instead of one full-file
|
|
3002
|
+
* rewrite per item. PERSIST_MAX_WAIT bounds the delay under sustained
|
|
3003
|
+
* activity so a chatty session still persists.
|
|
3004
|
+
*/
|
|
3005
|
+
schedulePersist(store) {
|
|
3006
|
+
if (this.persistSuspended)
|
|
3007
|
+
return;
|
|
3008
|
+
this.dirtyStores.add(store);
|
|
3009
|
+
const now = Date.now();
|
|
3010
|
+
if (this.persistFirstDirtyAt === 0) {
|
|
3011
|
+
this.persistFirstDirtyAt = now;
|
|
3012
|
+
}
|
|
3013
|
+
const remaining = TIMEOUTS.PERSIST_MAX_WAIT - (now - this.persistFirstDirtyAt);
|
|
3014
|
+
const delay = Math.max(0, Math.min(TIMEOUTS.PERSIST_DEBOUNCE, remaining));
|
|
3015
|
+
if (this.persistTimer) {
|
|
3016
|
+
clearTimeout(this.persistTimer);
|
|
3017
|
+
}
|
|
3018
|
+
this.persistTimer = setTimeout(() => this.flushPersist(), delay);
|
|
3019
|
+
// Never hold the process open just to write a cache.
|
|
3020
|
+
this.persistTimer.unref?.();
|
|
3021
|
+
}
|
|
3022
|
+
/** Write every dirty store now. */
|
|
3023
|
+
flushPersist() {
|
|
3024
|
+
const dirty = [...this.dirtyStores];
|
|
3025
|
+
this.cancelPersist();
|
|
3026
|
+
for (const store of dirty) {
|
|
3027
|
+
switch (store) {
|
|
3028
|
+
case "messages":
|
|
3029
|
+
this.saveMessagesToFile();
|
|
3030
|
+
break;
|
|
3031
|
+
case "chats":
|
|
3032
|
+
this.saveChatsToFile();
|
|
3033
|
+
break;
|
|
3034
|
+
case "contacts":
|
|
3035
|
+
this.saveContactsToFile();
|
|
3036
|
+
break;
|
|
3037
|
+
case "lid":
|
|
3038
|
+
this.saveLidMappingsToFile();
|
|
3039
|
+
break;
|
|
3040
|
+
case "labels":
|
|
3041
|
+
this.saveLabelsToFile();
|
|
3042
|
+
break;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
/** Drop pending writes without writing them. */
|
|
3047
|
+
cancelPersist() {
|
|
3048
|
+
if (this.persistTimer) {
|
|
3049
|
+
clearTimeout(this.persistTimer);
|
|
3050
|
+
this.persistTimer = null;
|
|
3051
|
+
}
|
|
3052
|
+
this.dirtyStores.clear();
|
|
3053
|
+
this.persistFirstDirtyAt = 0;
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
3056
|
+
* Write JSON via a temp file + rename, so a crash mid-write never leaves a
|
|
3057
|
+
* truncated store behind. Compact output: these are caches, not configs.
|
|
3058
|
+
*/
|
|
3059
|
+
writeJsonFileAtomic(filePath, data) {
|
|
3060
|
+
const tmpPath = `${filePath}.tmp`;
|
|
3061
|
+
fs.writeFileSync(tmpPath, JSON.stringify(data), "utf8");
|
|
3062
|
+
fs.renameSync(tmpPath, filePath);
|
|
3063
|
+
}
|
|
3064
|
+
// =============================================================================
|
|
2935
3065
|
// Persistent Label Storage
|
|
2936
3066
|
// =============================================================================
|
|
2937
3067
|
/**
|
|
@@ -2955,7 +3085,7 @@ export class MiawClient extends EventEmitter {
|
|
|
2955
3085
|
}
|
|
2956
3086
|
// Convert Map to array for JSON serialization
|
|
2957
3087
|
const labelsData = Array.from(this.labelsStore.values());
|
|
2958
|
-
|
|
3088
|
+
this.writeJsonFileAtomic(labelsPath, labelsData);
|
|
2959
3089
|
this.logger.debug(`Saved ${labelsData.length} labels to ${labelsPath}`);
|
|
2960
3090
|
}
|
|
2961
3091
|
catch (error) {
|
|
@@ -3023,7 +3153,7 @@ export class MiawClient extends EventEmitter {
|
|
|
3023
3153
|
}
|
|
3024
3154
|
}
|
|
3025
3155
|
const contactsData = Array.from(uniqueContacts.values());
|
|
3026
|
-
|
|
3156
|
+
this.writeJsonFileAtomic(contactsPath, contactsData);
|
|
3027
3157
|
this.logger.debug(`Saved ${contactsData.length} contacts to ${contactsPath}`);
|
|
3028
3158
|
}
|
|
3029
3159
|
catch (error) {
|
|
@@ -3079,7 +3209,7 @@ export class MiawClient extends EventEmitter {
|
|
|
3079
3209
|
const mappings = this.getLidMappings();
|
|
3080
3210
|
const count = Object.keys(mappings).length;
|
|
3081
3211
|
if (count > 0) {
|
|
3082
|
-
|
|
3212
|
+
this.writeJsonFileAtomic(mappingsPath, mappings);
|
|
3083
3213
|
this.logger.debug(`Saved ${count} LID mappings to ${mappingsPath}`);
|
|
3084
3214
|
}
|
|
3085
3215
|
}
|
|
@@ -3141,7 +3271,7 @@ export class MiawClient extends EventEmitter {
|
|
|
3141
3271
|
}
|
|
3142
3272
|
}
|
|
3143
3273
|
const chatsData = Array.from(uniqueChats.values());
|
|
3144
|
-
|
|
3274
|
+
this.writeJsonFileAtomic(chatsPath, chatsData);
|
|
3145
3275
|
this.logger.debug(`Saved ${chatsData.length} chats to ${chatsPath}`);
|
|
3146
3276
|
}
|
|
3147
3277
|
catch (error) {
|
|
@@ -3194,18 +3324,26 @@ export class MiawClient extends EventEmitter {
|
|
|
3194
3324
|
*/
|
|
3195
3325
|
storeMessage(normalized) {
|
|
3196
3326
|
const chatJid = normalized.from;
|
|
3197
|
-
|
|
3198
|
-
if (
|
|
3199
|
-
|
|
3200
|
-
|
|
3327
|
+
let ids = this.messageIdIndex.get(chatJid);
|
|
3328
|
+
if (!ids) {
|
|
3329
|
+
ids = new Set();
|
|
3330
|
+
this.messageIdIndex.set(chatJid, ids);
|
|
3331
|
+
}
|
|
3332
|
+
// Dedup by id. An empty id (missing key.id) can't be deduped -> append.
|
|
3333
|
+
if (normalized.id) {
|
|
3334
|
+
if (ids.has(normalized.id)) {
|
|
3201
3335
|
return false;
|
|
3202
3336
|
}
|
|
3337
|
+
ids.add(normalized.id);
|
|
3338
|
+
}
|
|
3339
|
+
const bucket = this.messagesStore.get(chatJid);
|
|
3340
|
+
if (bucket) {
|
|
3203
3341
|
bucket.push(normalized);
|
|
3204
3342
|
}
|
|
3205
3343
|
else {
|
|
3206
3344
|
this.messagesStore.set(chatJid, [normalized]);
|
|
3207
3345
|
}
|
|
3208
|
-
this.
|
|
3346
|
+
this.schedulePersist("messages");
|
|
3209
3347
|
return true;
|
|
3210
3348
|
}
|
|
3211
3349
|
/**
|
|
@@ -3228,7 +3366,7 @@ export class MiawClient extends EventEmitter {
|
|
|
3228
3366
|
}
|
|
3229
3367
|
// Count total messages
|
|
3230
3368
|
const totalMessages = Array.from(this.messagesStore.values()).reduce((sum, msgs) => sum + msgs.length, 0);
|
|
3231
|
-
|
|
3369
|
+
this.writeJsonFileAtomic(messagesPath, messagesData);
|
|
3232
3370
|
this.logger.debug(`Saved ${totalMessages} messages across ${this.messagesStore.size} chats to ${messagesPath}`);
|
|
3233
3371
|
}
|
|
3234
3372
|
catch (error) {
|
|
@@ -3250,10 +3388,12 @@ export class MiawClient extends EventEmitter {
|
|
|
3250
3388
|
if (messagesData && typeof messagesData === "object") {
|
|
3251
3389
|
// Clear existing store and populate from file
|
|
3252
3390
|
this.messagesStore.clear();
|
|
3391
|
+
this.messageIdIndex.clear();
|
|
3253
3392
|
let totalMessages = 0;
|
|
3254
3393
|
for (const [jid, messages] of Object.entries(messagesData)) {
|
|
3255
3394
|
if (Array.isArray(messages)) {
|
|
3256
3395
|
this.messagesStore.set(jid, messages);
|
|
3396
|
+
this.messageIdIndex.set(jid, new Set(messages.map((m) => m.id).filter(Boolean)));
|
|
3257
3397
|
totalMessages += messages.length;
|
|
3258
3398
|
}
|
|
3259
3399
|
}
|
|
@@ -6548,6 +6688,8 @@ export class MiawClient extends EventEmitter {
|
|
|
6548
6688
|
this.activeProxyKey = undefined;
|
|
6549
6689
|
// Cleanup console filter if it was enabled (reference counted)
|
|
6550
6690
|
this.releaseConsoleFilter();
|
|
6691
|
+
// Session is preserved, so pending store writes must land
|
|
6692
|
+
this.flushPersist();
|
|
6551
6693
|
this.updateConnectionState("disconnected");
|
|
6552
6694
|
this.logger.info("Disconnected (session preserved)");
|
|
6553
6695
|
// The `disconnected` event previously fired only from handleDisconnect(),
|
|
@@ -6656,8 +6798,8 @@ export class MiawClient extends EventEmitter {
|
|
|
6656
6798
|
this.socket.end(undefined);
|
|
6657
6799
|
this.socket = null;
|
|
6658
6800
|
}
|
|
6659
|
-
// Clear session files
|
|
6660
|
-
this.
|
|
6801
|
+
// Clear session files (and drop pending store writes with them)
|
|
6802
|
+
this.wipeSessionFiles();
|
|
6661
6803
|
// Reset logout flag
|
|
6662
6804
|
this.loggingOut = false;
|
|
6663
6805
|
// Cleanup console filter if it was enabled (reference counted)
|
|
@@ -6762,21 +6904,24 @@ export class MiawClient extends EventEmitter {
|
|
|
6762
6904
|
}
|
|
6763
6905
|
}
|
|
6764
6906
|
/**
|
|
6765
|
-
*
|
|
6907
|
+
* Request the phone's full message history on the next connection.
|
|
6908
|
+
* Only a fresh pairing receives it; an existing session is not re-synced.
|
|
6766
6909
|
*/
|
|
6767
6910
|
enableSync() {
|
|
6768
6911
|
this.options.syncFullHistory = true;
|
|
6769
|
-
this.logger.info("
|
|
6912
|
+
this.logger.info("Full history sync enabled (will take effect on next connect)");
|
|
6770
6913
|
}
|
|
6771
6914
|
/**
|
|
6772
|
-
*
|
|
6915
|
+
* Ingest only the bootstrap on the next connection (the default). The
|
|
6916
|
+
* bootstrap and on-demand replies are still processed, so loadMoreMessages()
|
|
6917
|
+
* keeps working.
|
|
6773
6918
|
*/
|
|
6774
6919
|
disableSync() {
|
|
6775
6920
|
this.options.syncFullHistory = false;
|
|
6776
|
-
this.logger.info("
|
|
6921
|
+
this.logger.info("Full history sync disabled (bootstrap + on-demand still active; takes effect on next connect)");
|
|
6777
6922
|
}
|
|
6778
6923
|
/**
|
|
6779
|
-
* Check if history sync is enabled
|
|
6924
|
+
* Check if full history sync is enabled
|
|
6780
6925
|
*/
|
|
6781
6926
|
isSyncEnabled() {
|
|
6782
6927
|
return this.options.syncFullHistory;
|