aunubaileys 1.0.33 → 1.0.35
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 +8 -0
- package/lib/Socket/messages-recv.js +13 -0
- package/lib/Types/Events.d.ts +15 -0
- package/lib/Utils/decode-wa-message.js +22 -2
- package/lib/Utils/messages.d.ts +3 -2
- package/lib/Utils/messages.js +20 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,14 @@ 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.35 / Baru di 1.0.35
|
|
68
|
+
|
|
69
|
+
- 📡 **Decrypt failures now emit a structured signal instead of a log flood** — one rotten session can fail hundreds of messages in minutes (B-01), and every failure was logged at ERROR level, drowning the logs while the bot had no programmatic way to know a Bad MAC flood was happening. Now: (1) the decrypt error log is throttled per sender address — first line immediately, then at most one line per minute per sender carrying the suppressed-failure count; (2) a new `messages.decrypt-failure` event carries the chat, sender, signal address, consecutive failure count, and whether this failure tripped the session/sender-key reset — consumers can alert and aggregate without parsing logs. The failure handling itself is unchanged. Closing release of the 1.0.14–1.0.35 fix train.
|
|
70
|
+
|
|
71
|
+
### 🆕 What's new in 1.0.34 / Baru di 1.0.34
|
|
72
|
+
|
|
73
|
+
- 🗳️ **One person no longer counts as two poll voters across the LID/PN split** — poll aggregation keyed voters by their raw message-key identity, so someone voting first as a phone number and later as a LID (or the reverse) counted as two different voters, and a poll update arriving in the other identity form piled up as a second entry instead of replacing the old one. `getAggregateVotesInPollMessage` and `updateMessageWithPollUpdate` now accept an optional `{ lidStore }` option: voters are canonicalized to their phone-number JID and deduped per option. Without a store, legacy behaviour is unchanged.
|
|
74
|
+
|
|
67
75
|
### 🆕 What's new in 1.0.33 / Baru di 1.0.33
|
|
68
76
|
|
|
69
77
|
- 💓 **One lost keep-alive pong no longer kills the connection** — upstream ended the connection as soon as the gap since last inbound traffic exceeded one interval + 5s; on a quiet connection the ping response is the only inbound traffic, so a single lost pong killed a healthy connection and forced a full reconnect. The connection is now ended only after two consecutive intervals without a reply; genuinely dead connections are still detected, one interval later.
|
|
@@ -751,6 +751,19 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
751
751
|
else {
|
|
752
752
|
decryptFailCounts.set(failKey, fails);
|
|
753
753
|
}
|
|
754
|
+
// AunuBaileys: structured decrypt-failure event so
|
|
755
|
+
// consumers can alert and aggregate on Bad MAC
|
|
756
|
+
// floods (B-01) instead of parsing log lines.
|
|
757
|
+
ev.emit('messages.decrypt-failure', {
|
|
758
|
+
remoteJid: msg.key.remoteJid,
|
|
759
|
+
author,
|
|
760
|
+
address: addr,
|
|
761
|
+
isGroup: isGroupMsg,
|
|
762
|
+
failures: fails,
|
|
763
|
+
healed: fails >= DECRYPT_FAIL_SESSION_RESET_LIMIT,
|
|
764
|
+
messageId: node.attrs.id,
|
|
765
|
+
timestamp: Date.now()
|
|
766
|
+
});
|
|
754
767
|
}
|
|
755
768
|
catch (healErr) {
|
|
756
769
|
logger.debug({ err: healErr }, 'aunu: session self-heal step failed, continuing with normal retry');
|
package/lib/Types/Events.d.ts
CHANGED
|
@@ -43,6 +43,21 @@ export type BaileysEventMap = {
|
|
|
43
43
|
};
|
|
44
44
|
'contacts.upsert': Contact[];
|
|
45
45
|
'contacts.update': Partial<Contact>[];
|
|
46
|
+
'messages.decrypt-failure': {
|
|
47
|
+
/** the chat the failed message arrived in */
|
|
48
|
+
remoteJid: string;
|
|
49
|
+
/** sender JID (participant in groups) */
|
|
50
|
+
author: string;
|
|
51
|
+
/** signal-protocol address the failure was counted against */
|
|
52
|
+
address: string;
|
|
53
|
+
isGroup: boolean;
|
|
54
|
+
/** consecutive failures for this address, including this one */
|
|
55
|
+
failures: number;
|
|
56
|
+
/** true when this failure tripped the session/sender-key reset */
|
|
57
|
+
healed: boolean;
|
|
58
|
+
messageId: string;
|
|
59
|
+
timestamp: number;
|
|
60
|
+
};
|
|
46
61
|
'messages.delete': {
|
|
47
62
|
keys: WAMessageKey[];
|
|
48
63
|
} | {
|
|
@@ -109,6 +109,26 @@ export function decodeMessageNode(stanza, meId, meLid) {
|
|
|
109
109
|
sender: msgType === 'chat' ? author : chatId
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
+
// AunuBaileys: per-address throttle for the per-message decrypt error log.
|
|
113
|
+
// One rotten session can fail hundreds of messages in minutes (B-01: every
|
|
114
|
+
// failure from a single LID address flooded the logs at ERROR level); the
|
|
115
|
+
// first failure per address logs immediately, then at most one line per
|
|
116
|
+
// minute, carrying how many failures were suppressed in between. Nothing
|
|
117
|
+
// about the failure handling itself changes — only the log volume.
|
|
118
|
+
const DECRYPT_ERROR_LOG_INTERVAL_MS = 60_000;
|
|
119
|
+
const decryptErrorLogState = new Map();
|
|
120
|
+
const logDecryptErrorThrottled = (logger, address, key, err) => {
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
const state = decryptErrorLogState.get(address) || { lastLoggedAt: 0, suppressed: 0 };
|
|
123
|
+
if (now - state.lastLoggedAt >= DECRYPT_ERROR_LOG_INTERVAL_MS) {
|
|
124
|
+
logger.error({ key, err, suppressedFailures: state.suppressed }, 'failed to decrypt message');
|
|
125
|
+
decryptErrorLogState.set(address, { lastLoggedAt: now, suppressed: 0 });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
decryptErrorLogState.set(address, { ...state, suppressed: state.suppressed + 1 });
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
112
132
|
export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
|
|
113
133
|
const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid);
|
|
114
134
|
return {
|
|
@@ -171,7 +191,7 @@ export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
|
|
|
171
191
|
});
|
|
172
192
|
}
|
|
173
193
|
catch (err) {
|
|
174
|
-
logger
|
|
194
|
+
logDecryptErrorThrottled(logger, author, fullMessage.key, err);
|
|
175
195
|
}
|
|
176
196
|
}
|
|
177
197
|
if (fullMessage.message) {
|
|
@@ -182,7 +202,7 @@ export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
|
|
|
182
202
|
}
|
|
183
203
|
}
|
|
184
204
|
catch (err) {
|
|
185
|
-
logger
|
|
205
|
+
logDecryptErrorThrottled(logger, author, fullMessage.key, err);
|
|
186
206
|
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT;
|
|
187
207
|
fullMessage.messageStubParameters = [err.message];
|
|
188
208
|
}
|
package/lib/Utils/messages.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare const updateMessageWithReceipt: (msg: Pick<WAMessage, "userReceip
|
|
|
44
44
|
/** Update the message with a new reaction */
|
|
45
45
|
export declare const updateMessageWithReaction: (msg: Pick<WAMessage, "reactions">, reaction: proto.IReaction) => void;
|
|
46
46
|
/** Update the message with a new poll update */
|
|
47
|
-
export declare const updateMessageWithPollUpdate: (msg: Pick<WAMessage, "pollUpdates">, update: proto.IPollUpdate) => void;
|
|
47
|
+
export declare const updateMessageWithPollUpdate: (msg: Pick<WAMessage, "pollUpdates">, update: proto.IPollUpdate, options?: PollLIDOptions) => void;
|
|
48
48
|
type VoteAggregation = {
|
|
49
49
|
name: string;
|
|
50
50
|
voters: string[];
|
|
@@ -55,7 +55,8 @@ type VoteAggregation = {
|
|
|
55
55
|
* @param meId your jid
|
|
56
56
|
* @returns A list of options & their voters
|
|
57
57
|
*/
|
|
58
|
-
export
|
|
58
|
+
export interface PollLIDOptions { lidStore?: { getPNForLID(jid: string): string | undefined }; }
|
|
59
|
+
export declare function getAggregateVotesInPollMessage({ message, pollUpdates }: Pick<WAMessage, 'pollUpdates' | 'message'>, meId?: string, options?: PollLIDOptions): VoteAggregation[];
|
|
59
60
|
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
60
61
|
export declare const aggregateMessageKeysNotFromMe: (keys: proto.IMessageKey[]) => {
|
|
61
62
|
jid: string;
|
package/lib/Utils/messages.js
CHANGED
|
@@ -1008,9 +1008,16 @@ export const updateMessageWithReaction = (msg, reaction) => {
|
|
|
1008
1008
|
msg.reactions = reactions;
|
|
1009
1009
|
};
|
|
1010
1010
|
/** Update the message with a new poll update */
|
|
1011
|
-
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
1012
|
-
|
|
1013
|
-
|
|
1011
|
+
export const updateMessageWithPollUpdate = (msg, update, options) => {
|
|
1012
|
+
// AunuBaileys: normalize the voter through the LID store when one is
|
|
1013
|
+
// given, so a vote arriving in the other identity form REPLACES the
|
|
1014
|
+
// voter's earlier update instead of piling up as a second voter.
|
|
1015
|
+
const resolveVoter = (key) => {
|
|
1016
|
+
const author = getKeyAuthor(key);
|
|
1017
|
+
return options?.lidStore?.getPNForLID?.(author) || author;
|
|
1018
|
+
};
|
|
1019
|
+
const authorID = resolveVoter(update.pollUpdateMessageKey);
|
|
1020
|
+
const reactions = (msg.pollUpdates || []).filter(r => resolveVoter(r.pollUpdateMessageKey) !== authorID);
|
|
1014
1021
|
if (update.vote?.selectedOptions?.length) {
|
|
1015
1022
|
reactions.push(update);
|
|
1016
1023
|
}
|
|
@@ -1022,7 +1029,7 @@ export const updateMessageWithPollUpdate = (msg, update) => {
|
|
|
1022
1029
|
* @param meId your jid
|
|
1023
1030
|
* @returns A list of options & their voters
|
|
1024
1031
|
*/
|
|
1025
|
-
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
1032
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId, options) {
|
|
1026
1033
|
const opts = message?.pollCreationMessage?.options ||
|
|
1027
1034
|
message?.pollCreationMessageV2?.options ||
|
|
1028
1035
|
message?.pollCreationMessageV3?.options ||
|
|
@@ -1050,7 +1057,15 @@ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
1050
1057
|
};
|
|
1051
1058
|
data = voteHashMap[hash];
|
|
1052
1059
|
}
|
|
1053
|
-
|
|
1060
|
+
// AunuBaileys: voters are canonicalized to their phone-number
|
|
1061
|
+
// JID through the LID store when one is given, and deduped per
|
|
1062
|
+
// option — without this, one person voting first as PN and
|
|
1063
|
+
// later as LID counted as two different voters.
|
|
1064
|
+
const voter = getKeyAuthor(update.pollUpdateMessageKey, meId);
|
|
1065
|
+
const canonicalVoter = options?.lidStore?.getPNForLID?.(voter) || voter;
|
|
1066
|
+
if (!data.voters.includes(canonicalVoter)) {
|
|
1067
|
+
data.voters.push(canonicalVoter);
|
|
1068
|
+
}
|
|
1054
1069
|
}
|
|
1055
1070
|
}
|
|
1056
1071
|
return Object.values(voteHashMap);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aunubaileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.35",
|
|
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",
|