@aunucore/baileys 1.0.73 โ 1.0.75
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/README.md +2 -3
- package/lib/Signal/libsignal.js +10 -3
- package/lib/Socket/chats.js +25 -0
- package/lib/Socket/index.js +21 -1
- package/lib/Socket/messages-recv.js +4 -4
- package/lib/Types/Auth.d.ts +2 -2
- package/lib/Utils/antiban.js +22 -0
- package/lib/Utils/aunu-bug-guard.js +14 -0
- package/lib/Utils/auth-utils.js +13 -3
- package/lib/Utils/crypto.js +12 -1
- package/lib/Utils/messages-media.js +7 -1
- package/lib/Utils/messages.js +8 -0
- package/lib/Utils/noise-handler.js +9 -0
- package/lib/Utils/signal.js +9 -2
- package/lib/Utils/validate-connection.js +5 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -128,10 +128,9 @@ Everything else is the upstream core. The patch layer stays small and deliberate
|
|
|
128
128
|
|
|
129
129
|
### ๐ฐ Recent releases
|
|
130
130
|
|
|
131
|
+
- **1.0.75 โ Partial-config safety + watchdog cleanup.** A partial `transactionOpts` in the socket config can no longer kill every key-store transaction: the config is deep-merged over the defaults and the transaction layer falls back per-field (`maxCommitRetries` 10, `delayBetweenTriesMs` 3000, retries clamped to at least 1 so an explicit `0` means exactly one attempt) โ previously the shallow config spread replaced the whole default object, `maxCommitRetries` came out `undefined`, and every transaction threw `failed to commit transaction mutations` without a single commit attempt. The session watchdog's interval is now stopped when the socket ends (its `stop()` existed but was never called anywhere), closing a one-timer-and-socket-graph leak per reconnect cycle.
|
|
132
|
+
- **1.0.74 โ Fix-all batch.** The entire 1.0.74 heartbeat queue, closed in one version: `resyncAppState` can no longer spin forever holding the key-store transaction mutex (server-omitted collections finish with a warning, plus a 25-round cap), `startByte`-only media downloads no longer silently return an empty stream, an all-failed sticker pack throws Boom 400 instead of uploading empty, key-material guards now check TYPES (Buffer/string only) at six sites so wrong-typed creds fail descriptively instead of as raw TypeErrors, the noise handshake guards a missing `noiseKey` up front, the pre-login `creds.me` dereference family is closed (acks for `<unavailable>` stanzas survive fresh pairing), and two per-entity maps (`RateLimiter.hits`, bug-guard `recentBySender`) evict stale keys with a lazy sweep.
|
|
131
133
|
- **1.0.73 โ Heartbeat queue + a new README.** Three verified heartbeat fixes: a corrupt frame header can no longer wedge `decodeFrame` (zero-length headers are skipped, frames over 10 MiB fail loudly instead of buffering forever), `botRecentSecretsByChat` is capped at 1,000 chats FIFO, and bare key-store writes are serialized through the transaction mutex so a `migrateSession` commit can't overwrite a fresher session record. Plus a fully rewritten README and 46 curated npm keywords.
|
|
132
|
-
- **1.0.72 โ Send integrity.** Fixes a critical regression in 1.0.71 that made `sendMessage` relay an empty message, plus auditor-verified fixes: media-cache contamination, sent-message cache LID/PN resolution, newsletter media stanza typing, USync backoff wired with a 60s clamp, and a 30-item heartbeat sweep (HB-61โฆHB-90).
|
|
133
|
-
- **1.0.71 โ The farm batch.** Polls V6 + image polls, spoiler & group-status proto fields, A2UI widgets, the crash-payload detector, MEX username management, six USync protocols, msmsg decryption, heartbeat fixes HB-49โฆHB-59 โ every viable harvest from a 2,500-package ecosystem sweep, in one release.
|
|
134
|
-
|
|
135
134
|
Full changelog for every version since 1.0.2: **[docs](https://docsjs.aunucore.eu.cc)** ยท **[GitHub Releases](https://github.com/AunuCore/AunuBaileys-lib/releases)**.
|
|
136
135
|
|
|
137
136
|
### ๐ Credits
|
package/lib/Signal/libsignal.js
CHANGED
|
@@ -5,6 +5,10 @@ import { jidDecode } from '../WABinary/index.js';
|
|
|
5
5
|
import { SenderKeyName } from './Group/sender-key-name.js';
|
|
6
6
|
import { SenderKeyRecord } from './Group/sender-key-record.js';
|
|
7
7
|
import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group/index.js';
|
|
8
|
+
// AunuBaileys (1.0.74): HB-98 โ key material is only ever a Buffer
|
|
9
|
+
// (or a string in a hand-edited creds file). Anything else is a
|
|
10
|
+
// wrong-typed corrupt value, not key material.
|
|
11
|
+
const isKeyMaterial = (value) => Buffer.isBuffer(value) || typeof value === 'string';
|
|
8
12
|
export function makeLibSignalRepository(auth) {
|
|
9
13
|
const storage = signalStorage(auth);
|
|
10
14
|
return {
|
|
@@ -153,7 +157,10 @@ function signalStorage({ creds, keys }) {
|
|
|
153
157
|
// that peer's pkmsg forever with no self-heal match.
|
|
154
158
|
// Return undefined instead โ libsignal raises the
|
|
155
159
|
// diagnosable `PreKeyError: Invalid PreKey ID`.
|
|
156
|
-
|
|
160
|
+
// (1.0.74, HB-98: wrong-TYPED material โ e.g. a number โ
|
|
161
|
+
// slipped past the truthiness check and threw the bare
|
|
162
|
+
// TypeError from Buffer.from anyway; same treatment.)
|
|
163
|
+
if (!isKeyMaterial(key.private) || !isKeyMaterial(key.public)) {
|
|
157
164
|
return undefined;
|
|
158
165
|
}
|
|
159
166
|
return {
|
|
@@ -169,7 +176,7 @@ function signalStorage({ creds, keys }) {
|
|
|
169
176
|
// keyPair object itself โ {keyPair:{}} or {keyPair:{public}}
|
|
170
177
|
// slipped through and threw a bare TypeError on EVERY pkmsg.
|
|
171
178
|
// Check the fields, like getOurIdentity does (HB-26).
|
|
172
|
-
if (!key?.keyPair?.private || !key?.keyPair?.public) {
|
|
179
|
+
if (!isKeyMaterial(key?.keyPair?.private) || !isKeyMaterial(key?.keyPair?.public)) {
|
|
173
180
|
// AunuBaileys: a partial/corrupt creds restore (creds
|
|
174
181
|
// present, signedPreKey gone) otherwise fails EVERY
|
|
175
182
|
// pre-key message with a cryptic TypeError and never
|
|
@@ -201,7 +208,7 @@ function signalStorage({ creds, keys }) {
|
|
|
201
208
|
// throw a bare TypeError here on EVERY pkmsg, forever, with
|
|
202
209
|
// no self-heal classifier matching it. Fail descriptively
|
|
203
210
|
// so the host knows a re-pair is the fix.
|
|
204
|
-
if (!signedIdentityKey?.private || !signedIdentityKey?.public) {
|
|
211
|
+
if (!isKeyMaterial(signedIdentityKey?.private) || !isKeyMaterial(signedIdentityKey?.public)) {
|
|
205
212
|
throw new Error('Our signed identity key is missing or incomplete โ the auth state is corrupt; re-pair the device');
|
|
206
213
|
}
|
|
207
214
|
return {
|
package/lib/Socket/chats.js
CHANGED
|
@@ -11,6 +11,8 @@ import { getBinaryNodeChild, getBinaryNodeChildren, isJidUser, isLidUser, jidDec
|
|
|
11
11
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
12
12
|
import { makeUSyncSocket } from './usync.js';
|
|
13
13
|
const MAX_SYNC_ATTEMPTS = 2;
|
|
14
|
+
// AunuBaileys (1.0.74): HB-95 โ hard cap on resyncAppState rounds.
|
|
15
|
+
const MAX_RESYNC_ROUNDS = 25;
|
|
14
16
|
export const makeChatsSocket = (config) => {
|
|
15
17
|
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage } = config;
|
|
16
18
|
const sock = makeUSyncSocket(config);
|
|
@@ -373,7 +375,17 @@ export const makeChatsSocket = (config) => {
|
|
|
373
375
|
// keep executing till all collections are done
|
|
374
376
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
375
377
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
378
|
+
let resyncRounds = 0;
|
|
376
379
|
while (collectionsToHandle.size) {
|
|
380
|
+
// AunuBaileys (1.0.74): HB-95 second safety net โ even
|
|
381
|
+
// with absent collections handled below, never let this
|
|
382
|
+
// loop spin forever inside the key-store transaction:
|
|
383
|
+
// past the round cap, bail out descriptively so the
|
|
384
|
+
// transaction (and its mutex) is always released.
|
|
385
|
+
resyncRounds += 1;
|
|
386
|
+
if (resyncRounds > MAX_RESYNC_ROUNDS) {
|
|
387
|
+
throw new Boom(`resyncAppState did not converge after ${MAX_RESYNC_ROUNDS} rounds (still pending: ${[...collectionsToHandle].join(', ')})`, { statusCode: 500 });
|
|
388
|
+
}
|
|
377
389
|
const states = {};
|
|
378
390
|
const nodes = [];
|
|
379
391
|
for (const name of collectionsToHandle) {
|
|
@@ -416,6 +428,19 @@ export const makeChatsSocket = (config) => {
|
|
|
416
428
|
});
|
|
417
429
|
// extract from binary node
|
|
418
430
|
const decoded = await extractSyncdPatches(result, config?.options);
|
|
431
|
+
// AunuBaileys (1.0.74): HB-95 โ a collection the server
|
|
432
|
+
// omits from its sync response never enters `decoded`,
|
|
433
|
+
// so it was never deleted from collectionsToHandle and
|
|
434
|
+
// the loop above re-issued the identical IQ forever,
|
|
435
|
+
// holding the key-store transaction mutex the whole
|
|
436
|
+
// time. Treat an absent collection as done, with a
|
|
437
|
+
// warning.
|
|
438
|
+
for (const name of [...collectionsToHandle]) {
|
|
439
|
+
if (!(name in decoded)) {
|
|
440
|
+
logger.warn({ name }, `collection ${name} missing from app-state sync response; treating it as done`);
|
|
441
|
+
collectionsToHandle.delete(name);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
419
444
|
for (const key in decoded) {
|
|
420
445
|
const name = key;
|
|
421
446
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
package/lib/Socket/index.js
CHANGED
|
@@ -16,6 +16,15 @@ const makeWASocket = (config) => {
|
|
|
16
16
|
...DEFAULT_CONNECTION_CONFIG,
|
|
17
17
|
...config
|
|
18
18
|
};
|
|
19
|
+
// AunuBaileys (1.0.75, HB-104): transactionOpts is merged per-field โ
|
|
20
|
+
// the shallow spread above replaced the whole default object, so a
|
|
21
|
+
// host passing a PARTIAL transactionOpts silently lost the other
|
|
22
|
+
// field's default (and, before the auth-utils fallback, every
|
|
23
|
+
// key-store commit with it).
|
|
24
|
+
newConfig.transactionOpts = {
|
|
25
|
+
...DEFAULT_CONNECTION_CONFIG.transactionOpts,
|
|
26
|
+
...(config?.transactionOpts ?? {})
|
|
27
|
+
};
|
|
19
28
|
// If the user hasn't provided their own history sync function,
|
|
20
29
|
// let's create a default one that respects the syncFullHistory flag.
|
|
21
30
|
if (config.shouldSyncHistoryMessage === undefined) {
|
|
@@ -30,7 +39,18 @@ const makeWASocket = (config) => {
|
|
|
30
39
|
// config.sessionWatchdog (object = options, true = defaults).
|
|
31
40
|
// It only reports; it never reconnects.
|
|
32
41
|
if (newConfig.sessionWatchdog) {
|
|
33
|
-
|
|
42
|
+
const watchdog = attachSessionWatchdog(sock, newConfig.sessionWatchdog === true ? undefined : newConfig.sessionWatchdog);
|
|
43
|
+
sock.aunuSessionWatchdog = watchdog;
|
|
44
|
+
// AunuBaileys (1.0.75, HB-105): stop() existed but was never
|
|
45
|
+
// called anywhere in the lib โ the interval is unref'd yet its
|
|
46
|
+
// closure holds ev + sock, so every ended socket with the
|
|
47
|
+
// watchdog enabled leaked one 30s timer and its whole socket
|
|
48
|
+
// graph for the life of the process, one per reconnect cycle.
|
|
49
|
+
// Register stop() as a socket-end handler, the same pattern as
|
|
50
|
+
// stopOfflineDrain (HB-66). stop() is idempotent (safe on the
|
|
51
|
+
// double-end path), and sockets without a watchdog never reach
|
|
52
|
+
// this branch.
|
|
53
|
+
sock.registerSocketEndHandler?.(() => watchdog.stop());
|
|
34
54
|
}
|
|
35
55
|
sock.aunuHandler = new AunuHandler(sock);
|
|
36
56
|
attachAunuAutoJoin(sock, newConfig);
|
|
@@ -74,7 +74,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
74
74
|
stanza.attrs.type = attrs.type;
|
|
75
75
|
}
|
|
76
76
|
if (tag === 'message' && getBinaryNodeChild({ tag, attrs, content }, 'unavailable')) {
|
|
77
|
-
stanza.attrs.from = authState.creds.me
|
|
77
|
+
stanza.attrs.from = authState.creds.me?.id;
|
|
78
78
|
}
|
|
79
79
|
logger.debug({ recv: { tag, attrs }, sent: stanza.attrs }, 'sent ack');
|
|
80
80
|
await sendNode(stanza);
|
|
@@ -407,7 +407,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
407
407
|
break;
|
|
408
408
|
case 'devices': {
|
|
409
409
|
const devices = getBinaryNodeChildren(child, 'device');
|
|
410
|
-
if (areJidsSameUser(child.attrs.jid, authState.creds.me
|
|
410
|
+
if (areJidsSameUser(child.attrs.jid, authState.creds.me?.id)) {
|
|
411
411
|
const deviceJids = devices.map(d => d.attrs.jid);
|
|
412
412
|
logger.info({ deviceJids }, 'got my own devices');
|
|
413
413
|
}
|
|
@@ -773,7 +773,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
773
773
|
processingMutex.mutex(async () => {
|
|
774
774
|
const msg = await processNotification(node);
|
|
775
775
|
if (msg) {
|
|
776
|
-
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me
|
|
776
|
+
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me?.id);
|
|
777
777
|
msg.key = {
|
|
778
778
|
remoteJid,
|
|
779
779
|
fromMe,
|
|
@@ -824,7 +824,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
824
824
|
let response;
|
|
825
825
|
if (getBinaryNodeChild(node, 'unavailable') && !encNode) {
|
|
826
826
|
await sendMessageAck(node);
|
|
827
|
-
const { key } = decodeMessageNode(node, authState.creds.me
|
|
827
|
+
const { key } = decodeMessageNode(node, authState.creds.me?.id, authState.creds.me?.lid || '').fullMessage;
|
|
828
828
|
// AunuBaileys: bot / hosted / view_once placeholders can never
|
|
829
829
|
// be resent by the phone โ don't burn a resend request on
|
|
830
830
|
// them (behaviour learned from @violetix/baileys).
|
package/lib/Types/Auth.d.ts
CHANGED
|
@@ -89,8 +89,8 @@ export type SignalKeyStoreWithTransaction = SignalKeyStore & {
|
|
|
89
89
|
transaction<T>(exec: () => Promise<T>): Promise<T>;
|
|
90
90
|
};
|
|
91
91
|
export type TransactionCapabilityOptions = {
|
|
92
|
-
maxCommitRetries
|
|
93
|
-
delayBetweenTriesMs
|
|
92
|
+
maxCommitRetries?: number;
|
|
93
|
+
delayBetweenTriesMs?: number;
|
|
94
94
|
};
|
|
95
95
|
export type SignalAuthState = {
|
|
96
96
|
creds: SignalCreds;
|
package/lib/Utils/antiban.js
CHANGED
|
@@ -28,9 +28,28 @@ export class RateLimiter {
|
|
|
28
28
|
this.windowMs = windowMs;
|
|
29
29
|
this.hits = new Map();
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* AunuBaileys (1.0.74): HB-103 โ acquire/tryAcquire prune a key's
|
|
33
|
+
* old timestamps but never deleted the KEY, so one entry per
|
|
34
|
+
* recipient JID accumulated for the life of the socket. Lazy
|
|
35
|
+
* sweep, run only once the map passes the threshold: evict every
|
|
36
|
+
* key whose newest hit is already outside its rate-limit window
|
|
37
|
+
* (such a key would behave as brand-new anyway โ no behaviour
|
|
38
|
+
* change, amortised cost).
|
|
39
|
+
*/
|
|
40
|
+
sweepStaleKeys(now = Date.now()) {
|
|
41
|
+
for (const [key, arr] of this.hits) {
|
|
42
|
+
if (!arr.length || now - arr[arr.length - 1] >= this.windowMs) {
|
|
43
|
+
this.hits.delete(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
/** Tunggu sampai ada slot untuk key; kembalikan waktu tunggu (ms). */
|
|
32
48
|
async acquire(key) {
|
|
33
49
|
const now = Date.now();
|
|
50
|
+
if (this.hits.size > 10000) {
|
|
51
|
+
this.sweepStaleKeys(now);
|
|
52
|
+
}
|
|
34
53
|
let arr = this.hits.get(key);
|
|
35
54
|
if (!arr) {
|
|
36
55
|
arr = [];
|
|
@@ -49,6 +68,9 @@ export class RateLimiter {
|
|
|
49
68
|
/** Cek tanpa menunggu; true bila masih boleh. */
|
|
50
69
|
tryAcquire(key) {
|
|
51
70
|
const now = Date.now();
|
|
71
|
+
if (this.hits.size > 10000) {
|
|
72
|
+
this.sweepStaleKeys(now);
|
|
73
|
+
}
|
|
52
74
|
let arr = this.hits.get(key);
|
|
53
75
|
if (!arr) {
|
|
54
76
|
arr = [];
|
|
@@ -303,6 +303,20 @@ export const attachAunuBugGuard = (sock, options = {}) => {
|
|
|
303
303
|
const stamps = (recentBySender.get(sender) || []).filter((t) => now - t < burstWindowMs);
|
|
304
304
|
stamps.push(now);
|
|
305
305
|
recentBySender.set(sender, stamps);
|
|
306
|
+
// AunuBaileys (1.0.74): HB-103 โ stale senders were never
|
|
307
|
+
// evicted from this map (their stamp lists got filtered, the
|
|
308
|
+
// keys stayed), so it grew by one entry per distinct flagged
|
|
309
|
+
// sender for the life of the process. Lazy sweep, only past
|
|
310
|
+
// the threshold: drop senders whose newest detection is
|
|
311
|
+
// already outside the burst window.
|
|
312
|
+
if (recentBySender.size > 1000) {
|
|
313
|
+
for (const [other, otherStamps] of recentBySender) {
|
|
314
|
+
const newest = otherStamps[otherStamps.length - 1];
|
|
315
|
+
if (newest === undefined || now - newest >= burstWindowMs) {
|
|
316
|
+
recentBySender.delete(other);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
306
320
|
return stamps.length >= burstThreshold;
|
|
307
321
|
};
|
|
308
322
|
const handleBug = async (message, result, where) => {
|
package/lib/Utils/auth-utils.js
CHANGED
|
@@ -87,7 +87,17 @@ export function makeCacheableSignalKeyStore(store, logger, _cache) {
|
|
|
87
87
|
* @param logger logger to log events
|
|
88
88
|
* @returns SignalKeyStore with transaction capability
|
|
89
89
|
*/
|
|
90
|
-
export const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetweenTriesMs }) => {
|
|
90
|
+
export const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetweenTriesMs } = {}) => {
|
|
91
|
+
// AunuBaileys (1.0.75, HB-104): a partial transactionOpts from the
|
|
92
|
+
// host (makeWASocket's shallow spread used to replace the whole
|
|
93
|
+
// default object) left maxCommitRetries undefined, so the commit
|
|
94
|
+
// loop below never ran even once and EVERY mutating transaction
|
|
95
|
+
// threw 'failed to commit transaction mutations' without a single
|
|
96
|
+
// commit attempt. Fall back per-field to the Defaults values, and
|
|
97
|
+
// clamp retries to at least 1 so an explicit 0 means exactly one
|
|
98
|
+
// attempt, matching host intent.
|
|
99
|
+
const commitRetries = Math.max(1, maxCommitRetries ?? 10);
|
|
100
|
+
const commitDelayMs = delayBetweenTriesMs ?? 3000;
|
|
91
101
|
// number of queries made to the DB during the transaction
|
|
92
102
|
// only there for logging purposes
|
|
93
103
|
let dbQueriesInTransaction = 0;
|
|
@@ -116,7 +126,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
|
|
|
116
126
|
logger.trace('committing transaction');
|
|
117
127
|
// retry mechanism to ensure we've some recovery
|
|
118
128
|
// in case a transaction fails in the first attempt
|
|
119
|
-
let tries =
|
|
129
|
+
let tries = commitRetries;
|
|
120
130
|
let committed = false;
|
|
121
131
|
let lastCommitError;
|
|
122
132
|
while (tries) {
|
|
@@ -131,7 +141,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
|
|
|
131
141
|
catch (error) {
|
|
132
142
|
lastCommitError = error;
|
|
133
143
|
logger.warn(`failed to commit ${Object.keys(mutations).length} mutations, tries left=${tries}`);
|
|
134
|
-
await delay(
|
|
144
|
+
await delay(commitDelayMs);
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
147
|
if (!committed) {
|
package/lib/Utils/crypto.js
CHANGED
|
@@ -5,7 +5,18 @@ import { KEY_BUNDLE_TYPE } from '../Defaults/index.js';
|
|
|
5
5
|
// insure browser & node compatibility
|
|
6
6
|
const { subtle } = globalThis.crypto;
|
|
7
7
|
/** prefix version byte to the pub keys, required for some curve crypto functions */
|
|
8
|
-
export const generateSignalPubKey = (pubKey) =>
|
|
8
|
+
export const generateSignalPubKey = (pubKey) => {
|
|
9
|
+
// AunuBaileys (1.0.74): HB-98 โ a wrong-typed public key (e.g. a
|
|
10
|
+
// number from a hand-edited creds file, via getOurIdentity) used
|
|
11
|
+
// to explode as a bare TypeError inside Buffer.concat. Key
|
|
12
|
+
// material is a Buffer (or a string in an edited creds file);
|
|
13
|
+
// anything else is corrupt โ fail descriptively.
|
|
14
|
+
if (!Buffer.isBuffer(pubKey) && typeof pubKey !== 'string') {
|
|
15
|
+
throw new Error('AunuBaileys: invalid public key material โ the auth state is incomplete or corrupt; restore a full backup or pair again');
|
|
16
|
+
}
|
|
17
|
+
const buf = typeof pubKey === 'string' ? Buffer.from(pubKey) : pubKey;
|
|
18
|
+
return buf.length === 33 ? buf : Buffer.concat([KEY_BUNDLE_TYPE, buf]);
|
|
19
|
+
};
|
|
9
20
|
export const Curve = {
|
|
10
21
|
generateKeyPair: () => {
|
|
11
22
|
const { pubKey, privKey } = libsignal.curve.generateKeyPair();
|
|
@@ -664,7 +664,13 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
664
664
|
const pushBytes = (bytes, push) => {
|
|
665
665
|
if (startByte || endByte) {
|
|
666
666
|
const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0);
|
|
667
|
-
|
|
667
|
+
// AunuBaileys (1.0.74): HB-96 โ with a startByte but NO
|
|
668
|
+
// endByte, `endByte - bytesFetched` was NaN and every chunk
|
|
669
|
+
// sliced to zero bytes (a silent empty download). No
|
|
670
|
+
// endByte means "to the end of the stream": end stays
|
|
671
|
+
// undefined. (When endByte IS set, an end past this chunk
|
|
672
|
+
// clamps inside slice() exactly like undefined did.)
|
|
673
|
+
const end = endByte == null ? undefined : Math.max(endByte - bytesFetched, 0);
|
|
668
674
|
push(bytes.slice(start, end));
|
|
669
675
|
bytesFetched += bytes.length;
|
|
670
676
|
}
|
package/lib/Utils/messages.js
CHANGED
|
@@ -453,6 +453,14 @@ async function prepareStickerPackMessage(stickerPack, options) {
|
|
|
453
453
|
}
|
|
454
454
|
});
|
|
455
455
|
const stickerMetadata = (await Promise.all(stickerPromises)).filter(Boolean);
|
|
456
|
+
// AunuBaileys (1.0.74): HB-97 โ the input guard above only checks
|
|
457
|
+
// the REQUESTED stickers; if every one of them failed (404, too
|
|
458
|
+
// big, unconvertible) the pack used to upload happily with
|
|
459
|
+
// `stickers: []` and report success. An all-failed pack is an
|
|
460
|
+
// error; a partially-failed pack still succeeds.
|
|
461
|
+
if (stickerMetadata.length === 0) {
|
|
462
|
+
throw new Boom('All stickers in the pack failed to process โ refusing to upload an empty sticker pack', { statusCode: 400 });
|
|
463
|
+
}
|
|
456
464
|
// Process and add cover/tray icon to the ZIP
|
|
457
465
|
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
458
466
|
const { stream: coverStream } = await getStream(cover);
|
|
@@ -85,6 +85,15 @@ export const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publi
|
|
|
85
85
|
mixIntoKey,
|
|
86
86
|
finishInit,
|
|
87
87
|
processHandshake: async ({ serverHello }, noiseKey) => {
|
|
88
|
+
// AunuBaileys (1.0.74): HB-102 โ a partial creds restore can
|
|
89
|
+
// reach the handshake with noiseKey missing; dereferencing
|
|
90
|
+
// it below threw a bare TypeError BEFORE the descriptive
|
|
91
|
+
// HB-80 guard in generateRegistrationNode could run, and
|
|
92
|
+
// the host reconnect-looped on it at every boot. Fail
|
|
93
|
+
// descriptively, in the HB-80 style.
|
|
94
|
+
if (!noiseKey?.public || !noiseKey?.private) {
|
|
95
|
+
throw new Error('AunuBaileys: creds.noiseKey is missing or incomplete โ the auth state is incomplete or corrupt; restore a full backup or pair again');
|
|
96
|
+
}
|
|
88
97
|
authenticate(serverHello.ephemeral);
|
|
89
98
|
await mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral));
|
|
90
99
|
const decStaticContent = decrypt(serverHello.static);
|
package/lib/Utils/signal.js
CHANGED
|
@@ -2,6 +2,10 @@ import { KEY_BUNDLE_TYPE } from '../Defaults/index.js';
|
|
|
2
2
|
import { assertNodeErrorFree, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildUInt, jidDecode, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
3
3
|
import { Curve, generateSignalPubKey } from './crypto.js';
|
|
4
4
|
import { encodeBigEndian } from './generics.js';
|
|
5
|
+
// AunuBaileys (1.0.74): HB-98 โ key material is only ever a Buffer
|
|
6
|
+
// (or a string in a hand-edited creds file). Anything else is a
|
|
7
|
+
// wrong-typed corrupt value, not key material.
|
|
8
|
+
const isKeyMaterial = (value) => Buffer.isBuffer(value) || typeof value === 'string';
|
|
5
9
|
function chunk(array, size) {
|
|
6
10
|
const chunks = [];
|
|
7
11
|
for (let i = 0; i < array.length; i += size) {
|
|
@@ -204,9 +208,12 @@ export const getNextPreKeys = async ({ creds, keys }, count) => {
|
|
|
204
208
|
// ({} โ valid JSON, no key material) used to be uploaded as an
|
|
205
209
|
// EMPTY <value> (HB-83); the decrypt path has guarded this since
|
|
206
210
|
// 1.0.71 (loadPreKey). Skip entries without a public key here too.
|
|
211
|
+
// (1.0.74, HB-98: truthiness alone also let a wrong-TYPED public โ
|
|
212
|
+
// e.g. a number โ through to the node encoder, which threw
|
|
213
|
+
// "invalid children". Only Buffer/string material counts.)
|
|
207
214
|
const preKeys = {};
|
|
208
215
|
for (const [preKeyId, pair] of Object.entries(storedPreKeys)) {
|
|
209
|
-
if (pair?.public) {
|
|
216
|
+
if (isKeyMaterial(pair?.public)) {
|
|
210
217
|
preKeys[preKeyId] = pair;
|
|
211
218
|
}
|
|
212
219
|
}
|
|
@@ -218,7 +225,7 @@ export const getNextPreKeysNode = async (state, count) => {
|
|
|
218
225
|
// below โ partial creds turned that into a bare TypeError and a
|
|
219
226
|
// reconnect loop at every boot (HB-68). Fail descriptively, like
|
|
220
227
|
// loadSignedPreKey (HB-26/54).
|
|
221
|
-
if (!creds.signedIdentityKey?.public || !creds.signedPreKey?.keyPair?.public || typeof creds.signedPreKey?.keyId === 'undefined') {
|
|
228
|
+
if (!isKeyMaterial(creds.signedIdentityKey?.public) || !isKeyMaterial(creds.signedPreKey?.keyPair?.public) || !isKeyMaterial(creds.signedPreKey?.signature) || typeof creds.signedPreKey?.keyId === 'undefined') {
|
|
222
229
|
throw new Error('AunuBaileys: creds.signedIdentityKey / creds.signedPreKey are missing or incomplete โ the auth state is incomplete or corrupt; restore a full backup or pair again');
|
|
223
230
|
}
|
|
224
231
|
const { update, preKeys } = await getNextPreKeys(state, count);
|
|
@@ -6,6 +6,10 @@ import { getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary/index
|
|
|
6
6
|
import { Curve, hmacSign } from './crypto.js';
|
|
7
7
|
import { encodeBigEndian } from './generics.js';
|
|
8
8
|
import { createSignalIdentity } from './signal.js';
|
|
9
|
+
// AunuBaileys (1.0.74): HB-98 โ key material is only ever a Buffer
|
|
10
|
+
// (or a string in a hand-edited creds file). Anything else is a
|
|
11
|
+
// wrong-typed corrupt value, not key material.
|
|
12
|
+
const isKeyMaterial = (value) => Buffer.isBuffer(value) || typeof value === 'string';
|
|
9
13
|
const getUserAgent = (config) => {
|
|
10
14
|
// AunuBaileys (1.0.70): a host overriding version with undefined
|
|
11
15
|
// used to throw a raw TypeError here; fall back to the bundled
|
|
@@ -76,7 +80,7 @@ export const generateRegistrationNode = ({ registrationId, signedPreKey, signedI
|
|
|
76
80
|
// below โ partial creds turned that into a bare TypeError deep in
|
|
77
81
|
// the pairing flow (HB-80). Fail descriptively, like
|
|
78
82
|
// loadSignedPreKey (HB-26/54).
|
|
79
|
-
if (!signedIdentityKey?.public || !signedPreKey?.keyPair?.public || typeof signedPreKey?.keyId === 'undefined' || !signedPreKey?.signature) {
|
|
83
|
+
if (!isKeyMaterial(signedIdentityKey?.public) || !isKeyMaterial(signedPreKey?.keyPair?.public) || typeof signedPreKey?.keyId === 'undefined' || !isKeyMaterial(signedPreKey?.signature)) {
|
|
80
84
|
throw new Error('AunuBaileys: creds.signedIdentityKey / creds.signedPreKey are missing or incomplete โ the auth state is incomplete or corrupt; restore a full backup or pair again');
|
|
81
85
|
}
|
|
82
86
|
// the app version needs to be md5 hashed
|
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.75",
|
|
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",
|
|
@@ -69,17 +69,17 @@
|
|
|
69
69
|
"preinstall": "node ./engine-requirements.js"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
+
"@adiwajshing/keyed-db": "^0.2.4",
|
|
72
73
|
"@cacheable/node-cache": "^1.4.0",
|
|
73
74
|
"@hapi/boom": "^9.1.3",
|
|
74
75
|
"async-mutex": "^0.5.0",
|
|
75
76
|
"axios": "^1.6.0",
|
|
77
|
+
"fflate": "^0.8.2",
|
|
76
78
|
"libsignal": "git+https://github.com/whiskeysockets/libsignal-node",
|
|
77
79
|
"music-metadata": "^11.7.0",
|
|
78
80
|
"pino": "^9.6",
|
|
79
81
|
"protobufjs": "^7.2.4",
|
|
80
|
-
"ws": "^8.13.0"
|
|
81
|
-
"@adiwajshing/keyed-db": "^0.2.4",
|
|
82
|
-
"fflate": "^0.8.2"
|
|
82
|
+
"ws": "^8.13.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@eslint/eslintrc": "^3.3.1",
|
|
@@ -112,11 +112,11 @@
|
|
|
112
112
|
"typescript": "^5.8.2"
|
|
113
113
|
},
|
|
114
114
|
"peerDependencies": {
|
|
115
|
+
"@napi-rs/image": "^1.12.0",
|
|
115
116
|
"audio-decode": "^2.1.3",
|
|
116
117
|
"jimp": "^1.6.0",
|
|
117
118
|
"link-preview-js": "^3.0.0",
|
|
118
|
-
"sharp": "*"
|
|
119
|
-
"@napi-rs/image": "^1.12.0"
|
|
119
|
+
"sharp": "*"
|
|
120
120
|
},
|
|
121
121
|
"peerDependenciesMeta": {
|
|
122
122
|
"audio-decode": {
|