davexbaileys 2.5.22 → 2.5.24

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.
Files changed (36) hide show
  1. package/README.md +12 -179
  2. package/WAProto/GenerateStatics.sh +2 -0
  3. package/lib/Defaults/baileys-version.json +1 -1
  4. package/lib/Defaults/index.js +14 -3
  5. package/lib/Signal/libsignal.js +79 -35
  6. package/lib/Signal/lid-mapping.js +156 -0
  7. package/lib/Socket/chats.js +69 -14
  8. package/lib/Socket/communities.js +430 -0
  9. package/lib/Socket/groups.js +7 -1
  10. package/lib/Socket/index.js +11 -6
  11. package/lib/Socket/messages-recv.js +63 -30
  12. package/lib/Socket/messages-send.js +114 -3
  13. package/lib/Socket/socket.js +98 -1
  14. package/lib/Types/Bussines.js +2 -0
  15. package/lib/Utils/browser-utils.js +33 -0
  16. package/lib/Utils/chat-utils.js +22 -12
  17. package/lib/Utils/decode-wa-message.js +7 -0
  18. package/lib/Utils/event-buffer.js +3 -1
  19. package/lib/Utils/generics.js +28 -1
  20. package/lib/Utils/history.js +11 -4
  21. package/lib/Utils/index.js +7 -0
  22. package/lib/Utils/message-retry-manager.js +151 -0
  23. package/lib/Utils/messages.js +31 -3
  24. package/lib/Utils/offline-node-processor.js +34 -0
  25. package/lib/Utils/pre-key-manager.js +95 -0
  26. package/lib/Utils/process-message.js +6 -4
  27. package/lib/Utils/stanza-ack.js +45 -0
  28. package/lib/Utils/sync-action-utils.js +23 -0
  29. package/lib/Utils/tc-token-utils.js +209 -0
  30. package/lib/WABinary/jid-utils.js +33 -3
  31. package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
  32. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +38 -0
  33. package/lib/WAUSync/Protocols/index.js +1 -0
  34. package/lib/WAUSync/USyncQuery.js +5 -0
  35. package/lib/WAUSync/USyncUser.js +8 -0
  36. package/package.json +12 -4
package/README.md CHANGED
@@ -1,208 +1,41 @@
1
1
  # davexbaileys
2
- [![npm version](https://img.shields.io/npm/v/davexbaileys?style=flat-square&color=blue)](https://www.npmjs.com/package/davexbaileys)
2
+
3
+ [![npm version](https://img.shields.io/npm/v/davexbaileys?style=flat-square&color=blue)](https://www.npmjs.com/package/davexbaileys)
3
4
  [![npm downloads](https://img.shields.io/npm/dm/davexbaileys?style=flat-square&color=green)](https://www.npmjs.com/package/davexbaileys)
4
5
  [![npm total](https://img.shields.io/npm/dt/davexbaileys?style=flat-square&color=orange&label=total+downloads)](https://www.npmjs.com/package/davexbaileys)
5
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)
6
7
  [![Node.js >=20](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen?style=flat-square)](https://nodejs.org)
7
8
 
8
-
9
- A lightweight, full-featured WhatsApp Web API library for Node.js — maintained by **Dave Tech**.
9
+ > A lightweight, full-featured WhatsApp Web API library for Node.js — maintained by **Dave Tech**.
10
10
 
11
- ## Installation
11
+ ## Install
12
12
 
13
13
  ```bash
14
14
  npm install davexbaileys
15
15
  ```
16
16
 
17
+ ## Requirements
18
+
19
+ - **Node.js** `>=20.0.0` (same as official [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys))
20
+
17
21
  ## Quick Start
18
22
 
19
23
  ```js
20
24
  const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion } = require('davexbaileys');
21
25
 
22
26
  const { version } = await fetchLatestBaileysVersion();
23
- const { state, saveCreds } = await useMultiFileAuthState('auth_info');
27
+ const { state, saveCreds } = await useMultiFileAuthState('./auth');
24
28
  const sock = makeWASocket({ version, auth: state });
25
29
  sock.ev.on('creds.update', saveCreds);
26
30
  ```
27
31
 
28
- ---
29
-
30
- ## Feature Reference
31
-
32
- ### Connection & Version
33
- - `fetchLatestBaileysVersion()` — get bundled WA version
34
- - `fetchLatestWaWebVersion()` — fetch live WA version from WhatsApp web
35
- - Multi-device (MD) with pairing code or QR
36
- - Auto reconnect, smart keepalive
37
-
38
- ---
39
-
40
- ### Messaging
41
- - Send text, images, videos, audio, documents, stickers, GIFs, reactions, polls, contacts, locations, events
42
- - Edit, delete, forward messages
43
- - Quote/reply, mention contacts
44
- - `sock.sendMessage(jid, { pin: key })` — pin/unpin a message in a chat
45
- - `sock.pinMessage(jid, key, type)` — explicit pin (type=1 pin, type=2 unpin)
46
-
47
- ---
48
-
49
- ### Private Chat Commands
50
- | Method | Description |
51
- |--------|-------------|
52
- | `sock.pinChat(jid, true/false)` | Pin or unpin a chat |
53
- | `sock.archiveChat(jid, lastMsgs)` | Archive a chat |
54
- | `sock.unarchiveChat(jid, lastMsgs)` | Unarchive a chat |
55
- | `sock.markChatRead(jid, lastMsgs)` | Mark chat as read |
56
- | `sock.markChatUnread(jid, lastMsgs)` | Mark chat as unread |
57
- | `sock.muteChat(jid, durationMs)` | Mute a chat (ms=0 to unmute) |
58
- | `sock.unmuteChat(jid)` | Unmute a chat |
59
- | `sock.deleteChat(jid, lastMsgs)` | Delete a chat |
60
- | `sock.clearChat(jid, lastMsgs)` | Clear all messages |
61
- | `sock.star(jid, messages, star)` | Star/unstar messages |
62
- | `sock.addOrEditContact(jid, contact)` | Add or edit a contact |
63
- | `sock.removeContact(jid)` | Remove a contact |
64
- | `sock.chatModify(mod, jid)` | Raw chat modification |
65
-
66
- ---
67
-
68
- ### Privacy & Settings
69
- | Method | Description |
70
- |--------|-------------|
71
- | `sock.updateLastSeenPrivacy(value)` | Who sees your last seen (`all`/`contacts`/`contact_blacklist`/`none`) |
72
- | `sock.updateOnlinePrivacy(value)` | Online status visibility |
73
- | `sock.updateProfilePicturePrivacy(value)` | Profile photo visibility |
74
- | `sock.updateStatusPrivacy(value)` | Status (about) text visibility |
75
- | `sock.updateReadReceiptsPrivacy(value)` | Read receipts (blue ticks) |
76
- | `sock.updateCallPrivacy(value)` | Who can call you |
77
- | `sock.updateGroupsAddPrivacy(value)` | Who can add you to groups |
78
- | `sock.updateGroupsJoinPrivacy(value)` | Alias for groups add privacy |
79
- | `sock.updateAboutPrivacy(value)` | Bio/about visibility |
80
- | `sock.updateMessagesPrivacy(value)` | Messages privacy |
81
- | `sock.updateDefaultDisappearingMode(duration)` | Default disappearing mode (seconds) |
82
- | `sock.updateDisableLinkPreviewsPrivacy(bool)` | Disable/enable link previews |
83
- | `sock.updateStatusResharePrivacy(bool)` | Prevent others from resharing your status |
84
- | `sock.silenceUnknownCallers()` | Silence calls from unknown numbers |
85
- | `sock.allowUnknownCallers()` | Allow calls from everyone |
86
- | `sock.fetchPrivacySettings()` | Fetch all current privacy settings |
87
- | `sock.fetchDisappearingDuration(...jids)` | Fetch disappearing message settings for contacts |
88
- | `sock.updateProfileStatus(text)` | Update bio/about text |
89
- | `sock.updateProfileName(name)` | Update display name |
90
- | `sock.updateProfilePicture(jid, buffer)` | Update profile picture |
91
- | `sock.removeProfilePicture(jid)` | Remove profile picture |
92
-
93
- ---
94
-
95
- ### Group Commands
96
- - Create, leave, join by invite link/code
97
- - Add, remove, promote, demote participants
98
- - Update subject, description, icon, settings
99
- - Toggle ephemeral (disappearing messages)
100
- - `sock.groupSettingUpdate(jid, setting)` — lock/unlock settings
101
-
102
- #### Anti-Feature Helpers (Group Moderation)
103
- ```js
104
- const { isAntiLink, isAntiSticker, isAntiImage, isAntiVideo, isAntiAudio,
105
- isAntiDocument, isAntiViewOnce, isAntiBug, isAntiFiles,
106
- getMessageType, hasLink, extractLinks,
107
- isSticker, isImage, isVideo, isAudio, isDocument,
108
- isViewOnce, isReaction, isPoll, isGif, isForwarded,
109
- LINK_REGEX } = require('davexbaileys');
110
-
111
- sock.ev.on('messages.upsert', async ({ messages }) => {
112
- const msg = messages[0];
113
- if (msg.key.fromMe || !msg.key.remoteJid.endsWith('@g.us')) return;
114
-
115
- if (isAntiLink(msg)) { /* delete message, warn user */ }
116
- if (isAntiSticker(msg)) { /* no stickers allowed */ }
117
- if (isAntiImage(msg)) { /* no images */ }
118
- if (isAntiVideo(msg)) { /* no videos */ }
119
- if (isAntiAudio(msg)) { /* no voice notes */ }
120
- if (isAntiDocument(msg)){ /* no documents */ }
121
- if (isAntiViewOnce(msg)){ /* no view-once messages */ }
122
- if (isAntiBug(msg)) { /* potential crash message */ }
123
- if (isAntiFiles(msg)) { /* no files of any kind */ }
124
-
125
- console.log('Message type:', getMessageType(msg));
126
- });
127
- ```
128
-
129
- #### Anti-Group-Mention (detect group JIDs in messages/statuses)
130
- ```js
131
- const { isAntiGroupMention, getGroupMentions } = require('davexbaileys');
132
-
133
- // true if any @g.us JID is mentioned in the message
134
- if (isAntiGroupMention(msg)) {
135
- const groups = getGroupMentions(msg); // array of group JIDs mentioned
136
- await sock.sendMessage(msg.key.remoteJid, { text: '❌ Group mentions are not allowed!' });
137
- }
138
- ```
139
-
140
- ---
141
-
142
- ### Newsletter (Channel) Commands
143
- | Method | Description |
144
- |--------|-------------|
145
- | `sock.newsletterCreate(name, desc)` | Create a channel |
146
- | `sock.newsletterFollow(jid)` | Follow a channel |
147
- | `sock.newsletterUnfollow(jid)` | Unfollow |
148
- | `sock.newsletterMute(jid)` | Mute a channel |
149
- | `sock.newsletterUnmute(jid)` | Unmute |
150
- | `sock.newsletterMetadata(type, key)` | Get channel metadata |
151
- | `sock.newsletterSubscribers(jid)` | Get subscribers |
152
- | `sock.newsletterReactMessage(jid, serverId, emoji)` | React to a post |
153
- | `sock.newsletterFetchMessages(jid, count, since)` | Fetch posts |
154
- | `sock.newsletterUpdateName(jid, name)` | Update channel name |
155
- | `sock.newsletterUpdateDescription(jid, desc)` | Update description |
156
- | `sock.newsletterUpdatePicture(jid, buffer)` | Update channel photo |
157
- | `sock.newsletterRemovePicture(jid)` | Remove channel photo |
158
- | `sock.newsletterDelete(jid)` | Delete channel |
159
- | `sock.newsletterChangeOwner(jid, newOwnerJid)` | Transfer ownership |
160
- | `sock.subscribeNewsletterUpdates(jid)` | Subscribe to live updates |
161
-
162
- **Events:**
163
- ```js
164
- sock.ev.on('newsletter.reaction', ({ id, server_id, reaction }) => {});
165
- sock.ev.on('newsletter.view', ({ id, server_id, count }) => {});
166
- sock.ev.on('newsletter-participants.update', update => {});
167
- sock.ev.on('newsletter-settings.update', update => {});
168
- ```
169
-
170
- > Auto-react: davexbaileys automatically reacts 👍 to new posts from the Dave Tech channel.
171
-
172
- ---
173
-
174
- ### WhatsApp Business
175
- - `sock.getCatalog({ jid, limit, cursor })`
176
- - `sock.getCollections(jid, limit)`
177
- - `sock.getOrderDetails(orderId, tokenBase64)`
178
- - `sock.getBusinessProfile(jid)`
179
- - `sock.addOrEditQuickReply(quickReply)` — business quick replies
180
- - `sock.removeQuickReply(timestamp)`
181
-
182
- ---
183
-
184
- ### Link Preview
185
- ```js
186
- // Enable rich link previews:
187
- const sock = makeWASocket({ generateHighQualityLinkPreview: true, ... });
188
-
189
- // Disable link previews in your account settings:
190
- await sock.updateDisableLinkPreviewsPrivacy(true);
191
-
192
- // Utility helper (basic preview object):
193
- const { generateLinkPreview } = require('davexbaileys');
194
- const preview = generateLinkPreview('https://example.com', 'Example', 'Description');
195
- ```
196
-
197
- ---
32
+ 📦 Full docs & API reference → **[npmjs.com/package/davexbaileys](https://www.npmjs.com/package/davexbaileys)**
198
33
 
199
34
  ## Author
200
35
 
201
- **Dave Tech**
202
- GitHub: [Davex-254](https://github.com/Davex-254)
203
- Channel: [Dave Tech on WhatsApp](https://whatsapp.com/channel/0029Vb6wIVU9Bb5w69FQvt0W)
36
+ **Dave Tech** — [WhatsApp Channel](https://whatsapp.com/channel/0029Vb6wIVU9Bb5w69FQvt0W)
204
37
 
205
38
  ## License
206
39
 
207
- MIT
40
+ [MIT](LICENSE) © Dave Tech
208
41
 
@@ -0,0 +1,2 @@
1
+ yarn pbjs -t static-module -w commonjs -o ./WAProto/index.js ./WAProto/WAProto.proto;
2
+ yarn pbts -o ./WAProto/index.d.ts ./WAProto/index.js;
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": [2, 3000, 1032141294]
2
+ "version": [2, 3000, 1035194821]
3
3
  }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_CACHE_TTLS = exports.INITIAL_PREKEY_COUNT = exports.MIN_PREKEY_COUNT = exports.MEDIA_KEYS = exports.MEDIA_HKDF_KEY_MAPPING = exports.MEDIA_PATH_MAP = exports.DEFAULT_CONNECTION_CONFIG = exports.PROCESSABLE_HISTORY_TYPES = exports.WA_CERT_DETAILS = exports.URL_REGEX = exports.NOISE_WA_HEADER = exports.KEY_BUNDLE_TYPE = exports.DICT_VERSION = exports.NOISE_MODE = exports.WA_DEFAULT_EPHEMERAL = exports.PHONE_CONNECTION_CB = exports.DEF_TAG_PREFIX = exports.DEF_CALLBACK_PREFIX = exports.DEFAULT_ORIGIN = exports.UNAUTHORIZED_CODES = void 0;
6
+ exports.DEFAULT_CACHE_TTLS = exports.MIN_UPLOAD_INTERVAL = exports.UPLOAD_TIMEOUT = exports.INITIAL_PREKEY_COUNT = exports.MIN_PREKEY_COUNT = exports.MEDIA_KEYS = exports.MEDIA_HKDF_KEY_MAPPING = exports.MEDIA_PATH_MAP = exports.DEFAULT_CONNECTION_CONFIG = exports.PROCESSABLE_HISTORY_TYPES = exports.WA_CERT_DETAILS = exports.URL_REGEX = exports.NOISE_WA_HEADER = exports.KEY_BUNDLE_TYPE = exports.DICT_VERSION = exports.NOISE_MODE = exports.WA_DEFAULT_EPHEMERAL = exports.WA_ADV_HOSTED_DEVICE_SIG_PREFIX = exports.WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX = exports.WA_ADV_DEVICE_SIG_PREFIX = exports.WA_ADV_ACCOUNT_SIG_PREFIX = exports.PHONE_CONNECTION_CB = exports.DEF_TAG_PREFIX = exports.DEF_CALLBACK_PREFIX = exports.CALL_AUDIO_PREFIX = exports.CALL_VIDEO_PREFIX = exports.DEFAULT_ORIGIN = exports.UNAUTHORIZED_CODES = void 0;
7
7
  const WAProto_1 = require("../../WAProto");
8
8
  const libsignal_1 = require("../Signal/libsignal");
9
9
  const Utils_1 = require("../Utils");
@@ -11,9 +11,15 @@ const logger_1 = __importDefault(require("../Utils/logger"));
11
11
  const baileys_version_json_1 = require("./baileys-version.json");
12
12
  exports.UNAUTHORIZED_CODES = [401, 403, 419];
13
13
  exports.DEFAULT_ORIGIN = 'https://web.whatsapp.com';
14
+ exports.CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/';
15
+ exports.CALL_AUDIO_PREFIX = 'https://call.whatsapp.com/voice/';
14
16
  exports.DEF_CALLBACK_PREFIX = 'CB:';
15
17
  exports.DEF_TAG_PREFIX = 'TAG:';
16
18
  exports.PHONE_CONNECTION_CB = 'CB:Pong';
19
+ exports.WA_ADV_ACCOUNT_SIG_PREFIX = Buffer.from([6, 0]);
20
+ exports.WA_ADV_DEVICE_SIG_PREFIX = Buffer.from([6, 1]);
21
+ exports.WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX = Buffer.from([6, 5]);
22
+ exports.WA_ADV_HOSTED_DEVICE_SIG_PREFIX = Buffer.from([6, 6]);
17
23
  exports.WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60;
18
24
  exports.NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0';
19
25
  exports.DICT_VERSION = 3;
@@ -29,11 +35,13 @@ exports.PROCESSABLE_HISTORY_TYPES = [
29
35
  WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.PUSH_NAME,
30
36
  WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.RECENT,
31
37
  WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.FULL,
32
- WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.ON_DEMAND
38
+ WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.ON_DEMAND,
39
+ WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.NON_BLOCKING_DATA,
40
+ WAProto_1.proto.Message.HistorySyncNotification.HistorySyncType.INITIAL_STATUS_V3
33
41
  ];
34
42
  exports.DEFAULT_CONNECTION_CONFIG = {
35
43
  version: baileys_version_json_1.version,
36
- browser: Utils_1.Browsers.ubuntu('Chrome'),
44
+ browser: Utils_1.Browsers.macOS('Chrome'),
37
45
  waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
38
46
  connectTimeoutMs: 20000,
39
47
  keepAliveIntervalMs: 30000,
@@ -53,6 +61,7 @@ exports.DEFAULT_CONNECTION_CONFIG = {
53
61
  linkPreviewImageThumbnailWidth: 192,
54
62
  transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 3000 },
55
63
  generateHighQualityLinkPreview: false,
64
+ enableAutoSessionRecreation: true,
56
65
  options: {},
57
66
  appStateMacVerification: {
58
67
  patch: false,
@@ -97,6 +106,8 @@ exports.MEDIA_HKDF_KEY_MAPPING = {
97
106
  exports.MEDIA_KEYS = Object.keys(exports.MEDIA_PATH_MAP);
98
107
  exports.MIN_PREKEY_COUNT = 5;
99
108
  exports.INITIAL_PREKEY_COUNT = 30;
109
+ exports.UPLOAD_TIMEOUT = 30000;
110
+ exports.MIN_UPLOAD_INTERVAL = 5000;
100
111
  exports.DEFAULT_CACHE_TTLS = {
101
112
  SIGNAL_STORE: 5 * 60, // 5 minutes
102
113
  MSG_RETRY: 60 * 60, // 1 hour
@@ -35,18 +35,30 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.makeLibSignalRepository = makeLibSignalRepository;
37
37
  const libsignal = __importStar(require("libsignal"));
38
+ const lru_cache_1 = require("lru-cache");
38
39
  const Utils_1 = require("../Utils");
39
40
  const WABinary_1 = require("../WABinary");
40
41
  const sender_key_name_1 = require("./Group/sender-key-name");
41
42
  const sender_key_record_1 = require("./Group/sender-key-record");
42
43
  const Group_1 = require("./Group");
43
- function makeLibSignalRepository(auth) {
44
- const storage = signalStorage(auth);
44
+ const lid_mapping_1 = require("./lid-mapping");
45
+
46
+ function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
47
+ const lidMapping = new lid_mapping_1.LIDMappingStore(auth.keys, logger, pnToLIDFunc);
48
+ const storage = signalStorage(auth, lidMapping);
49
+ const parsedKeys = auth.keys;
50
+ const migratedSessionCache = new lru_cache_1.LRUCache({
51
+ ttl: 3 * 24 * 60 * 60 * 1000,
52
+ ttlAutopurge: true,
53
+ updateAgeOnGet: true
54
+ });
45
55
  return {
46
56
  decryptGroupMessage({ group, authorJid, msg }) {
47
57
  const senderName = jidToSignalSenderKeyName(group, authorJid);
48
58
  const cipher = new Group_1.GroupCipher(storage, senderName);
49
- return cipher.decrypt(msg);
59
+ return parsedKeys.transaction(async () => {
60
+ return cipher.decrypt(msg);
61
+ }, group);
50
62
  },
51
63
  async processSenderKeyDistributionMessage({ item, authorJid }) {
52
64
  const builder = new Group_1.GroupSessionBuilder(storage);
@@ -56,60 +68,91 @@ function makeLibSignalRepository(auth) {
56
68
  const senderName = jidToSignalSenderKeyName(item.groupId, authorJid);
57
69
  const senderMsg = new Group_1.SenderKeyDistributionMessage(null, null, null, null, item.axolotlSenderKeyDistributionMessage);
58
70
  const senderNameStr = senderName.toString();
59
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
60
- if (!senderKey) {
61
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
62
- }
63
- await builder.process(senderName, senderMsg);
71
+ return parsedKeys.transaction(async () => {
72
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
73
+ if (!senderKey) {
74
+ await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
75
+ }
76
+ await builder.process(senderName, senderMsg);
77
+ }, item.groupId);
64
78
  },
65
79
  async decryptMessage({ jid, type, ciphertext }) {
66
80
  const addr = jidToSignalProtocolAddress(jid);
67
81
  const session = new libsignal.SessionCipher(storage, addr);
68
- let result;
69
- switch (type) {
70
- case 'pkmsg':
71
- result = await session.decryptPreKeyWhisperMessage(ciphertext);
72
- break;
73
- case 'msg':
74
- result = await session.decryptWhisperMessage(ciphertext);
75
- break;
76
- default:
77
- throw new Error(`Unknown message type: ${type}`);
82
+ async function doDecrypt() {
83
+ let result;
84
+ switch (type) {
85
+ case 'pkmsg':
86
+ result = await session.decryptPreKeyWhisperMessage(ciphertext);
87
+ break;
88
+ case 'msg':
89
+ result = await session.decryptWhisperMessage(ciphertext);
90
+ break;
91
+ default:
92
+ throw new Error(`Unknown message type: ${type}`);
93
+ }
94
+ return result;
78
95
  }
79
- return result;
96
+ return parsedKeys.transaction(async () => {
97
+ return await doDecrypt();
98
+ }, jid);
80
99
  },
81
100
  async encryptMessage({ jid, data }) {
82
101
  const addr = jidToSignalProtocolAddress(jid);
83
102
  const cipher = new libsignal.SessionCipher(storage, addr);
84
- const { type: sigType, body } = await cipher.encrypt(data);
85
- const type = sigType === 3 ? 'pkmsg' : 'msg';
86
- return { type, ciphertext: Buffer.from(body, 'binary') };
103
+ return parsedKeys.transaction(async () => {
104
+ const { type: sigType, body } = await cipher.encrypt(data);
105
+ const type = sigType === 3 ? 'pkmsg' : 'msg';
106
+ return { type, ciphertext: Buffer.from(body, 'binary') };
107
+ }, jid);
87
108
  },
88
109
  async encryptGroupMessage({ group, meId, data }) {
89
110
  const senderName = jidToSignalSenderKeyName(group, meId);
90
111
  const builder = new Group_1.GroupSessionBuilder(storage);
91
112
  const senderNameStr = senderName.toString();
92
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
93
- if (!senderKey) {
94
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
95
- }
96
- const senderKeyDistributionMessage = await builder.create(senderName);
97
- const session = new Group_1.GroupCipher(storage, senderName);
98
- const ciphertext = await session.encrypt(data);
99
- return {
100
- ciphertext,
101
- senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
102
- };
113
+ return parsedKeys.transaction(async () => {
114
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
115
+ if (!senderKey) {
116
+ await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
117
+ }
118
+ const senderKeyDistributionMessage = await builder.create(senderName);
119
+ const session = new Group_1.GroupCipher(storage, senderName);
120
+ const ciphertext = await session.encrypt(data);
121
+ return {
122
+ ciphertext,
123
+ senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
124
+ };
125
+ }, group);
103
126
  },
104
127
  async injectE2ESession({ jid, session }) {
105
128
  const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid));
106
- await cipher.initOutgoing(session);
129
+ return parsedKeys.transaction(async () => {
130
+ await cipher.initOutgoing(session);
131
+ }, jid);
107
132
  },
108
133
  jidToSignalProtocolAddress(jid) {
109
134
  return jidToSignalProtocolAddress(jid).toString();
135
+ },
136
+ lidMapping,
137
+ async validateSession(jid) {
138
+ try {
139
+ const addr = jidToSignalProtocolAddress(jid);
140
+ const session = await storage.loadSession(addr.toString());
141
+ if (!session) {
142
+ return { exists: false, reason: 'no session' };
143
+ }
144
+ if (!session.haveOpenSession()) {
145
+ return { exists: false, reason: 'no open session' };
146
+ }
147
+ return { exists: true };
148
+ }
149
+ catch (error) {
150
+ return { exists: false, reason: 'validation error' };
151
+ }
110
152
  }
111
153
  };
112
154
  }
155
+
113
156
  const jidToSignalProtocolAddress = (jid) => {
114
157
  const { user, device } = (0, WABinary_1.jidDecode)(jid);
115
158
  return new libsignal.ProtocolAddress(user, device || 0);
@@ -117,7 +160,8 @@ const jidToSignalProtocolAddress = (jid) => {
117
160
  const jidToSignalSenderKeyName = (group, user) => {
118
161
  return new sender_key_name_1.SenderKeyName(group, jidToSignalProtocolAddress(user));
119
162
  };
120
- function signalStorage({ creds, keys }) {
163
+
164
+ function signalStorage({ creds, keys }, lidMapping) {
121
165
  return {
122
166
  loadSession: async (id) => {
123
167
  const { [id]: sess } = await keys.get('session', [id]);
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LIDMappingStore = void 0;
4
+ const lru_cache_1 = require("lru-cache");
5
+ const WABinary_1 = require("../WABinary");
6
+
7
+ class LIDMappingStore {
8
+ constructor(keys, logger, pnToLIDFunc) {
9
+ this.mappingCache = new lru_cache_1.LRUCache({
10
+ ttl: 3 * 24 * 60 * 60 * 1000,
11
+ ttlAutopurge: true,
12
+ updateAgeOnGet: true
13
+ });
14
+ this.keys = keys;
15
+ this.pnToLIDFunc = pnToLIDFunc;
16
+ this.logger = logger;
17
+ }
18
+
19
+ async storeLIDPNMappings(pairs) {
20
+ const pairMap = {};
21
+ for (const { lid, pn } of pairs) {
22
+ if (!((0, WABinary_1.isLidUser)(lid) && (0, WABinary_1.isPnUser)(pn)) &&
23
+ !((0, WABinary_1.isPnUser)(lid) && (0, WABinary_1.isLidUser)(pn))) {
24
+ this.logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`);
25
+ continue;
26
+ }
27
+ const lidDecoded = (0, WABinary_1.jidDecode)(lid);
28
+ const pnDecoded = (0, WABinary_1.jidDecode)(pn);
29
+ if (!lidDecoded || !pnDecoded) return;
30
+ const pnUser = pnDecoded.user;
31
+ const lidUser = lidDecoded.user;
32
+ let existingLidUser = this.mappingCache.get(`pn:${pnUser}`);
33
+ if (!existingLidUser) {
34
+ this.logger.trace(`Cache miss for PN user ${pnUser}; checking database`);
35
+ const stored = await this.keys.get('lid-mapping', [pnUser]);
36
+ existingLidUser = stored[pnUser];
37
+ if (existingLidUser) {
38
+ this.mappingCache.set(`pn:${pnUser}`, existingLidUser);
39
+ this.mappingCache.set(`lid:${existingLidUser}`, pnUser);
40
+ }
41
+ }
42
+ if (existingLidUser === lidUser) {
43
+ this.logger.debug({ pnUser, lidUser }, 'LID mapping already exists, skipping');
44
+ continue;
45
+ }
46
+ pairMap[pnUser] = lidUser;
47
+ }
48
+ this.logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`);
49
+ await this.keys.transaction(async () => {
50
+ for (const [pnUser, lidUser] of Object.entries(pairMap)) {
51
+ await this.keys.set({
52
+ 'lid-mapping': {
53
+ [pnUser]: lidUser,
54
+ [`${lidUser}_reverse`]: pnUser
55
+ }
56
+ });
57
+ this.mappingCache.set(`pn:${pnUser}`, lidUser);
58
+ this.mappingCache.set(`lid:${lidUser}`, pnUser);
59
+ }
60
+ }, 'lid-mapping');
61
+ }
62
+
63
+ async getLIDForPN(pn) {
64
+ return (await this.getLIDsForPNs([pn]))?.[0]?.lid || null;
65
+ }
66
+
67
+ async getLIDsForPNs(pns) {
68
+ const usyncFetch = {};
69
+ const successfulPairs = {};
70
+ for (const pn of pns) {
71
+ if (!(0, WABinary_1.isPnUser)(pn) && !(0, WABinary_1.isHostedPnUser)(pn)) continue;
72
+ const decoded = (0, WABinary_1.jidDecode)(pn);
73
+ if (!decoded) continue;
74
+ const pnUser = decoded.user;
75
+ let lidUser = this.mappingCache.get(`pn:${pnUser}`);
76
+ if (!lidUser) {
77
+ const stored = await this.keys.get('lid-mapping', [pnUser]);
78
+ lidUser = stored[pnUser];
79
+ if (lidUser) {
80
+ this.mappingCache.set(`pn:${pnUser}`, lidUser);
81
+ this.mappingCache.set(`lid:${lidUser}`, pnUser);
82
+ }
83
+ else {
84
+ this.logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`);
85
+ const device = decoded.device || 0;
86
+ let normalizedPn = (0, WABinary_1.jidNormalizedUser)(pn);
87
+ if ((0, WABinary_1.isHostedPnUser)(normalizedPn)) {
88
+ normalizedPn = `${pnUser}@s.whatsapp.net`;
89
+ }
90
+ if (!usyncFetch[normalizedPn]) {
91
+ usyncFetch[normalizedPn] = [device];
92
+ }
93
+ else {
94
+ usyncFetch[normalizedPn]?.push(device);
95
+ }
96
+ continue;
97
+ }
98
+ }
99
+ lidUser = lidUser.toString();
100
+ if (!lidUser) {
101
+ this.logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`);
102
+ return null;
103
+ }
104
+ const pnDevice = decoded.device !== undefined ? decoded.device : 0;
105
+ const domainType = decoded.domainType;
106
+ const deviceSpecificLid = `${lidUser}${pnDevice ? `:${pnDevice}` : ''}@${domainType === 128 ? 'hosted.lid' : 'lid'}`;
107
+ this.logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`);
108
+ successfulPairs[pn] = { lid: deviceSpecificLid, pn };
109
+ }
110
+ if (Object.keys(usyncFetch).length > 0) {
111
+ const result = await this.pnToLIDFunc?.(Object.keys(usyncFetch));
112
+ if (result && result.length > 0) {
113
+ await this.storeLIDPNMappings(result);
114
+ for (const pair of result) {
115
+ const pnDecoded = (0, WABinary_1.jidDecode)(pair.pn);
116
+ const pnUser = pnDecoded?.user;
117
+ if (!pnUser) continue;
118
+ const lidUser = (0, WABinary_1.jidDecode)(pair.lid)?.user;
119
+ if (!lidUser) continue;
120
+ for (const device of (usyncFetch[pair.pn] || [])) {
121
+ const deviceSpecificLid = `${lidUser}${device ? `:${device}` : ''}@${device === 99 ? 'hosted.lid' : 'lid'}`;
122
+ this.logger.trace(`getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid}`);
123
+ const deviceSpecificPn = `${pnUser}${device ? `:${device}` : ''}@${device === 99 ? 'hosted' : 's.whatsapp.net'}`;
124
+ successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn };
125
+ }
126
+ }
127
+ }
128
+ else {
129
+ return null;
130
+ }
131
+ }
132
+ return Object.values(successfulPairs);
133
+ }
134
+
135
+ async getPNForLID(lid) {
136
+ if (!(0, WABinary_1.isLidUser)(lid)) return null;
137
+ const decoded = (0, WABinary_1.jidDecode)(lid);
138
+ if (!decoded) return null;
139
+ const lidUser = decoded.user;
140
+ let pnUser = this.mappingCache.get(`lid:${lidUser}`);
141
+ if (!pnUser || typeof pnUser !== 'string') {
142
+ const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`]);
143
+ pnUser = stored[`${lidUser}_reverse`];
144
+ if (!pnUser || typeof pnUser !== 'string') {
145
+ this.logger.trace(`No reverse mapping found for LID user: ${lidUser}`);
146
+ return null;
147
+ }
148
+ this.mappingCache.set(`lid:${lidUser}`, pnUser);
149
+ }
150
+ const lidDevice = decoded.device !== undefined ? decoded.device : 0;
151
+ const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === 129 ? 'hosted' : 's.whatsapp.net'}`;
152
+ this.logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
153
+ return pnJid;
154
+ }
155
+ }
156
+ exports.LIDMappingStore = LIDMappingStore;