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.
- package/README.md +12 -179
- package/WAProto/GenerateStatics.sh +2 -0
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +14 -3
- package/lib/Signal/libsignal.js +79 -35
- package/lib/Signal/lid-mapping.js +156 -0
- package/lib/Socket/chats.js +69 -14
- package/lib/Socket/communities.js +430 -0
- package/lib/Socket/groups.js +7 -1
- package/lib/Socket/index.js +11 -6
- package/lib/Socket/messages-recv.js +63 -30
- package/lib/Socket/messages-send.js +114 -3
- package/lib/Socket/socket.js +98 -1
- package/lib/Types/Bussines.js +2 -0
- package/lib/Utils/browser-utils.js +33 -0
- package/lib/Utils/chat-utils.js +22 -12
- package/lib/Utils/decode-wa-message.js +7 -0
- package/lib/Utils/event-buffer.js +3 -1
- package/lib/Utils/generics.js +28 -1
- package/lib/Utils/history.js +11 -4
- package/lib/Utils/index.js +7 -0
- package/lib/Utils/message-retry-manager.js +151 -0
- package/lib/Utils/messages.js +31 -3
- package/lib/Utils/offline-node-processor.js +34 -0
- package/lib/Utils/pre-key-manager.js +95 -0
- package/lib/Utils/process-message.js +6 -4
- package/lib/Utils/stanza-ack.js +45 -0
- package/lib/Utils/sync-action-utils.js +23 -0
- package/lib/Utils/tc-token-utils.js +209 -0
- package/lib/WABinary/jid-utils.js +33 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +38 -0
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncQuery.js +5 -0
- package/lib/WAUSync/USyncUser.js +8 -0
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -1,208 +1,41 @@
|
|
|
1
1
|
# davexbaileys
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/davexbaileys)
|
|
3
4
|
[](https://www.npmjs.com/package/davexbaileys)
|
|
4
5
|
[](https://www.npmjs.com/package/davexbaileys)
|
|
5
6
|
[](LICENSE)
|
|
6
7
|
[](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
|
-
##
|
|
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('
|
|
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
|
|
package/lib/Defaults/index.js
CHANGED
|
@@ -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.
|
|
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
|
package/lib/Signal/libsignal.js
CHANGED
|
@@ -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
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|