aunubaileys 1.0.45 β†’ 1.0.46

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 CHANGED
@@ -64,6 +64,10 @@ Everything else is pure upstream. When upstream moves, we rebase the patch layer
64
64
 
65
65
 
66
66
 
67
+ ### πŸ†• What's new in 1.0.46 / Baru di 1.0.46
68
+
69
+ - 🩹 **The "Over 2000 messages into the future" flood now heals itself.** Session-layer decrypt failures β€” libsignal's aggregate "No matching sessions found for message" / "No session record" / "No sessions available", which includes the unfillable counter gap left by regressed session state (e.g. sessions restored from a backup while the sender kept sending; observed live in the Tenka deployment on 11 Nov 2026, on the same LID address as the old B-01 finding) β€” are now recognised from `messageStubParameters` and trigger an **immediate E2E session reset** instead of waiting for 3 failures: for DMs and groups alike (groups also drop the author's sender key), including the session record under its alternate PN↔LID address form, with the retry receipt carrying fresh keys. The old self-heal missed this flood because it waited 3 strikes, in groups only touched the sender key while the broken layer is the E2E session, and deletion could miss the aliased record. Other error classes (ordinary Bad MAC) keep the exact 1.0.14/1.0.17 3-strike behaviour. New exported classifier: `isUnrecoverableSessionError(text)`; the `messages.decrypt-failure` event gains an `errorClass` field (`'session'`/`'other'`) and `healed` now reflects a reset that actually happened on that message.
70
+
67
71
  ### πŸ†• What's new in 1.0.45 / Baru di 1.0.45
68
72
 
69
73
  - πŸ“ž **VOIP call support (from plogme, original implementation)** β€” one combined release from an 8-package sweep (raiden-baileys, @neoxr/wb, @zervonex/baileys, @itsliaaa/starseal, alya-baileys, @ryuu-reinzz/baileys, @cogitator-ai/channels, plogme β€” all audited clean). New socket methods: `offerCall(jid, isVideo?)` and `groupCall(groupJid, participants, isVideo?)` build the `<offer>` with a per-device encrypted call-key envelope, `cancelGroupCall(groupJid, callId)`, and `acceptCall(callId, callFrom)` / `terminateCall(callId, callFrom, reason?)` join the existing `rejectCall`. Pure stanza builders are exported from `lib/Utils/call-utils.js` so the wire shapes are unit-testable. The stanza shapes follow the WhatsApp call protocol; the code is an original AunuBaileys implementation (plogme's license forbids copying its code). This is the signalling layer β€” two-way VOIP media is out of scope. plogme's voip-rekey decoder was **not** ported: the 6.7.24 proto has no `E2eRekeyPayload`.
@@ -5,7 +5,7 @@ import Long from 'long';
5
5
  import { proto } from '../../WAProto/index.js';
6
6
  import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaults/index.js';
7
7
  import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
- import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, buildCallControlStanza, parseRetryReceiptSession, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
8
+ import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, isUnrecoverableSessionError, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, buildCallControlStanza, parseRetryReceiptSession, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
9
9
  import { makeMutex } from '../Utils/make-mutex.js';
10
10
  import { sentMessageCacheFor } from '../Utils/sent-message-cache.js';
11
11
  import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
@@ -758,17 +758,47 @@ export const makeMessagesRecvSocket = (config) => {
758
758
  const addr = signalRepository.jidToSignalProtocolAddress(author);
759
759
  const failKey = isGroupMsg ? `sk:${msg.key.remoteJid}:${addr}` : addr;
760
760
  const fails = (decryptFailCounts.get(failKey) || 0) + 1;
761
- if (fails >= DECRYPT_FAIL_SESSION_RESET_LIMIT) {
761
+ // AunuBaileys (1.0.46): kegagalan lapis sesi β€” record
762
+ // sesi tersimpan tidak dapat mendekripsi pesan ini
763
+ // sama sekali (termasuk jurang "Over 2000 messages
764
+ // into the future" akibat state sesi yang mundur).
765
+ // Jurang itu tidak bisa diisi ulang, jadi menunggu
766
+ // 3x hanya membanjiri log; dan di grup, menghapus
767
+ // sender key saja tidak menyentuh sesi E2E yang
768
+ // rusak. Kelas ini direset SEKETIKA.
769
+ const sessionLayerFailure = isUnrecoverableSessionError(msg?.messageStubParameters?.[0]);
770
+ let healed = false;
771
+ const resetE2ESession = async () => {
772
+ await signalRepository.deleteSession(author);
773
+ // sesi yang sama bisa tersimpan di bawah bentuk
774
+ // alamat alternatifnya (PN <-> LID)
775
+ const alt = sock.aunuLid?.getAltJid?.(author);
776
+ if (alt && alt !== author) {
777
+ await signalRepository.deleteSession(alt);
778
+ }
779
+ };
780
+ if (sessionLayerFailure) {
781
+ await resetE2ESession();
782
+ if (isGroupMsg) {
783
+ await signalRepository.deleteSenderKey(msg.key.remoteJid, author);
784
+ }
785
+ decryptFailCounts.delete(failKey);
786
+ forceIncludeKeys = true;
787
+ healed = true;
788
+ logger.warn({ author, addr, group: isGroupMsg ? msg.key.remoteJid : undefined }, 'aunu: reset E2E session after an unrecoverable session-layer decrypt failure (stored session cannot decrypt, e.g. regressed state); retry receipt carries fresh keys');
789
+ }
790
+ else if (fails >= DECRYPT_FAIL_SESSION_RESET_LIMIT) {
762
791
  if (isGroupMsg) {
763
792
  await signalRepository.deleteSenderKey(msg.key.remoteJid, author);
764
793
  logger.warn({ author, group: msg.key.remoteJid }, 'aunu: deleted stale sender-key record after repeated group decrypt failures; retry request asks the author to resend with a fresh distribution message');
765
794
  }
766
795
  else {
767
- await signalRepository.deleteSession(author);
796
+ await resetE2ESession();
768
797
  logger.warn({ author, addr }, 'aunu: deleted stale E2E session after repeated decrypt failures (Bad MAC self-heal); retry receipt carries fresh keys');
769
798
  }
770
799
  decryptFailCounts.delete(failKey);
771
800
  forceIncludeKeys = true;
801
+ healed = true;
772
802
  }
773
803
  else {
774
804
  decryptFailCounts.set(failKey, fails);
@@ -782,7 +812,8 @@ export const makeMessagesRecvSocket = (config) => {
782
812
  address: addr,
783
813
  isGroup: isGroupMsg,
784
814
  failures: fails,
785
- healed: fails >= DECRYPT_FAIL_SESSION_RESET_LIMIT,
815
+ healed,
816
+ errorClass: sessionLayerFailure ? 'session' : 'other',
786
817
  messageId: node.attrs.id,
787
818
  timestamp: Date.now()
788
819
  });
@@ -2,6 +2,7 @@ import type { SignalRepository, WAMessage } from '../Types/index.js';
2
2
  import { type BinaryNode } from '../WABinary/index.js';
3
3
  import type { ILogger } from './logger.js';
4
4
  export declare const NO_MESSAGE_FOUND_ERROR_TEXT = "Message absent from node";
5
+ export declare const isUnrecoverableSessionError: (text: unknown) => boolean;
5
6
  export declare const MISSING_KEYS_ERROR_TEXT = "Key used already or never filled";
6
7
  export declare const NACK_REASONS: {
7
8
  ParsingError: number;
@@ -216,4 +216,15 @@ export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
216
216
  }
217
217
  };
218
218
  };
219
+ /**
220
+ * AunuBaileys: true when a decrypt failure text is libsignal's session-layer
221
+ * aggregate β€” the stored session record(s) could not decrypt the message at
222
+ * all ("No matching sessions found for message", "No session record",
223
+ * "No sessions available"). This class includes the unfillable counter gap
224
+ * ("Over 2000 messages into the future!") thrown when the stored session
225
+ * state has regressed behind the sender's ratchet, e.g. after a backup
226
+ * restore; such a session can never decrypt again and must be reset.
227
+ */
228
+ export const isUnrecoverableSessionError = (text) => typeof text === 'string' &&
229
+ /no matching sessions found|no session record|no sessions available/i.test(text);
219
230
  //# sourceMappingURL=decode-wa-message.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aunubaileys",
3
3
  "type": "module",
4
- "version": "1.0.45",
4
+ "version": "1.0.46",
5
5
  "description": "AunuBaileys \u2014 a WhatsApp Web API for bots that never sleep. Full upstream power with the beloved in-memory store restored and pairing that just works. Forged by AunuCore.",
6
6
  "keywords": [
7
7
  "whatsapp",