@aunucore/baileys 1.0.86 → 1.0.87
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/lib/Socket/chats.js +51 -4
- package/lib/Socket/messages-recv.js +8 -1
- package/lib/Socket/mex.js +20 -2
- package/lib/Utils/aunu-message-inspect.js +30 -24
- package/lib/Utils/chat-utils.js +43 -0
- package/lib/WAUSync/USyncQuery.js +16 -1
- package/package.json +1 -1
package/lib/Socket/chats.js
CHANGED
|
@@ -82,8 +82,30 @@ export const makeChatsSocket = (config) => {
|
|
|
82
82
|
// AunuBaileys (1.0.71): a privacy change is a settings change —
|
|
83
83
|
// refetch the full set (the cached copy is now stale) and publish
|
|
84
84
|
// it (donor event: violetix 'privacy.update' on account_sync).
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
// AunuBaileys (1.0.87, HB-163): the SET above has already
|
|
86
|
+
// been applied by the server when this refetch runs. A
|
|
87
|
+
// refetch failure used to reject the whole privacyQuery —
|
|
88
|
+
// the caller saw a failed update that had in fact taken
|
|
89
|
+
// effect, the cache kept the OLD value, and readMessages
|
|
90
|
+
// derived its receipt type from that stale cache (sending
|
|
91
|
+
// 'read' receipts against a receipts setting the user had
|
|
92
|
+
// just switched off). Make the refetch best-effort: on
|
|
93
|
+
// failure, fold the applied change into the cached copy so
|
|
94
|
+
// cache and event reflect what the server now holds for
|
|
95
|
+
// this key. Never fabricate a partial cache when none
|
|
96
|
+
// exists — that would suppress the next full fetch.
|
|
97
|
+
try {
|
|
98
|
+
privacySettings = await fetchPrivacySettings(true);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
logger.warn({ err, name, value }, 'failed to refetch privacy settings after a successful set; folding the change into the cached copy');
|
|
102
|
+
if (privacySettings) {
|
|
103
|
+
privacySettings = { ...privacySettings, [name]: value };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (privacySettings) {
|
|
107
|
+
ev.emit('privacy.update', privacySettings);
|
|
108
|
+
}
|
|
87
109
|
};
|
|
88
110
|
const updateMessagesPrivacy = async (value) => {
|
|
89
111
|
await privacyQuery('messages', value);
|
|
@@ -946,11 +968,35 @@ export const makeChatsSocket = (config) => {
|
|
|
946
968
|
const doAppStateSync = async () => {
|
|
947
969
|
if (syncState === SyncState.Syncing) {
|
|
948
970
|
logger.info('Doing app state sync');
|
|
949
|
-
|
|
950
|
-
//
|
|
971
|
+
// AunuBaileys (1.0.87, HB-241): resyncAppState can
|
|
972
|
+
// throw OUTSIDE its per-collection handling — the
|
|
973
|
+
// sync IQ itself, a malformed blob in
|
|
974
|
+
// extractSyncdPatches, the resync-round cap. That
|
|
975
|
+
// throw used to reject upsertMessage and leave
|
|
976
|
+
// syncState at Syncing for the rest of the session:
|
|
977
|
+
// the full initial sync was never retried (nothing
|
|
978
|
+
// re-triggers it) and the state machine never
|
|
979
|
+
// reached Online. A failed sync must END the
|
|
980
|
+
// Syncing state, not freeze it — go Online and
|
|
981
|
+
// flush either way, and only count the sync as
|
|
982
|
+
// complete (the accountSyncCounter advance below)
|
|
983
|
+
// when it actually succeeded.
|
|
984
|
+
let syncFailed = false;
|
|
985
|
+
try {
|
|
986
|
+
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
987
|
+
}
|
|
988
|
+
catch (err) {
|
|
989
|
+
syncFailed = true;
|
|
990
|
+
logger.error({ err }, 'app state sync failed; transitioning to Online state anyway');
|
|
991
|
+
}
|
|
992
|
+
// Sync ran its course, go online and flush everything
|
|
951
993
|
syncState = SyncState.Online;
|
|
952
994
|
logger.info('App state sync complete, transitioning to Online state and flushing buffer');
|
|
953
995
|
ev.flush();
|
|
996
|
+
// (HB-241) a failed sync is not a completed one:
|
|
997
|
+
// do not advance the counter that signals the
|
|
998
|
+
// initial sync finished.
|
|
999
|
+
if (!syncFailed) {
|
|
954
1000
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
955
1001
|
// AunuBaileys (1.0.81, HB-257): same guard as the
|
|
956
1002
|
// pushName emit above — a throwing 'creds.update'
|
|
@@ -963,6 +1009,7 @@ export const makeChatsSocket = (config) => {
|
|
|
963
1009
|
catch (err) {
|
|
964
1010
|
onUnexpectedError(err, 'creds.update (accountSyncCounter)');
|
|
965
1011
|
}
|
|
1012
|
+
}
|
|
966
1013
|
}
|
|
967
1014
|
};
|
|
968
1015
|
await Promise.all([
|
|
@@ -1771,7 +1771,14 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1771
1771
|
}
|
|
1772
1772
|
const operation = data?.operation;
|
|
1773
1773
|
const updates = data?.updates;
|
|
1774
|
-
|
|
1774
|
+
// AunuBaileys (1.0.87, HB-121): this guard used to be a
|
|
1775
|
+
// truthiness check only — a MEX payload whose `updates`
|
|
1776
|
+
// is a non-array (object/number/boolean) passed it, and
|
|
1777
|
+
// the for...of loops in every switch branch below threw a
|
|
1778
|
+
// TypeError, silently dropping the whole notification
|
|
1779
|
+
// (handleNotification acks the stanza in its finally
|
|
1780
|
+
// either way, so nothing ever retried it).
|
|
1781
|
+
if (!Array.isArray(updates) || !operation) {
|
|
1775
1782
|
logger.warn({ data }, 'Invalid mex newsletter notification content');
|
|
1776
1783
|
return;
|
|
1777
1784
|
}
|
package/lib/Socket/mex.js
CHANGED
|
@@ -22,11 +22,29 @@ export const executeWMexQuery = async (variables, queryId, dataPath, query, gene
|
|
|
22
22
|
const result = await wMexQuery(variables, queryId, query, generateMessageTag);
|
|
23
23
|
const child = getBinaryNodeChild(result, 'result');
|
|
24
24
|
if (child?.content) {
|
|
25
|
-
|
|
25
|
+
// AunuBaileys (1.0.87, HB-211): a <result> that is not JSON
|
|
26
|
+
// (including a child-node array body, which stringifies to
|
|
27
|
+
// "[object Object]") used to reject with a raw SyntaxError
|
|
28
|
+
// the host could not classify.
|
|
29
|
+
let data;
|
|
30
|
+
try {
|
|
31
|
+
data = JSON.parse(child.content.toString());
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Boom('GraphQL server error: unparseable response', { statusCode: 400, data: result });
|
|
35
|
+
}
|
|
26
36
|
if (data.errors && data.errors.length > 0) {
|
|
27
37
|
const errorMessages = data.errors.map((err) => err.message || 'Unknown error').join(', ');
|
|
28
38
|
const firstError = data.errors[0];
|
|
29
|
-
|
|
39
|
+
// AunuBaileys (1.0.87, HB-211): the server's error_code
|
|
40
|
+
// went into the Boom constructor unsanitised — a string
|
|
41
|
+
// code ('RATE_LIMITED') or an out-of-range one (200)
|
|
42
|
+
// made `new Boom` itself throw a non-Boom TypeError,
|
|
43
|
+
// and the server's actual error message was lost with
|
|
44
|
+
// it. Same sanitation as HB-42: only a numeric HTTP
|
|
45
|
+
// error code survives, everything else becomes 400.
|
|
46
|
+
const rawErrorCode = firstError.extensions?.error_code;
|
|
47
|
+
const errorCode = typeof rawErrorCode === 'number' && rawErrorCode >= 400 && rawErrorCode <= 599 ? rawErrorCode : 400;
|
|
30
48
|
throw new Boom(`GraphQL server error: ${errorMessages}`, { statusCode: errorCode, data: firstError });
|
|
31
49
|
}
|
|
32
50
|
const response = dataPath ? data?.data?.[dataPath] : data?.data;
|
|
@@ -37,31 +37,37 @@ import { jidDecode } from '../WABinary/index.js';
|
|
|
37
37
|
import { getKeyAuthor } from './generics.js';
|
|
38
38
|
import { getAggregateVotesInPollMessage, normalizeMessageContent } from './messages.js';
|
|
39
39
|
|
|
40
|
+
// AunuBaileys (1.0.87, HB-218): every operand is optional-chained,
|
|
41
|
+
// exactly like the base getFutureProofMessage in messages.js. A
|
|
42
|
+
// wrapper whose `message` field is absent decodes with message ===
|
|
43
|
+
// null; the bare derefs used to throw a raw TypeError on the next
|
|
44
|
+
// loop iteration of normalizeAunuMessageContentExtended (and in
|
|
45
|
+
// getAggregateVotesInPollMessageExtended downstream of it).
|
|
40
46
|
const getFutureProofMessageExtended = (message) => (
|
|
41
|
-
message
|
|
42
|
-
message
|
|
43
|
-
message
|
|
44
|
-
message
|
|
45
|
-
message
|
|
46
|
-
message
|
|
47
|
-
message
|
|
48
|
-
message
|
|
49
|
-
message
|
|
50
|
-
message
|
|
51
|
-
message
|
|
52
|
-
message
|
|
53
|
-
message
|
|
54
|
-
message
|
|
55
|
-
message
|
|
56
|
-
message
|
|
57
|
-
message
|
|
58
|
-
message
|
|
59
|
-
message
|
|
60
|
-
message
|
|
61
|
-
message
|
|
62
|
-
message
|
|
63
|
-
message
|
|
64
|
-
message
|
|
47
|
+
message?.ephemeralMessage ||
|
|
48
|
+
message?.viewOnceMessage ||
|
|
49
|
+
message?.documentWithCaptionMessage ||
|
|
50
|
+
message?.viewOnceMessageV2 ||
|
|
51
|
+
message?.viewOnceMessageV2Extension ||
|
|
52
|
+
message?.editedMessage ||
|
|
53
|
+
message?.groupMentionedMessage ||
|
|
54
|
+
message?.botInvokeMessage ||
|
|
55
|
+
message?.lottieStickerMessage ||
|
|
56
|
+
message?.eventCoverImage ||
|
|
57
|
+
message?.statusMentionMessage ||
|
|
58
|
+
message?.pollCreationOptionImageMessage ||
|
|
59
|
+
message?.associatedChildMessage ||
|
|
60
|
+
message?.groupStatusMentionMessage ||
|
|
61
|
+
message?.pollCreationMessageV4 ||
|
|
62
|
+
message?.pollCreationMessageV5 ||
|
|
63
|
+
message?.statusAddYours ||
|
|
64
|
+
message?.groupStatusMessage ||
|
|
65
|
+
message?.limitSharingMessage ||
|
|
66
|
+
message?.botTaskMessage ||
|
|
67
|
+
message?.questionMessage ||
|
|
68
|
+
message?.groupStatusMessageV2 ||
|
|
69
|
+
message?.botForwardedMessage ||
|
|
70
|
+
message?.spoilerMessage
|
|
65
71
|
);
|
|
66
72
|
|
|
67
73
|
/**
|
package/lib/Utils/chat-utils.js
CHANGED
|
@@ -286,6 +286,22 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
|
|
|
286
286
|
};
|
|
287
287
|
const mutationMap = {};
|
|
288
288
|
for (const syncd of syncds) {
|
|
289
|
+
// AunuBaileys (1.0.87, HB-141 + HB-149): snapshot the state
|
|
290
|
+
// before this patch. The version assignment inside the try
|
|
291
|
+
// runs BEFORE the patch is decoded, so a patch that failed
|
|
292
|
+
// (bad content HMAC, missing key, snapshot-MAC mismatch)
|
|
293
|
+
// still advanced the persisted version past itself — its
|
|
294
|
+
// mutations were never re-sent by the server, and the LT
|
|
295
|
+
// hash diverged from the true state permanently: every
|
|
296
|
+
// later snapshot-MAC check failed while the log claimed the
|
|
297
|
+
// patch had merely been "skipped". decodeSyncdPatch never
|
|
298
|
+
// mutates newState in place (the LT-hash generator copies
|
|
299
|
+
// the index map and returns fresh state), so restoring
|
|
300
|
+
// these three references in the catch is an exact rollback.
|
|
301
|
+
const prevVersion = newState.version;
|
|
302
|
+
const prevHash = newState.hash;
|
|
303
|
+
const prevIndexValueMap = newState.indexValueMap;
|
|
304
|
+
const appliedMutations = [];
|
|
289
305
|
try {
|
|
290
306
|
const { version, keyId, snapshotMac } = syncd;
|
|
291
307
|
if (syncd.externalMutations) {
|
|
@@ -300,6 +316,11 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
|
|
|
300
316
|
const decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, shouldMutate
|
|
301
317
|
? mutation => {
|
|
302
318
|
const index = mutation.syncAction.index?.toString();
|
|
319
|
+
// (HB-141 + HB-149) record for rollback: a patch
|
|
320
|
+
// whose snapshot-MAC check fails below has already
|
|
321
|
+
// delivered its mutations here — they must not
|
|
322
|
+
// survive being "skipped" either.
|
|
323
|
+
appliedMutations.push({ index, had: index in mutationMap, prev: mutationMap[index] });
|
|
303
324
|
mutationMap[index] = mutation;
|
|
304
325
|
}
|
|
305
326
|
: () => { }, true);
|
|
@@ -324,6 +345,28 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
|
|
|
324
345
|
// AunuBaileys: satu patch rusak tidak membunuh seluruh sinkronisasi
|
|
325
346
|
logger?.warn({ err, name, version: syncd.version }, 'skipping undecodable app state patch');
|
|
326
347
|
syncd.mutations = [];
|
|
348
|
+
// AunuBaileys (1.0.87, HB-141 + HB-149): roll the failed
|
|
349
|
+
// patch back completely, then stop this batch — later
|
|
350
|
+
// patches build on this one's state, and letting one of
|
|
351
|
+
// them advance the persisted version past the hole would
|
|
352
|
+
// cement the loss. The version stays at the last good
|
|
353
|
+
// patch, so the next resync re-requests the failed patch
|
|
354
|
+
// instead of reporting a version that silently skips it;
|
|
355
|
+
// a transient cause (failed external download, a sync key
|
|
356
|
+
// that had not arrived yet) now self-heals.
|
|
357
|
+
newState.version = prevVersion;
|
|
358
|
+
newState.hash = prevHash;
|
|
359
|
+
newState.indexValueMap = prevIndexValueMap;
|
|
360
|
+
for (let i = appliedMutations.length - 1; i >= 0; i--) {
|
|
361
|
+
const { index, had, prev } = appliedMutations[i];
|
|
362
|
+
if (had) {
|
|
363
|
+
mutationMap[index] = prev;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
delete mutationMap[index];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
327
370
|
}
|
|
328
371
|
}
|
|
329
372
|
return { state: newState, mutationMap };
|
|
@@ -110,7 +110,22 @@ export class USyncQuery {
|
|
|
110
110
|
if (errCode === 401 || errCode === 403 || errCode === 405) {
|
|
111
111
|
return ['isBlockedByContact', true];
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
// AunuBaileys (1.0.87, HB-175): any
|
|
114
|
+
// OTHER per-user parser error — the
|
|
115
|
+
// protocol node carried an <error>
|
|
116
|
+
// child with e.g. a 404 — used to be
|
|
117
|
+
// rethrown, aborting the parse of
|
|
118
|
+
// the ENTIRE batch: one user's error
|
|
119
|
+
// cost every other user's devices /
|
|
120
|
+
// contacts, and downstream sends that
|
|
121
|
+
// depend on the device list failed
|
|
122
|
+
// outright. Treat it the way the
|
|
123
|
+
// business parser treats an error
|
|
124
|
+
// child: this user simply has no data
|
|
125
|
+
// for this protocol (the null is
|
|
126
|
+
// dropped by the filter below); the
|
|
127
|
+
// rest of the batch continues.
|
|
128
|
+
return [protocol, null];
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
else {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aunucore/baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.87",
|
|
5
5
|
"description": "AunuBaileys — the self-healing WhatsApp Web API for Node.js. Bad MAC and \"2000 messages into the future\" errors repair themselves, LID and phone-number identities stay in sync, anti-ban is built in, pairing codes are custom — plus 29 experimental features no other Baileys fork ships: invisible watermarks, per-person tag-all cards, side-profile text, ghost mentions and more. ESM, CJS and full TypeScript types.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|