@pontalabs/baileys 1.0.11 → 1.1.0
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 +96 -85
- package/lib/Defaults/connection.js +39 -0
- package/lib/Defaults/constants.js +50 -0
- package/lib/Defaults/history.js +13 -0
- package/lib/Defaults/index.js +36 -151
- package/lib/Defaults/media.js +43 -0
- package/lib/Defaults/phonenumber-mcc.json +221 -221
- package/lib/Defaults/prefix.js +12 -0
- package/lib/Defaults/vialeys-version.json +7 -0
- package/lib/Signal/Group/ciphertext-message.js +10 -15
- package/lib/Signal/Group/group-session-builder.js +34 -60
- package/lib/Signal/Group/group_cipher.js +69 -76
- package/lib/Signal/Group/index.js +41 -60
- package/lib/Signal/Group/keyhelper.js +12 -57
- package/lib/Signal/Group/sender-chain-key.js +20 -33
- package/lib/Signal/Group/sender-key-distribution-message.js +43 -48
- package/lib/Signal/Group/sender-key-message.js +58 -53
- package/lib/Signal/Group/sender-key-name.js +21 -31
- package/lib/Signal/Group/sender-key-record.js +21 -34
- package/lib/Signal/Group/sender-key-state.js +59 -106
- package/lib/Signal/Group/sender-message-key.js +16 -21
- package/lib/Signal/libsignal.js +282 -203
- package/lib/Signal/lid-mapping.js +164 -184
- package/lib/Socket/Client/index.js +31 -22
- package/lib/Socket/Client/types.js +9 -15
- package/lib/Socket/Client/websocket.js +42 -37
- package/lib/Socket/business.js +212 -336
- package/lib/Socket/chats.js +696 -940
- package/lib/Socket/community.js +454 -369
- package/lib/Socket/groups.js +278 -280
- package/lib/Socket/index.js +9 -21
- package/lib/Socket/messages-recv.js +1027 -1570
- package/lib/Socket/messages-send.js +1180 -1136
- package/lib/Socket/mex.js +59 -0
- package/lib/Socket/newsletter.js +193 -251
- package/lib/Socket/socket.js +758 -898
- package/lib/Store/index.js +35 -24
- package/lib/Store/make-cache-manager-store.js +53 -66
- package/lib/Store/make-in-memory-store.js +319 -325
- package/lib/Store/make-ordered-dictionary.js +49 -57
- package/lib/Store/object-repository.js +10 -15
- package/lib/Types/Auth.js +2 -3
- package/lib/Types/Bussines.js +2 -3
- package/lib/Types/Call.js +2 -3
- package/lib/Types/Chat.js +10 -9
- package/lib/Types/Contact.js +2 -3
- package/lib/Types/Events.js +2 -3
- package/lib/Types/GroupMetadata.js +2 -3
- package/lib/Types/Label.js +6 -12
- package/lib/Types/LabelAssociation.js +4 -13
- package/lib/Types/Message.js +10 -18
- package/lib/Types/MexUpdates.js +15 -18
- package/lib/Types/Newsletter.js +26 -34
- package/lib/Types/Product.js +2 -3
- package/lib/Types/Signal.js +2 -3
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +8 -13
- package/lib/Types/USync.js +2 -3
- package/lib/Types/index.js +49 -40
- package/lib/Utils/auth-utils.js +190 -495
- package/lib/Utils/browser-utils.js +29 -0
- package/lib/Utils/business.js +177 -199
- package/lib/Utils/chat-utils.js +711 -624
- package/lib/Utils/crypto.js +116 -161
- package/lib/Utils/decode-wa-message.js +225 -321
- package/lib/Utils/event-buffer.js +363 -405
- package/lib/Utils/generics.js +318 -472
- package/lib/Utils/history.js +72 -76
- package/lib/Utils/index.js +49 -41
- package/lib/Utils/link-preview.js +59 -103
- package/lib/Utils/logger.js +4 -7
- package/lib/Utils/lt-hash.js +29 -42
- package/lib/Utils/make-mutex.js +26 -42
- package/lib/Utils/message-retry-manager.js +134 -160
- package/lib/Utils/messages-media.js +665 -731
- package/lib/Utils/messages.js +518 -1
- package/lib/Utils/noise-handler.js +127 -125
- package/lib/Utils/pre-key-manager.js +86 -0
- package/lib/Utils/process-message.js +739 -351
- package/lib/Utils/signal.js +146 -129
- package/lib/Utils/use-multi-file-auth-state.js +68 -135
- package/lib/Utils/validate-connection.js +183 -142
- package/lib/Utils/vialeys-event-stream.js +41 -0
- package/lib/WABinary/constants.js +1281 -1289
- package/lib/WABinary/decode.js +145 -200
- package/lib/WABinary/encode.js +149 -202
- package/lib/WABinary/generic-utils.js +79 -97
- package/lib/WABinary/index.js +36 -25
- package/lib/WABinary/jid-utils.js +91 -123
- package/lib/WABinary/types.js +2 -3
- package/lib/WAM/BinaryInfo.js +8 -13
- package/lib/WAM/constants.js +16584 -11464
- package/lib/WAM/encode.js +117 -133
- package/lib/WAM/index.js +34 -23
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +62 -69
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +11 -25
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +32 -44
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +13 -23
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +24 -38
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +17 -31
- package/lib/WAUSync/Protocols/index.js +37 -26
- package/lib/WAUSync/USyncQuery.js +66 -66
- package/lib/WAUSync/USyncUser.js +13 -18
- package/lib/WAUSync/index.js +34 -23
- package/lib/index.js +95 -28
- package/package.json +2 -1
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.js +0 -83
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.js +0 -70
- package/lib/Utils/signal-housekeeping.d.ts +0 -19
- package/lib/Utils/signal-housekeeping.js +0 -73
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const { Boom: Boom } = require("@hapi/boom");
|
|
4
|
+
const { proto: proto } = require("../../WAProto");
|
|
5
|
+
const {
|
|
6
|
+
areJidsSameUser: areJidsSameUser,
|
|
7
|
+
isHostedLidUser: isHostedLidUser,
|
|
8
|
+
isHostedPnUser: isHostedPnUser,
|
|
9
|
+
isJidBroadcast: isJidBroadcast,
|
|
10
|
+
isJidGroup: isJidGroup,
|
|
11
|
+
isJidMetaAI: isJidMetaAI,
|
|
12
|
+
isJidNewsletter: isJidNewsletter,
|
|
13
|
+
isJidStatusBroadcast: isJidStatusBroadcast,
|
|
14
|
+
isLidUser: isLidUser,
|
|
15
|
+
isPnUser: isPnUser,
|
|
16
|
+
} = require("../WABinary");
|
|
17
|
+
const { unpadRandomMax16: unpadRandomMax16 } = require("./generics");
|
|
18
|
+
const { getDevice: getDevice } = require("./messages");
|
|
19
|
+
const NO_MESSAGE_FOUND_ERROR_TEXT = "Message absent from node";
|
|
20
|
+
const MISSING_KEYS_ERROR_TEXT = "Key used already or never filled";
|
|
15
21
|
const DECRYPTION_RETRY_CONFIG = {
|
|
16
22
|
maxRetries: 3,
|
|
17
23
|
baseDelayMs: 100,
|
|
18
|
-
sessionRecordErrors: [
|
|
19
|
-
}
|
|
20
|
-
|
|
24
|
+
sessionRecordErrors: ["No session record", "SessionError: No session record"],
|
|
25
|
+
};
|
|
21
26
|
const NACK_REASONS = {
|
|
22
27
|
ParsingError: 487,
|
|
23
28
|
UnrecognizedStanza: 488,
|
|
@@ -31,371 +36,270 @@ const NACK_REASONS = {
|
|
|
31
36
|
UnhandledError: 500,
|
|
32
37
|
UnsupportedAdminRevoke: 550,
|
|
33
38
|
UnsupportedLIDGroup: 551,
|
|
34
|
-
DBOperationFailed: 552
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
DBOperationFailed: 552,
|
|
40
|
+
};
|
|
41
|
+
const extractAddressingContext = (stanza) => {
|
|
42
|
+
let senderAlt;
|
|
43
|
+
let recipientAlt;
|
|
44
|
+
const sender = stanza.attrs.participant || stanza.attrs.from;
|
|
45
|
+
const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith("lid") ? "lid" : "pn");
|
|
46
|
+
if (addressingMode === "lid") {
|
|
47
|
+
senderAlt =
|
|
48
|
+
stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn;
|
|
49
|
+
recipientAlt = stanza.attrs.recipient_pn;
|
|
50
|
+
} else {
|
|
51
|
+
senderAlt =
|
|
52
|
+
stanza.attrs.participant_lid ||
|
|
53
|
+
stanza.attrs.sender_lid ||
|
|
54
|
+
stanza.attrs.peer_recipient_lid;
|
|
55
|
+
recipientAlt = stanza.attrs.recipient_lid;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
addressingMode: addressingMode,
|
|
59
|
+
senderAlt: senderAlt,
|
|
60
|
+
recipientAlt: recipientAlt,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const getDecryptionJid = async (sender, repository) => {
|
|
64
|
+
if (isLidUser(sender) || isHostedLidUser(sender)) {
|
|
65
|
+
return sender;
|
|
66
|
+
}
|
|
67
|
+
const mapped = await repository.lidMapping.getLIDForPN(sender);
|
|
68
|
+
return mapped || sender;
|
|
69
|
+
};
|
|
70
|
+
const storeMappingFromEnvelope = async (stanza, sender, repository, decryptionJid, logger) => {
|
|
71
|
+
const { senderAlt: senderAlt } = extractAddressingContext(stanza);
|
|
72
|
+
if (senderAlt && isLidUser(senderAlt) && isPnUser(sender) && decryptionJid === sender) {
|
|
73
|
+
try {
|
|
74
|
+
await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }]);
|
|
75
|
+
await repository.migrateSession(sender, senderAlt);
|
|
76
|
+
logger.debug(
|
|
77
|
+
{ sender: sender, senderAlt: senderAlt },
|
|
78
|
+
"Stored LID mapping from envelope"
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.warn(
|
|
82
|
+
{ sender: sender, senderAlt: senderAlt, error: error },
|
|
83
|
+
"Failed to store LID mapping"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
43
88
|
function decodeMessageNode(stanza, meId, meLid) {
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
let author
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const recipient = stanza.attrs.recipient
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
89
|
+
let msgType;
|
|
90
|
+
let chatId;
|
|
91
|
+
let author;
|
|
92
|
+
let fromMe = false;
|
|
93
|
+
const msgId = stanza.attrs.id;
|
|
94
|
+
const from = stanza.attrs.from;
|
|
95
|
+
const participant = stanza.attrs.participant;
|
|
96
|
+
const recipient = stanza.attrs.recipient;
|
|
97
|
+
const addressingContext = extractAddressingContext(stanza);
|
|
98
|
+
const isMe = (jid) => areJidsSameUser(jid, meId);
|
|
99
|
+
const isMeLid = (jid) => areJidsSameUser(jid, meLid);
|
|
100
|
+
if (isPnUser(from) || isLidUser(from) || isHostedLidUser(from) || isHostedPnUser(from)) {
|
|
101
|
+
if (recipient && !isJidMetaAI(recipient)) {
|
|
102
|
+
if (!isMe(from) && !isMeLid(from)) {
|
|
103
|
+
throw new Boom("receipient present, but msg not from me", {
|
|
104
|
+
data: stanza,
|
|
105
|
+
});
|
|
58
106
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
else {
|
|
62
|
-
chat_id = from
|
|
63
|
-
}
|
|
64
|
-
msg_type = 'chat'
|
|
65
|
-
author = from
|
|
66
|
-
} else if (WABinary_1.isLidUser(from)) {
|
|
67
|
-
if (recipient) {
|
|
68
|
-
if (!isMeLid(from)) {
|
|
69
|
-
throw new boom_1.Boom('receipient present, but msg not from me', { data: stanza })
|
|
107
|
+
if (isMe(from) || isMeLid(from)) {
|
|
108
|
+
fromMe = true;
|
|
70
109
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: recipient
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
// FIX: chat_id dari LID → resolve ke JID via sender_pn
|
|
78
|
-
chat_id = stanza.attrs.sender_pn
|
|
79
|
-
? WABinary_1.jidNormalizedUser(stanza.attrs.sender_pn)
|
|
80
|
-
: stanza.attrs.peer_recipient_pn
|
|
81
|
-
? WABinary_1.jidNormalizedUser(stanza.attrs.peer_recipient_pn)
|
|
82
|
-
: from
|
|
110
|
+
chatId = recipient;
|
|
111
|
+
} else {
|
|
112
|
+
chatId = from;
|
|
83
113
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
author = stanza.attrs.sender_pn
|
|
88
|
-
|| stanza.attrs.participant_pn
|
|
89
|
-
|| stanza.attrs.peer_recipient_pn
|
|
90
|
-
|| from
|
|
91
|
-
} else if (WABinary_1.isJidGroup(from)) {
|
|
114
|
+
msgType = "chat";
|
|
115
|
+
author = from;
|
|
116
|
+
} else if (isJidGroup(from)) {
|
|
92
117
|
if (!participant) {
|
|
93
|
-
throw new
|
|
118
|
+
throw new Boom("No participant in group message");
|
|
94
119
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
if (isMe(participant) || isMeLid(participant)) {
|
|
121
|
+
fromMe = true;
|
|
122
|
+
}
|
|
123
|
+
msgType = "group";
|
|
124
|
+
author = participant;
|
|
125
|
+
chatId = from;
|
|
126
|
+
} else if (isJidBroadcast(from)) {
|
|
99
127
|
if (!participant) {
|
|
100
|
-
throw new
|
|
128
|
+
throw new Boom("No participant in group message");
|
|
101
129
|
}
|
|
102
|
-
const isParticipantMe = isMe(participant)
|
|
103
|
-
if (
|
|
104
|
-
|
|
130
|
+
const isParticipantMe = isMe(participant);
|
|
131
|
+
if (isJidStatusBroadcast(from)) {
|
|
132
|
+
msgType = isParticipantMe ? "direct_peer_status" : "other_status";
|
|
133
|
+
} else {
|
|
134
|
+
msgType = isParticipantMe ? "peer_broadcast" : "other_broadcast";
|
|
105
135
|
}
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
fromMe = isParticipantMe;
|
|
137
|
+
chatId = from;
|
|
138
|
+
author = participant;
|
|
139
|
+
} else if (isJidNewsletter(from)) {
|
|
140
|
+
msgType = "newsletter";
|
|
141
|
+
chatId = from;
|
|
142
|
+
author = from;
|
|
143
|
+
if (isMe(from) || isMeLid(from)) {
|
|
144
|
+
fromMe = true;
|
|
108
145
|
}
|
|
109
|
-
chat_id = from
|
|
110
|
-
author = participant
|
|
111
|
-
} else if (WABinary_1.isJidNewsletter(from)) {
|
|
112
|
-
msg_type = 'newsletter'
|
|
113
|
-
chat_id = from
|
|
114
|
-
author = from
|
|
115
146
|
} else {
|
|
116
|
-
throw new
|
|
147
|
+
throw new Boom("Unknown message type", { data: stanza });
|
|
117
148
|
}
|
|
118
|
-
|
|
119
|
-
// sehingga fallback ke stanza.attrs.from tidak diperlukan dan bisa menyebabkan
|
|
120
|
-
// fromMe=true saat pesan dari orang lain di kondisi edge case LID.
|
|
121
|
-
// Kita batasi fallback hanya untuk group (di mana participant memang bisa undefined).
|
|
122
|
-
const fromMe = WABinary_1.isJidNewsletter(from)
|
|
123
|
-
? !!stanza.attrs?.is_sender
|
|
124
|
-
: WABinary_1.isLidUser(from)
|
|
125
|
-
? isMeLid(participant != null ? participant : stanza.attrs.from)
|
|
126
|
-
: (WABinary_1.isJidGroup(from) || WABinary_1.isJidBroadcast(from))
|
|
127
|
-
? isMe(participant != null ? participant : stanza.attrs.from)
|
|
128
|
-
: isMe(from)
|
|
129
|
-
const pushname = stanza?.attrs?.notify
|
|
149
|
+
const pushname = stanza?.attrs?.notify;
|
|
130
150
|
const key = {
|
|
131
|
-
remoteJid:
|
|
132
|
-
|
|
151
|
+
remoteJid: chatId,
|
|
152
|
+
remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
|
|
153
|
+
fromMe: fromMe,
|
|
133
154
|
id: msgId,
|
|
134
|
-
participant:
|
|
135
|
-
|
|
136
|
-
|
|
155
|
+
participant: participant,
|
|
156
|
+
participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
|
|
157
|
+
addressingMode: addressingContext.addressingMode,
|
|
158
|
+
...(msgType === "newsletter" && stanza.attrs.server_id
|
|
159
|
+
? { server_id: stanza.attrs.server_id }
|
|
160
|
+
: {}),
|
|
161
|
+
};
|
|
137
162
|
const fullMessage = {
|
|
138
|
-
key,
|
|
163
|
+
key: key,
|
|
164
|
+
category: stanza.attrs.category,
|
|
139
165
|
messageTimestamp: +stanza.attrs.t,
|
|
140
166
|
pushName: pushname,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
fullMessage.newsletter_server_id = +stanza.attrs?.server_id
|
|
146
|
-
}
|
|
167
|
+
platform: getDevice(key.id),
|
|
168
|
+
broadcast: isJidBroadcast(from),
|
|
169
|
+
newsletter: isJidNewsletter(from),
|
|
170
|
+
};
|
|
147
171
|
if (key.fromMe) {
|
|
148
|
-
fullMessage.status =
|
|
149
|
-
}
|
|
150
|
-
if (!key.fromMe) {
|
|
151
|
-
fullMessage.platform = messages_1.getDevice(key.id)
|
|
172
|
+
fullMessage.status = proto.WebMessageInfo.Status.SERVER_ACK;
|
|
152
173
|
}
|
|
153
174
|
return {
|
|
154
|
-
fullMessage,
|
|
155
|
-
author,
|
|
156
|
-
sender:
|
|
157
|
-
}
|
|
175
|
+
fullMessage: fullMessage,
|
|
176
|
+
author: author,
|
|
177
|
+
sender: msgType === "chat" ? author : chatId,
|
|
178
|
+
};
|
|
158
179
|
}
|
|
159
|
-
|
|
160
|
-
const getDecryptionJid = async (sender, repository) => {
|
|
161
|
-
if (WABinary_1.isLidUser(sender)) {
|
|
162
|
-
return sender
|
|
163
|
-
}
|
|
164
|
-
const mapped = await repository.lidMapping.getLIDForPN(sender)
|
|
165
|
-
if (!mapped) {
|
|
166
|
-
return sender
|
|
167
|
-
}
|
|
168
|
-
// FIX: pontalabs gak punya migrateSession (beda dari levvleys) yang
|
|
169
|
-
// mindahin sesi Signal dari address PN ke address LID begitu mapping
|
|
170
|
-
// disimpan. Jadi mapping doang gak jamin ada sesi beneran di address LID
|
|
171
|
-
// itu -- kalau dipaksa pakai, decrypt gagal "No session record" walau
|
|
172
|
-
// pesannya normal (msg, bukan pkmsg). Ini yang bikin pesan self-chat/PN
|
|
173
|
-
// ilang total: begitu mapping ke-cache, semua decrypt berikutnya diarahin
|
|
174
|
-
// ke LID yang sesinya gak pernah ada. Makanya sebelum pakai LID, pastikan
|
|
175
|
-
// dulu ada sesi aktif di situ; kalau enggak, tetep pakai address asli (PN).
|
|
176
|
-
try {
|
|
177
|
-
const lidSession = await repository.validateSession(mapped)
|
|
178
|
-
if (lidSession.exists) {
|
|
179
|
-
return mapped
|
|
180
|
-
}
|
|
181
|
-
} catch {
|
|
182
|
-
// biarin jatuh ke fallback sender di bawah kalau validateSession error
|
|
183
|
-
}
|
|
184
|
-
return sender
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// FIX: pontalabs sebelumnya gak pernah menyimpan mapping LID<->PN saat pesan
|
|
188
|
-
// masuk (levvleys punya storeMappingFromEnvelope, di sini gak ada). Akibatnya
|
|
189
|
-
// untuk kontak yang pakai addressing LID, getDecryptionJid bisa gagal nemuin
|
|
190
|
-
// sesi yang benar secara intermiten (tergantung cache mapping ada atau belum),
|
|
191
|
-
// bikin decrypt gagal -> pesan jadi CIPHERTEXT -> gak pernah nyampe ke bot.
|
|
192
|
-
const extractSenderAlt = (stanza) => {
|
|
193
|
-
const sender = stanza.attrs.participant || stanza.attrs.from
|
|
194
|
-
const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith('lid') ? 'lid' : 'pn')
|
|
195
|
-
if (addressingMode === 'lid') {
|
|
196
|
-
return stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
|
|
197
|
-
}
|
|
198
|
-
return stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const storeMappingFromEnvelope = async (stanza, sender, repository, decryptionJid, logger) => {
|
|
202
|
-
const senderAlt = extractSenderAlt(stanza)
|
|
203
|
-
if (senderAlt && WABinary_1.isLidUser(senderAlt) && WABinary_1.isJidUser(sender) && decryptionJid === sender) {
|
|
204
|
-
try {
|
|
205
|
-
await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }])
|
|
206
|
-
if (typeof repository.migrateSession === 'function') {
|
|
207
|
-
await repository.migrateSession(sender, senderAlt)
|
|
208
|
-
}
|
|
209
|
-
logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope')
|
|
210
|
-
} catch (error) {
|
|
211
|
-
logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping')
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
180
|
const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
|
|
217
|
-
const {
|
|
181
|
+
const {
|
|
182
|
+
fullMessage: fullMessage,
|
|
183
|
+
author: author,
|
|
184
|
+
sender: sender,
|
|
185
|
+
} = decodeMessageNode(stanza, meId, meLid);
|
|
218
186
|
return {
|
|
219
|
-
fullMessage,
|
|
187
|
+
fullMessage: fullMessage,
|
|
220
188
|
category: stanza.attrs.category,
|
|
221
|
-
author,
|
|
189
|
+
author: author,
|
|
222
190
|
async decrypt() {
|
|
223
|
-
let decryptables = 0
|
|
191
|
+
let decryptables = 0;
|
|
224
192
|
if (Array.isArray(stanza.content)) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// array stanza.content dari server TIDAK dijamin naruh pkmsg/msg duluan.
|
|
231
|
-
// Kalau skmsg kebetulan diproses duluan, sender-key belum ada di store ->
|
|
232
|
-
// decryptGroupMessage() gagal "No SenderKeyRecord found" -> pesan pertama
|
|
233
|
-
// dari member baru selalu gagal decrypt & jadi stub CIPHERTEXT, padahal
|
|
234
|
-
// SKDM-nya baru "sampai" sepersekian detik kemudian di iterasi berikutnya.
|
|
235
|
-
// Reorder di sini (stable, gak ubah urutan relatif node lain seperti
|
|
236
|
-
// verified_name/unavailable) supaya skmsg selalu diproses PALING TERAKHIR,
|
|
237
|
-
// setelah semua kemungkinan SKDM sudah didaftarkan ke sender-key store.
|
|
238
|
-
const orderedContent = [
|
|
239
|
-
...stanza.content.filter(({ tag, attrs }) => !(tag === 'enc' && attrs.type === 'skmsg')),
|
|
240
|
-
...stanza.content.filter(({ tag, attrs }) => tag === 'enc' && attrs.type === 'skmsg')
|
|
241
|
-
]
|
|
242
|
-
for (const { tag, attrs, content } of orderedContent) {
|
|
243
|
-
if (tag === 'verified_name' && content instanceof Uint8Array) {
|
|
244
|
-
const cert = WAProto_1.proto.VerifiedNameCertificate.decode(content)
|
|
245
|
-
const details = WAProto_1.proto.VerifiedNameCertificate.Details.decode(cert.details)
|
|
246
|
-
fullMessage.verifiedBizName = details.verifiedName
|
|
193
|
+
for (const { tag: tag, attrs: attrs, content: content } of stanza.content) {
|
|
194
|
+
if (tag === "verified_name" && content instanceof Uint8Array) {
|
|
195
|
+
const cert = proto.VerifiedNameCertificate.decode(content);
|
|
196
|
+
const details = proto.VerifiedNameCertificate.Details.decode(cert.details);
|
|
197
|
+
fullMessage.verifiedBizName = details.verifiedName;
|
|
247
198
|
}
|
|
248
|
-
if (tag ===
|
|
199
|
+
if (tag === "unavailable" && attrs.type === "view_once") {
|
|
249
200
|
fullMessage.key.isViewOnce = true;
|
|
250
201
|
}
|
|
251
|
-
if (attrs.count && tag ===
|
|
252
|
-
fullMessage.retryCount = Number(attrs.count)
|
|
202
|
+
if (attrs.count && tag === "enc") {
|
|
203
|
+
fullMessage.retryCount = Number(attrs.count);
|
|
253
204
|
}
|
|
254
|
-
if (tag !==
|
|
255
|
-
continue
|
|
205
|
+
if (tag !== "enc" && tag !== "plaintext") {
|
|
206
|
+
continue;
|
|
256
207
|
}
|
|
257
208
|
if (!(content instanceof Uint8Array)) {
|
|
258
|
-
continue
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
decryptables += 1;
|
|
212
|
+
let msgBuffer;
|
|
213
|
+
const decryptionJid = await getDecryptionJid(author, repository);
|
|
214
|
+
if (tag !== "plaintext") {
|
|
215
|
+
await storeMappingFromEnvelope(
|
|
216
|
+
stanza,
|
|
217
|
+
author,
|
|
218
|
+
repository,
|
|
219
|
+
decryptionJid,
|
|
220
|
+
logger
|
|
221
|
+
);
|
|
259
222
|
}
|
|
260
|
-
decryptables += 1
|
|
261
|
-
let msgBuffer
|
|
262
223
|
try {
|
|
263
|
-
const e2eType = tag ===
|
|
224
|
+
const e2eType = tag === "plaintext" ? "plaintext" : attrs.type;
|
|
264
225
|
switch (e2eType) {
|
|
265
|
-
case
|
|
226
|
+
case "skmsg":
|
|
266
227
|
msgBuffer = await repository.decryptGroupMessage({
|
|
267
228
|
group: sender,
|
|
268
229
|
authorJid: author,
|
|
269
|
-
msg: content
|
|
270
|
-
})
|
|
271
|
-
break
|
|
272
|
-
case
|
|
273
|
-
case
|
|
274
|
-
const decryptionJid = await getDecryptionJid(author, repository)
|
|
275
|
-
await storeMappingFromEnvelope(stanza, author, repository, decryptionJid, logger)
|
|
230
|
+
msg: content,
|
|
231
|
+
});
|
|
232
|
+
break;
|
|
233
|
+
case "pkmsg":
|
|
234
|
+
case "msg":
|
|
276
235
|
msgBuffer = await repository.decryptMessage({
|
|
277
236
|
jid: decryptionJid,
|
|
278
237
|
type: e2eType,
|
|
279
|
-
ciphertext: content
|
|
280
|
-
})
|
|
281
|
-
break
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
break
|
|
238
|
+
ciphertext: content,
|
|
239
|
+
});
|
|
240
|
+
break;
|
|
241
|
+
case "plaintext":
|
|
242
|
+
msgBuffer = content;
|
|
243
|
+
break;
|
|
286
244
|
default:
|
|
287
|
-
throw new Error(`Unknown e2e type: ${e2eType}`)
|
|
245
|
+
throw new Error(`Unknown e2e type: ${e2eType}`);
|
|
288
246
|
}
|
|
289
|
-
let msg =
|
|
290
|
-
|
|
247
|
+
let msg = proto.Message.decode(
|
|
248
|
+
e2eType !== "plaintext" ? unpadRandomMax16(msgBuffer) : msgBuffer
|
|
249
|
+
);
|
|
250
|
+
msg = msg.deviceSentMessage?.message || msg;
|
|
291
251
|
if (msg.senderKeyDistributionMessage) {
|
|
292
252
|
try {
|
|
293
253
|
await repository.processSenderKeyDistributionMessage({
|
|
294
254
|
authorJid: author,
|
|
295
|
-
item: msg.senderKeyDistributionMessage
|
|
296
|
-
})
|
|
255
|
+
item: msg.senderKeyDistributionMessage,
|
|
256
|
+
});
|
|
297
257
|
} catch (err) {
|
|
298
|
-
logger.error(
|
|
258
|
+
logger.error(
|
|
259
|
+
{ key: fullMessage.key, err: err },
|
|
260
|
+
"failed to process sender key distribution message"
|
|
261
|
+
);
|
|
299
262
|
}
|
|
300
263
|
}
|
|
301
264
|
if (fullMessage.message) {
|
|
302
|
-
Object.assign(fullMessage.message, msg)
|
|
265
|
+
Object.assign(fullMessage.message, msg);
|
|
303
266
|
} else {
|
|
304
|
-
fullMessage.message = msg
|
|
305
|
-
}
|
|
306
|
-
if (WABinary_1.isJidGroup(sender)) {
|
|
307
|
-
const mtype = getTypeMessage(fullMessage.message)
|
|
308
|
-
const message = fullMessage.message?.[mtype]
|
|
309
|
-
const contextInfo = message?.contextInfo
|
|
310
|
-
const mentionedJid = contextInfo?.mentionedJid
|
|
311
|
-
const processMessageLIDs = async (msgObj, msgType) => {
|
|
312
|
-
if (!msgObj?.contextInfo) return false
|
|
313
|
-
const contextInfo = msgObj.contextInfo
|
|
314
|
-
const mentionedJid = contextInfo.mentionedJid
|
|
315
|
-
let needsUpdate = false
|
|
316
|
-
if (Array.isArray(mentionedJid) && mentionedJid.length > 0) {
|
|
317
|
-
const metadata = await global.groupMetadataCache(sender)
|
|
318
|
-
const updatedMentionedJid = []
|
|
319
|
-
for (const mentionedLid of mentionedJid) {
|
|
320
|
-
if (typeof mentionedLid === "string" && mentionedLid.endsWith('@lid')) {
|
|
321
|
-
const found = metadata.participants.find(p => p.lid === mentionedLid)
|
|
322
|
-
if (found) {
|
|
323
|
-
updatedMentionedJid.push(found.id)
|
|
324
|
-
if (msgObj.text) {
|
|
325
|
-
msgObj.text = msgObj.text.replace(new RegExp(`@${mentionedLid.split("@")[0]}`, "g"), `@${found.id.split("@")[0]}`);
|
|
326
|
-
} else if (msgObj.caption) {
|
|
327
|
-
msgObj.caption = msgObj.caption.replace(new RegExp(`@${mentionedLid.split("@")[0]}`, "g"), `@${found.id.split("@")[0]}`);
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
const lid = WABinary_1.jidNormalizedUser(mentionedLid)
|
|
331
|
-
const mentioned_jid = await repository.lidMapping.getPNForLID(mentionedLid)
|
|
332
|
-
if (mentioned_jid) {
|
|
333
|
-
const pn = WABinary_1.jidNormalizedUser(mentioned_jid)
|
|
334
|
-
await repository.lidMapping.storeLIDPNMappings([{ lid, pn }])
|
|
335
|
-
updatedMentionedJid.push(pn)
|
|
336
|
-
if (msgObj.text) {
|
|
337
|
-
msgObj.text = msgObj.text.replace(new RegExp(`@${mentionedLid.split("@")[0]}`, "g"), `@${pn.split("@")[0]}`);
|
|
338
|
-
} else if (msgObj.caption) {
|
|
339
|
-
msgObj.caption = msgObj.caption.replace(new RegExp(`@${mentionedLid.split("@")[0]}`, "g"), `@${pn.split("@")[0]}`);
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
updatedMentionedJid.push(mentionedLid)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
needsUpdate = true
|
|
346
|
-
} else {
|
|
347
|
-
updatedMentionedJid.push(mentionedLid)
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (needsUpdate) {
|
|
351
|
-
msgObj.contextInfo.mentionedJid = updatedMentionedJid
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return needsUpdate
|
|
355
|
-
}
|
|
356
|
-
const quotedMessage = contextInfo?.quotedMessage
|
|
357
|
-
if (quotedMessage) {
|
|
358
|
-
const quotedMtype = getTypeMessage(quotedMessage)
|
|
359
|
-
const quotedMsgObj = quotedMessage[quotedMtype]
|
|
360
|
-
if (quotedMsgObj?.contextInfo) {
|
|
361
|
-
await processMessageLIDs(quotedMsgObj, quotedMtype)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
267
|
+
fullMessage.message = msg;
|
|
364
268
|
}
|
|
365
269
|
} catch (err) {
|
|
366
270
|
const errorContext = {
|
|
367
271
|
key: fullMessage.key,
|
|
368
|
-
err,
|
|
369
|
-
messageType: tag ===
|
|
370
|
-
sender,
|
|
371
|
-
author,
|
|
372
|
-
isSessionRecordError: isSessionRecordError(err)
|
|
373
|
-
}
|
|
374
|
-
logger.error(errorContext,
|
|
375
|
-
fullMessage.messageStubType =
|
|
376
|
-
fullMessage.messageStubParameters = [err.message.toString()]
|
|
272
|
+
err: err,
|
|
273
|
+
messageType: tag === "plaintext" ? "plaintext" : attrs.type,
|
|
274
|
+
sender: sender,
|
|
275
|
+
author: author,
|
|
276
|
+
isSessionRecordError: isSessionRecordError(err),
|
|
277
|
+
};
|
|
278
|
+
logger.error(errorContext, "failed to decrypt message");
|
|
279
|
+
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT;
|
|
280
|
+
fullMessage.messageStubParameters = [err.message.toString()];
|
|
377
281
|
}
|
|
378
282
|
}
|
|
379
283
|
}
|
|
380
|
-
|
|
381
284
|
if (!decryptables && !fullMessage.key?.isViewOnce) {
|
|
382
|
-
fullMessage.messageStubType =
|
|
383
|
-
fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
|
|
285
|
+
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT;
|
|
286
|
+
fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT];
|
|
384
287
|
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
};
|
|
389
291
|
function isSessionRecordError(error) {
|
|
390
|
-
const errorMessage = error?.message || error?.toString() ||
|
|
391
|
-
return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some(errorPattern =>
|
|
292
|
+
const errorMessage = error?.message || error?.toString() || "";
|
|
293
|
+
return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some((errorPattern) =>
|
|
294
|
+
errorMessage.includes(errorPattern)
|
|
295
|
+
);
|
|
392
296
|
}
|
|
393
|
-
|
|
394
297
|
module.exports = {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
298
|
+
NACK_REASONS: NACK_REASONS,
|
|
299
|
+
decodeMessageNode: decodeMessageNode,
|
|
300
|
+
decryptMessageNode: decryptMessageNode,
|
|
301
|
+
extractAddressingContext: extractAddressingContext,
|
|
302
|
+
MISSING_KEYS_ERROR_TEXT: MISSING_KEYS_ERROR_TEXT,
|
|
303
|
+
DECRYPTION_RETRY_CONFIG: DECRYPTION_RETRY_CONFIG,
|
|
304
|
+
NO_MESSAGE_FOUND_ERROR_TEXT: NO_MESSAGE_FOUND_ERROR_TEXT,
|
|
305
|
+
};
|