whalibmob 5.5.90 → 5.6.2

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.
@@ -0,0 +1,273 @@
1
+ 'use strict';
2
+
3
+ // ClientPayload encoders for the WhatsApp Web (companion) handshake.
4
+ //
5
+ // The mobile handshake in proto.js builds a payload around `username` and a
6
+ // phone-shaped UserAgent. A companion sends the same message type with a
7
+ // different set of fields: on its very first connection it carries
8
+ // devicePairingData — the registration bundle the account's primary device
9
+ // signs during pairing — and on every connection after that it carries the
10
+ // device index the server issued and asks to be caught up.
11
+ //
12
+ // Written by hand against the wire format for the same reason proto.js is:
13
+ // keeping the handshake independent of a protobuf runtime.
14
+
15
+ const crypto = require('crypto');
16
+
17
+ // ─── primitives ──────────────────────────────────────────────────────────────
18
+
19
+ function encodeVarint(n) {
20
+ const out = [];
21
+ let v = n >>> 0;
22
+ do {
23
+ let b = v & 0x7f;
24
+ v >>>= 7;
25
+ if (v) b |= 0x80;
26
+ out.push(b);
27
+ } while (v);
28
+ return Buffer.from(out);
29
+ }
30
+
31
+ // username is a uint64 — the 32-bit helper above truncates long international
32
+ // numbers, so it gets its own encoder.
33
+ function encodeVarint64(value) {
34
+ const out = [];
35
+ let v = BigInt(value);
36
+ do {
37
+ let b = Number(v & 0x7fn);
38
+ v >>= 7n;
39
+ if (v > 0n) b |= 0x80;
40
+ out.push(b);
41
+ } while (v > 0n);
42
+ return Buffer.from(out);
43
+ }
44
+
45
+ function field(num, wireType, valueBytes) {
46
+ return Buffer.concat([encodeVarint((num << 3) | wireType), valueBytes]);
47
+ }
48
+
49
+ function bytesField(num, buf) { return field(num, 2, Buffer.concat([encodeVarint(buf.length), buf])); }
50
+ function stringField(num, str) { return bytesField(num, Buffer.from(String(str), 'utf8')); }
51
+ function varintField(num, n) { return field(num, 0, encodeVarint(n)); }
52
+ function uint64Field(num, n) { return field(num, 0, encodeVarint64(n)); }
53
+ function boolField(num, v) { return varintField(num, v ? 1 : 0); }
54
+
55
+ // WhatsApp encodes registration ids and pre-key ids inside the pairing bundle
56
+ // as fixed-width big-endian byte strings, not as varints.
57
+ function encodeBigEndian(value, length) {
58
+ length = length || 4;
59
+ const buf = Buffer.alloc(length);
60
+ for (let i = length - 1; i >= 0; i--) {
61
+ buf[i] = value & 0xff;
62
+ value = Math.floor(value / 256);
63
+ }
64
+ return buf;
65
+ }
66
+
67
+ function stripKeyPrefix(pub) {
68
+ if (pub && pub.length === 33 && pub[0] === 0x05) return pub.slice(1);
69
+ return pub;
70
+ }
71
+
72
+ // ─── DeviceProps ─────────────────────────────────────────────────────────────
73
+ //
74
+ // How the companion describes itself to the primary. `os` and the platform type
75
+ // are what the account owner reads in the linked-devices list on their phone;
76
+ // historySyncConfig decides how much of the past the primary ships over once
77
+ // the link is established.
78
+
79
+ const PLATFORM_TYPE = {
80
+ UNKNOWN: 0, CHROME: 1, FIREFOX: 2, IE: 3, OPERA: 4, SAFARI: 5, EDGE: 6,
81
+ DESKTOP: 7, IPAD: 8, ANDROID_TABLET: 9, OHANA: 10, ALOHA: 11, CATALINA: 12,
82
+ TCL_TV: 13, IOS_PHONE: 14, IOS_CATALYST: 15, ANDROID_PHONE: 16,
83
+ ANDROID_AMBIGUOUS: 17, WEAR_OS: 18, AR_WRIST: 19, AR_DEVICE: 20, UWP: 21,
84
+ VR: 22, CLOUD_API: 23, SMARTGLASSES: 24
85
+ };
86
+
87
+ function platformTypeFor(browserName) {
88
+ const key = String(browserName || '').toUpperCase();
89
+ if (key === 'ANDROID') return PLATFORM_TYPE.ANDROID_PHONE;
90
+ return PLATFORM_TYPE[key] !== undefined ? PLATFORM_TYPE[key] : PLATFORM_TYPE.CHROME;
91
+ }
92
+
93
+ // DeviceProps.HistorySyncConfig
94
+ function encodeHistorySyncConfig(opts) {
95
+ opts = opts || {};
96
+ const parts = [
97
+ varintField(3, opts.storageQuotaMb != null ? opts.storageQuotaMb : 10240),
98
+ boolField(4, opts.inlineInitialPayloadInE2EeMsg !== false),
99
+ boolField(6, !!opts.supportCallLogHistory),
100
+ boolField(7, opts.supportBotUserAgentChatHistory !== false),
101
+ boolField(8, opts.supportCagReactionsAndPolls !== false),
102
+ boolField(9, opts.supportBizHostedMsg !== false),
103
+ boolField(10, opts.supportRecentSyncChunkMessageCountTuning !== false),
104
+ boolField(11, opts.supportHostedGroupMsg !== false),
105
+ boolField(12, opts.supportFbidBotChatHistory !== false),
106
+ boolField(14, opts.supportMessageAssociation !== false),
107
+ boolField(15, !!opts.supportGroupHistory)
108
+ ];
109
+ if (opts.fullSyncDaysLimit) parts.unshift(varintField(1, opts.fullSyncDaysLimit));
110
+ if (opts.fullSyncSizeMbLimit) parts.unshift(varintField(2, opts.fullSyncSizeMbLimit));
111
+ return Buffer.concat(parts);
112
+ }
113
+
114
+ // DeviceProps.AppVersion — the companion's own version, not the account's.
115
+ function encodeDevicePropsVersion(primary, secondary, tertiary) {
116
+ return Buffer.concat([
117
+ varintField(1, primary),
118
+ varintField(2, secondary),
119
+ varintField(3, tertiary)
120
+ ]);
121
+ }
122
+
123
+ function encodeDeviceProps(opts) {
124
+ opts = opts || {};
125
+ return Buffer.concat([
126
+ stringField(1, opts.os || 'Ubuntu'),
127
+ bytesField(2, encodeDevicePropsVersion(10, 15, 7)),
128
+ varintField(3, platformTypeFor(opts.browserName || 'Chrome')),
129
+ boolField(4, !!opts.requireFullSync),
130
+ bytesField(5, encodeHistorySyncConfig(opts.historySync))
131
+ ]);
132
+ }
133
+
134
+ // ─── ClientPayload.UserAgent ─────────────────────────────────────────────────
135
+ //
136
+ // Platform 14 = WEB. Carries no phone identifiers at all — no real mcc/mnc, no
137
+ // device id, no phone id. That absence is part of what marks the connection as
138
+ // a companion rather than a primary.
139
+
140
+ const PLATFORM_WEB = 14;
141
+ const PLATFORM_ANDROID = 0;
142
+
143
+ function encodeAppVersion(v) {
144
+ return Buffer.concat([
145
+ varintField(1, v[0]),
146
+ varintField(2, v[1]),
147
+ varintField(3, v[2])
148
+ ]);
149
+ }
150
+
151
+ function encodeWebUserAgent(opts) {
152
+ const version = opts.version || [2, 3000, 1];
153
+ const browser = opts.browser || ['Ubuntu', 'Chrome', '120.0.0.0'];
154
+ const isAndroid = String(browser[1] || '').toLowerCase().includes('android');
155
+ return Buffer.concat([
156
+ varintField(1, isAndroid ? PLATFORM_ANDROID : PLATFORM_WEB),
157
+ bytesField(2, encodeAppVersion(version)),
158
+ stringField(3, '000'), // mcc
159
+ stringField(4, '000'), // mnc
160
+ stringField(5, '0.1'), // osVersion
161
+ stringField(6, 'Desktop'), // manufacturer
162
+ stringField(7, 'Desktop'), // device
163
+ stringField(8, '0.1'), // osBuildNumber
164
+ varintField(10, 0), // releaseChannel = RELEASE
165
+ stringField(11, 'en'), // localeLanguageIso6391
166
+ stringField(12, String(opts.countryCode || 'US')) // localeCountryIso31661Alpha2
167
+ ]);
168
+ }
169
+
170
+ // ClientPayload.WebInfo — webSubPlatform (field 4), WEB_BROWSER = 0.
171
+ function encodeWebInfo() {
172
+ return varintField(4, 0);
173
+ }
174
+
175
+ // ─── ClientPayload.DevicePairingRegistrationData ─────────────────────────────
176
+ //
177
+ // The bundle the primary device signs. eIdent is our identity public key, and
178
+ // the account signature returned in pair-success is made over it — that is the
179
+ // link between this companion and the account.
180
+ function encodeDevicePairingData(o) {
181
+ return Buffer.concat([
182
+ bytesField(1, o.eRegid),
183
+ bytesField(2, o.eKeytype),
184
+ bytesField(3, o.eIdent),
185
+ bytesField(4, o.eSkeyId),
186
+ bytesField(5, o.eSkeyVal),
187
+ bytesField(6, o.eSkeySig),
188
+ bytesField(7, o.buildHash),
189
+ bytesField(8, o.deviceProps)
190
+ ]);
191
+ }
192
+
193
+ // ─── ClientPayload ───────────────────────────────────────────────────────────
194
+ //
195
+ // Field numbers, straight off the schema:
196
+ // 1 username · 3 passive · 5 userAgent · 6 webInfo · 7 pushName
197
+ // 12 connectType · 13 connectReason · 18 device · 19 devicePairingData
198
+ // 33 pull
199
+
200
+ const CONNECT_TYPE_WIFI_UNKNOWN = 1;
201
+ const CONNECT_REASON_USER_ACTIVATED = 1;
202
+
203
+ function commonWebPayload(opts) {
204
+ const parts = [
205
+ bytesField(5, encodeWebUserAgent(opts)),
206
+ bytesField(6, encodeWebInfo()),
207
+ varintField(12, CONNECT_TYPE_WIFI_UNKNOWN),
208
+ varintField(13, CONNECT_REASON_USER_ACTIVATED)
209
+ ];
210
+ if (opts.pushName) parts.push(stringField(7, opts.pushName));
211
+ return parts;
212
+ }
213
+
214
+ /**
215
+ * First connection of a brand-new companion. There is no JID yet, so the
216
+ * payload carries the pairing bundle; the server answers with a pair-device
217
+ * (the QR path) or, once a pairing code has been requested and typed into the
218
+ * phone, with a pair-success.
219
+ */
220
+ function encodeCompanionRegisterPayload(opts) {
221
+ const version = opts.version || [2, 3000, 1];
222
+ const buildHash = crypto.createHash('md5').update(version.join('.')).digest();
223
+
224
+ const deviceProps = encodeDeviceProps({
225
+ os: (opts.browser && opts.browser[0]) || 'Ubuntu',
226
+ browserName: (opts.browser && opts.browser[1]) || 'Chrome',
227
+ requireFullSync: !!opts.syncFullHistory,
228
+ historySync: opts.historySync
229
+ });
230
+
231
+ const pairing = encodeDevicePairingData({
232
+ buildHash,
233
+ deviceProps,
234
+ eRegid: encodeBigEndian(opts.registrationId, 4),
235
+ eKeytype: Buffer.from([5]),
236
+ eIdent: stripKeyPrefix(opts.identityKeyPair.public),
237
+ eSkeyId: encodeBigEndian(opts.signedPreKey.id, 3),
238
+ eSkeyVal: stripKeyPrefix(opts.signedPreKey.public),
239
+ eSkeySig: opts.signedPreKey.signature
240
+ });
241
+
242
+ return Buffer.concat([
243
+ ...commonWebPayload(opts),
244
+ boolField(3, false), // passive
245
+ boolField(33, false), // pull
246
+ bytesField(19, pairing) // devicePairingData
247
+ ]);
248
+ }
249
+
250
+ /**
251
+ * Every connection after pairing. `username` is the account's phone number and
252
+ * `device` is the index the server assigned us; together they form the JID the
253
+ * primary lists under Linked Devices.
254
+ */
255
+ function encodeCompanionLoginPayload(opts) {
256
+ return Buffer.concat([
257
+ uint64Field(1, opts.username),
258
+ ...commonWebPayload(opts),
259
+ boolField(3, true), // passive
260
+ boolField(33, true), // pull
261
+ varintField(18, opts.device || 0)
262
+ ]);
263
+ }
264
+
265
+ module.exports = {
266
+ encodeCompanionRegisterPayload,
267
+ encodeCompanionLoginPayload,
268
+ encodeDeviceProps,
269
+ encodeHistorySyncConfig,
270
+ encodeBigEndian,
271
+ platformTypeFor,
272
+ PLATFORM_TYPE
273
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.5.90",
3
+ "version": "5.6.2",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API no web",
5
5
  "author": "Kunboruto50",
6
6
  "main": "index.js",
@@ -47,7 +47,8 @@
47
47
  "dotenv": "^16.4.5",
48
48
  "pino": "^9.0.0",
49
49
  "protobufjs": "^6.11.4",
50
- "uuid": "^11.1.0"
50
+ "uuid": "^11.1.0",
51
+ "ws": "^8.21.1"
51
52
  },
52
53
  "optionalDependencies": {
53
54
  "jimp": "^1.6.0"