@badzz88/baileys 8.5.4 → 8.5.5
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 +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
|
@@ -7,48 +7,24 @@ exports.encodeSignedDeviceIdentity =
|
|
|
7
7
|
void 0
|
|
8
8
|
const boom_1 = require('@hapi/boom')
|
|
9
9
|
const crypto_1 = require('crypto')
|
|
10
|
-
const
|
|
10
|
+
const Crypto_1 = require('./crypto')
|
|
11
11
|
const index_js_1 = require('../../WAProto/index.js')
|
|
12
12
|
const Defaults_1 = require('../Defaults')
|
|
13
13
|
const WABinary_1 = require('../WABinary')
|
|
14
14
|
const crypto_2 = require('./crypto')
|
|
15
15
|
const generics_1 = require('./generics')
|
|
16
16
|
const signal_1 = require('./signal')
|
|
17
|
-
const MOBILE_PLATFORMS = new Set(['Android', 'iOS'])
|
|
18
17
|
const getUserAgent = config => {
|
|
19
|
-
const os = config.browser[0]
|
|
20
|
-
let platform, device, osVersion
|
|
21
|
-
if (os === 'Android') {
|
|
22
|
-
platform = index_js_1.proto.ClientPayload.UserAgent.Platform.ANDROID
|
|
23
|
-
device = 'Android'
|
|
24
|
-
osVersion = config.browser[2] || '15.0.0'
|
|
25
|
-
} else if (os === 'iOS') {
|
|
26
|
-
platform = index_js_1.proto.ClientPayload.UserAgent.Platform.IOS
|
|
27
|
-
device = 'iPhone'
|
|
28
|
-
osVersion = config.browser[2] || '18.0'
|
|
29
|
-
} else if (config.syncFullHistory && os === 'Mac OS' && config.browser[1] === 'Desktop') {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
platform = index_js_1.proto.ClientPayload.UserAgent.Platform.MACOS
|
|
33
|
-
device = 'Desktop'
|
|
34
|
-
osVersion = '0.1'
|
|
35
|
-
} else {
|
|
36
|
-
platform = index_js_1.proto.ClientPayload.UserAgent.Platform.WEB
|
|
37
|
-
device = 'Desktop'
|
|
38
|
-
osVersion = '0.1'
|
|
39
|
-
}
|
|
40
18
|
return {
|
|
41
19
|
appVersion: {
|
|
42
20
|
primary: config.version[0],
|
|
43
21
|
secondary: config.version[1],
|
|
44
22
|
tertiary: config.version[2]
|
|
45
23
|
},
|
|
46
|
-
platform,
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
platform: index_js_1.proto.ClientPayload.UserAgent.Platform.WEB,
|
|
49
25
|
releaseChannel: index_js_1.proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
|
50
|
-
osVersion,
|
|
51
|
-
device,
|
|
26
|
+
osVersion: '0.1',
|
|
27
|
+
device: 'Desktop',
|
|
52
28
|
osBuildNumber: '0.1',
|
|
53
29
|
localeLanguageIso6391: 'en',
|
|
54
30
|
mnc: '000',
|
|
@@ -58,9 +34,7 @@ const getUserAgent = config => {
|
|
|
58
34
|
}
|
|
59
35
|
const PLATFORM_MAP = {
|
|
60
36
|
'Mac OS': index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
|
|
61
|
-
Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.
|
|
62
|
-
Android: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER,
|
|
63
|
-
iOS: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
|
|
37
|
+
Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
|
|
64
38
|
}
|
|
65
39
|
const getWebInfo = config => {
|
|
66
40
|
let webSubPlatform = index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
|
|
@@ -75,16 +49,14 @@ const getClientPayload = config => {
|
|
|
75
49
|
connectReason: index_js_1.proto.ClientPayload.ConnectReason.USER_ACTIVATED,
|
|
76
50
|
userAgent: getUserAgent(config)
|
|
77
51
|
}
|
|
78
|
-
|
|
79
|
-
payload.webInfo = getWebInfo(config)
|
|
80
|
-
}
|
|
52
|
+
payload.webInfo = getWebInfo(config)
|
|
81
53
|
return payload
|
|
82
54
|
}
|
|
83
55
|
const generateLoginNode = (userJid, config, creds) => {
|
|
84
56
|
const { user, device } = (0, WABinary_1.jidDecode)(userJid)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
57
|
+
// masqueradeAsPrimary: connect as device 0 so the WA server (and interop bridges)
|
|
58
|
+
// route primary-device traffic — including interop messages — to this session.
|
|
59
|
+
// Trade-off: the real phone (device 0 on TCP) may be kicked or see a conflict.
|
|
88
60
|
const effectiveDevice = config.masqueradeAsPrimary ? 0 : device
|
|
89
61
|
const payload = {
|
|
90
62
|
...getClientPayload(config),
|
|
@@ -94,8 +66,8 @@ const generateLoginNode = (userJid, config, creds) => {
|
|
|
94
66
|
device: effectiveDevice,
|
|
95
67
|
lidDbMigrated: false
|
|
96
68
|
}
|
|
97
|
-
|
|
98
|
-
|
|
69
|
+
// InteropData is required for Meta/FB-linked accounts (AccountType = HOSTED).
|
|
70
|
+
// Populated from creds when present so the server routes interop traffic correctly.
|
|
99
71
|
if (creds?.interopData?.accountId) {
|
|
100
72
|
payload.interopData = {
|
|
101
73
|
accountId: creds.interopData.accountId,
|
|
@@ -111,23 +83,18 @@ const getPlatformType = platform => {
|
|
|
111
83
|
return index_js_1.proto.DeviceProps.PlatformType[platformType] || index_js_1.proto.DeviceProps.PlatformType.CHROME
|
|
112
84
|
}
|
|
113
85
|
const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const appVersionBuf = Buffer.from(
|
|
86
|
+
// the app version needs to be md5 hashed
|
|
87
|
+
// and passed in
|
|
88
|
+
const appVersionBuf = Buffer.from(Crypto_1.md5(Buffer.from(config.version.join('.'))))
|
|
117
89
|
const companion = {
|
|
118
90
|
os: config.browser[0],
|
|
119
91
|
platformType: getPlatformType(config.browser[1]),
|
|
120
92
|
requireFullSync: config.syncFullHistory,
|
|
121
93
|
historySyncConfig: {
|
|
122
|
-
fullSyncDaysLimit: 30,
|
|
123
|
-
fullSyncSizeMbLimit: undefined,
|
|
124
94
|
storageQuotaMb: 10240,
|
|
125
95
|
inlineInitialPayloadInE2EeMsg: true,
|
|
126
96
|
recentSyncDaysLimit: undefined,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
supportCallLogHistory: config.browser[0] === 'Windows',
|
|
97
|
+
supportCallLogHistory: false,
|
|
131
98
|
supportBotUserAgentChatHistory: true,
|
|
132
99
|
supportCagReactionsAndPolls: true,
|
|
133
100
|
supportBizHostedMsg: true,
|
|
@@ -136,16 +103,9 @@ const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentity
|
|
|
136
103
|
supportFbidBotChatHistory: true,
|
|
137
104
|
supportAddOnHistorySyncMigration: undefined,
|
|
138
105
|
supportMessageAssociation: true,
|
|
139
|
-
supportGroupHistory:
|
|
106
|
+
supportGroupHistory: false,
|
|
140
107
|
onDemandReady: undefined,
|
|
141
|
-
supportGuestChat:
|
|
142
|
-
completeOnDemandReady: undefined,
|
|
143
|
-
thumbnailSyncDaysLimit: undefined,
|
|
144
|
-
initialSyncMaxMessagesPerChat: undefined,
|
|
145
|
-
supportManusHistory: true,
|
|
146
|
-
supportHatchHistory: true,
|
|
147
|
-
supportedBotChannelFbids: undefined,
|
|
148
|
-
supportInlineContacts: true
|
|
108
|
+
supportGuestChat: undefined
|
|
149
109
|
},
|
|
150
110
|
version: {
|
|
151
111
|
primary: 10,
|
|
@@ -249,8 +209,8 @@ const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, s
|
|
|
249
209
|
exports.configureSuccessfulPairing = configureSuccessfulPairing
|
|
250
210
|
const encodeSignedDeviceIdentity = (account, includeSignatureKey) => {
|
|
251
211
|
account = { ...account }
|
|
252
|
-
|
|
253
|
-
|
|
212
|
+
// set to null if we are not to include the signature key
|
|
213
|
+
// or if we are including the signature key but it is empty
|
|
254
214
|
if (!includeSignatureKey || !account.accountSignatureKey?.length) {
|
|
255
215
|
account.accountSignatureKey = null
|
|
256
216
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface AutoCacheViewOnceOptions {
|
|
2
|
+
cacheDir?: string
|
|
3
|
+
logger?: any
|
|
4
|
+
onCached?: (info: { id: string; jid: string; filePath: string; type: string }) => void
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getViewOnceContent(message: any): Record<string, any> | null
|
|
8
|
+
|
|
9
|
+
export function autoCacheViewOnceMedia(sock: any, options?: AutoCacheViewOnceOptions): () => void
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.autoCacheViewOnceMedia = void 0
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const Messages_1 = require('./messages')
|
|
7
|
+
const MessagesMedia_1 = require('./messages-media')
|
|
8
|
+
|
|
9
|
+
const VIEW_ONCE_WRAPPER_KEYS = ['viewOnceMessage', 'viewOnceMessageV2', 'viewOnceMessageV2Extension']
|
|
10
|
+
const VIEW_ONCE_INNER_KEYS = ['imageMessage', 'videoMessage', 'audioMessage']
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Unwraps a message and returns the inner view-once media content if present, otherwise null.
|
|
14
|
+
* Handles both the wrapper style (viewOnceMessage[V2/V2Extension] -> message -> ...)
|
|
15
|
+
* and the inline style (imageMessage/videoMessage with viewOnce: true set directly).
|
|
16
|
+
*/
|
|
17
|
+
const getViewOnceContent = message => {
|
|
18
|
+
if (!message) return null
|
|
19
|
+
const content = (0, Messages_1.extractMessageContent)(message)
|
|
20
|
+
if (!content) return null
|
|
21
|
+
for (const wrapperKey of VIEW_ONCE_WRAPPER_KEYS) {
|
|
22
|
+
const wrapper = content[wrapperKey]
|
|
23
|
+
if (wrapper?.message) {
|
|
24
|
+
const inner = (0, Messages_1.extractMessageContent)(wrapper.message)
|
|
25
|
+
if (inner) return inner
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (const key of VIEW_ONCE_INNER_KEYS) {
|
|
29
|
+
if (content[key]?.viewOnce) {
|
|
30
|
+
return { [key]: content[key] }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Registers a `messages.upsert` listener on the given socket that automatically
|
|
38
|
+
* downloads and saves view-once media (image/video/audio) to `cacheDir` as soon
|
|
39
|
+
* as it arrives, before the sender's app can mark it as opened/consumed.
|
|
40
|
+
*
|
|
41
|
+
* @param {ReturnType<typeof import('../Socket').default>} sock - an active Baileys socket
|
|
42
|
+
* @param {{ cacheDir?: string, logger?: any, onCached?: (info: { id: string, jid: string, filePath: string, type: string }) => void }} [options]
|
|
43
|
+
* @returns {() => void} a function that removes the listener
|
|
44
|
+
*/
|
|
45
|
+
const autoCacheViewOnceMedia = (sock, options = {}) => {
|
|
46
|
+
const cacheDir = options.cacheDir || './viewonce-cache'
|
|
47
|
+
const logger = options.logger || sock.logger
|
|
48
|
+
if (!fs.existsSync(cacheDir)) {
|
|
49
|
+
fs.mkdirSync(cacheDir, { recursive: true })
|
|
50
|
+
}
|
|
51
|
+
const handler = async ({ messages, type }) => {
|
|
52
|
+
if (type !== 'notify' && type !== 'append') return
|
|
53
|
+
for (const msg of messages) {
|
|
54
|
+
try {
|
|
55
|
+
const viewOnceContent = getViewOnceContent(msg.message)
|
|
56
|
+
if (!viewOnceContent) continue
|
|
57
|
+
const mediaType = Object.keys(viewOnceContent)[0]
|
|
58
|
+
const buffer = await (0, Messages_1.downloadMediaMessage)(
|
|
59
|
+
msg,
|
|
60
|
+
'buffer',
|
|
61
|
+
{},
|
|
62
|
+
{ logger, reuploadRequest: sock.updateMediaMessage }
|
|
63
|
+
)
|
|
64
|
+
const extension = (0, MessagesMedia_1.extensionForMediaMessage)(viewOnceContent) || 'bin'
|
|
65
|
+
const fileName = `${msg.key.id}.${extension}`
|
|
66
|
+
const filePath = path.join(cacheDir, fileName)
|
|
67
|
+
fs.writeFileSync(filePath, buffer)
|
|
68
|
+
logger?.debug?.({ id: msg.key.id, filePath, mediaType }, 'cached view-once media')
|
|
69
|
+
options.onCached?.({ id: msg.key.id, jid: msg.key.remoteJid, filePath, type: mediaType })
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger?.warn?.({ error, id: msg.key?.id }, 'failed to cache view-once media')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
sock.ev.on('messages.upsert', handler)
|
|
76
|
+
return () => sock.ev.off('messages.upsert', handler)
|
|
77
|
+
}
|
|
78
|
+
exports.autoCacheViewOnceMedia = autoCacheViewOnceMedia
|
|
79
|
+
exports.getViewOnceContent = getViewOnceContent
|
package/lib/WABinary/decode.js
CHANGED
|
@@ -56,37 +56,17 @@ const util_1 = require('util')
|
|
|
56
56
|
const zlib_1 = require('zlib')
|
|
57
57
|
const constants = __importStar(require('./constants'))
|
|
58
58
|
const jid_utils_1 = require('./jid-utils')
|
|
59
|
-
const rb = require('whatsapp-rust-bridge')
|
|
60
59
|
const inflatePromise = (0, util_1.promisify)(zlib_1.inflate)
|
|
61
60
|
const decompressingIfRequired = async buffer => {
|
|
62
61
|
if (2 & buffer.readUInt8()) {
|
|
63
62
|
buffer = await inflatePromise(buffer.slice(1))
|
|
64
63
|
} else {
|
|
65
|
-
|
|
64
|
+
// nodes with no compression have a 0x00 prefix, we remove that
|
|
66
65
|
buffer = buffer.slice(1)
|
|
67
66
|
}
|
|
68
67
|
return buffer
|
|
69
68
|
}
|
|
70
69
|
exports.decompressingIfRequired = decompressingIfRequired
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const rustNodeToJs = node => {
|
|
74
|
-
if (!node || typeof node !== 'object') return node
|
|
75
|
-
const result = { tag: node.tag, attrs: node.attrs || {} }
|
|
76
|
-
const content = node.content
|
|
77
|
-
if (content === undefined || content === null) {
|
|
78
|
-
|
|
79
|
-
} else if (content instanceof Uint8Array) {
|
|
80
|
-
result.content = Buffer.from(content)
|
|
81
|
-
} else if (typeof content === 'string') {
|
|
82
|
-
result.content = content
|
|
83
|
-
} else if (Array.isArray(content)) {
|
|
84
|
-
result.content = content.map(rustNodeToJs)
|
|
85
|
-
} else {
|
|
86
|
-
result.content = content
|
|
87
|
-
}
|
|
88
|
-
return result
|
|
89
|
-
}
|
|
90
70
|
const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) => {
|
|
91
71
|
const { DOUBLE_BYTE_TOKENS, SINGLE_BYTE_TOKENS, TAGS } = opts
|
|
92
72
|
const checkEOS = length => {
|
|
@@ -110,7 +90,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
110
90
|
return value
|
|
111
91
|
}
|
|
112
92
|
const readStringFromChars = length => {
|
|
113
|
-
return readBytes(length).toString('
|
|
93
|
+
return readBytes(length).toString('utf-8')
|
|
114
94
|
}
|
|
115
95
|
const readInt = (n, littleEndian = false) => {
|
|
116
96
|
checkEOS(n)
|
|
@@ -196,7 +176,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
196
176
|
const domainType = Number(rawDomainType)
|
|
197
177
|
const device = readByte()
|
|
198
178
|
const user = readString(readByte())
|
|
199
|
-
let server = 's.whatsapp.net'
|
|
179
|
+
let server = 's.whatsapp.net' // default whatsapp server
|
|
200
180
|
if (domainType === jid_utils_1.WAJIDDomains.LID) {
|
|
201
181
|
server = 'lid'
|
|
202
182
|
} else if (domainType === jid_utils_1.WAJIDDomains.HOSTED) {
|
|
@@ -217,13 +197,13 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
217
197
|
const device = readInt(2)
|
|
218
198
|
const integrator = readInt(2)
|
|
219
199
|
let server = 'interop'
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
200
|
+
// Only attempt to read the optional server field if at least 1 byte remains
|
|
201
|
+
// and the next byte is a valid string tag (avoids corrupting the stream on
|
|
202
|
+
// unknown tags that readString would swallow silently after a catch).
|
|
223
203
|
if (indexRef.index < buffer.length) {
|
|
224
204
|
const serverTag = buffer[indexRef.index]
|
|
225
|
-
|
|
226
|
-
|
|
205
|
+
// BINARY_8 / BINARY_20 / BINARY_32 or a single-byte token (1–235) are
|
|
206
|
+
// valid string tags; LIST_EMPTY (0) means no server string follows.
|
|
227
207
|
const isValidStringTag =
|
|
228
208
|
(serverTag >= 1 && serverTag <= 235) ||
|
|
229
209
|
serverTag === TAGS.BINARY_8 ||
|
|
@@ -232,7 +212,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
232
212
|
serverTag === TAGS.NIBBLE_8 ||
|
|
233
213
|
serverTag === TAGS.HEX_8
|
|
234
214
|
if (isValidStringTag) {
|
|
235
|
-
indexRef.index++
|
|
215
|
+
indexRef.index++ // consume the tag byte
|
|
236
216
|
server = readString(serverTag)
|
|
237
217
|
}
|
|
238
218
|
}
|
|
@@ -281,7 +261,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
281
261
|
case TAGS.NIBBLE_8:
|
|
282
262
|
return readPacked8(tag)
|
|
283
263
|
default:
|
|
284
|
-
|
|
264
|
+
// Unknown tag — token table may be outdated (new WA protocol version)
|
|
285
265
|
process.stderr.write(`[WABinary] unknown string tag 0x${tag.toString(16)} — token table outdated?\n`)
|
|
286
266
|
return ''
|
|
287
267
|
}
|
|
@@ -297,16 +277,14 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
297
277
|
const getTokenDouble = (index1, index2) => {
|
|
298
278
|
const dict = DOUBLE_BYTE_TOKENS[index1]
|
|
299
279
|
if (!dict) {
|
|
300
|
-
|
|
280
|
+
// Unknown dictionary — token table may be outdated (new WA protocol version)
|
|
301
281
|
process.stderr.write(`[WABinary] unknown double-byte token dict ${index1} — token table outdated?\n`)
|
|
302
282
|
return ''
|
|
303
283
|
}
|
|
304
284
|
const value = dict[index2]
|
|
305
285
|
if (typeof value === 'undefined') {
|
|
306
|
-
|
|
307
|
-
process.stderr.write(
|
|
308
|
-
`[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`
|
|
309
|
-
)
|
|
286
|
+
// Unknown token in known dictionary — token table may be outdated
|
|
287
|
+
process.stderr.write(`[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`)
|
|
310
288
|
return ''
|
|
311
289
|
}
|
|
312
290
|
return value
|
|
@@ -321,7 +299,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
321
299
|
if (listSize === 0 || !header) {
|
|
322
300
|
throw new Error('invalid node')
|
|
323
301
|
}
|
|
324
|
-
|
|
302
|
+
// read the attributes in
|
|
325
303
|
const attributesLength = (listSize - 1) >> 1
|
|
326
304
|
for (let i = 0; i < attributesLength; i++) {
|
|
327
305
|
const key = readString(readByte())
|
|
@@ -358,20 +336,8 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
|
|
|
358
336
|
}
|
|
359
337
|
}
|
|
360
338
|
exports.decodeDecompressedBinaryNode = decodeDecompressedBinaryNode
|
|
361
|
-
const JS_DECODE_OPTS = {
|
|
362
|
-
TAGS: constants.TAGS,
|
|
363
|
-
SINGLE_BYTE_TOKENS: constants.SINGLE_BYTE_TOKENS,
|
|
364
|
-
DOUBLE_BYTE_TOKENS: constants.DOUBLE_BYTE_TOKENS,
|
|
365
|
-
}
|
|
366
339
|
const decodeBinaryNode = buff => {
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
const internal = rb.decodeNode(u8)
|
|
370
|
-
return rustNodeToJs(internal.toJSON())
|
|
371
|
-
} catch (_) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return (0, exports.decodeDecompressedBinaryNode)(buff, JS_DECODE_OPTS)
|
|
375
|
-
}
|
|
340
|
+
const buffer = Buffer.isBuffer(buff) ? buff : Buffer.from(buff)
|
|
341
|
+
return decodeDecompressedBinaryNode(buffer, constants)
|
|
376
342
|
}
|
|
377
343
|
exports.decodeBinaryNode = decodeBinaryNode
|
package/lib/WABinary/encode.js
CHANGED
|
@@ -1,58 +1,228 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
var __createBinding =
|
|
3
|
-
(this && this.__createBinding) ||
|
|
4
|
-
(Object.create
|
|
5
|
-
? function (o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k
|
|
7
|
-
var desc = Object.getOwnPropertyDescriptor(m, k)
|
|
8
|
-
if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
-
desc = {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function () {
|
|
12
|
-
return m[k]
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
Object.defineProperty(o, k2, desc)
|
|
17
|
-
}
|
|
18
|
-
: function (o, m, k, k2) {
|
|
19
|
-
if (k2 === undefined) k2 = k
|
|
20
|
-
o[k2] = m[k]
|
|
21
|
-
})
|
|
22
|
-
var __setModuleDefault =
|
|
23
|
-
(this && this.__setModuleDefault) ||
|
|
24
|
-
(Object.create
|
|
25
|
-
? function (o, v) {
|
|
26
|
-
Object.defineProperty(o, 'default', { enumerable: true, value: v })
|
|
27
|
-
}
|
|
28
|
-
: function (o, v) {
|
|
29
|
-
o['default'] = v
|
|
30
|
-
})
|
|
31
|
-
var __importStar =
|
|
32
|
-
(this && this.__importStar) ||
|
|
33
|
-
(function () {
|
|
34
|
-
var ownKeys = function (o) {
|
|
35
|
-
ownKeys =
|
|
36
|
-
Object.getOwnPropertyNames ||
|
|
37
|
-
function (o) {
|
|
38
|
-
var ar = []
|
|
39
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k
|
|
40
|
-
return ar
|
|
41
|
-
}
|
|
42
|
-
return ownKeys(o)
|
|
43
|
-
}
|
|
44
|
-
return function (mod) {
|
|
45
|
-
if (mod && mod.__esModule) return mod
|
|
46
|
-
var result = {}
|
|
47
|
-
if (mod != null)
|
|
48
|
-
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== 'default') __createBinding(result, mod, k[i])
|
|
49
|
-
__setModuleDefault(result, mod)
|
|
50
|
-
return result
|
|
51
|
-
}
|
|
52
|
-
})()
|
|
53
2
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
54
3
|
exports.encodeBinaryNode = void 0
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
4
|
+
const constants = require('./constants')
|
|
5
|
+
const { jidDecode } = require('./jid-utils')
|
|
6
|
+
|
|
7
|
+
const encodeBinaryNodeJS = (node, opts = constants, buffer = [0]) => {
|
|
8
|
+
const encoded = encodeBinaryNodeInner(node, opts, buffer);
|
|
9
|
+
return Buffer.from(encoded);
|
|
10
|
+
};
|
|
11
|
+
const encodeBinaryNodeInner = ({ tag, attrs, content }, opts, buffer) => {
|
|
12
|
+
const { TAGS, TOKEN_MAP } = opts;
|
|
13
|
+
const pushByte = (value) => buffer.push(value & 0xff);
|
|
14
|
+
const pushInt = (value, n, littleEndian = false) => {
|
|
15
|
+
for (let i = 0; i < n; i++) {
|
|
16
|
+
const curShift = littleEndian ? i : n - 1 - i;
|
|
17
|
+
buffer.push((value >> (curShift * 8)) & 0xff);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const pushBytes = (bytes) => {
|
|
21
|
+
for (const b of bytes) {
|
|
22
|
+
buffer.push(b);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const pushInt16 = (value) => {
|
|
26
|
+
pushBytes([(value >> 8) & 0xff, value & 0xff]);
|
|
27
|
+
};
|
|
28
|
+
const pushInt20 = (value) => pushBytes([(value >> 16) & 0x0f, (value >> 8) & 0xff, value & 0xff]);
|
|
29
|
+
const writeByteLength = (length) => {
|
|
30
|
+
if (length >= 4294967296) {
|
|
31
|
+
throw new Error('string too large to encode: ' + length);
|
|
32
|
+
}
|
|
33
|
+
if (length >= 1 << 20) {
|
|
34
|
+
pushByte(TAGS.BINARY_32);
|
|
35
|
+
pushInt(length, 4); // 32 bit integer
|
|
36
|
+
}
|
|
37
|
+
else if (length >= 256) {
|
|
38
|
+
pushByte(TAGS.BINARY_20);
|
|
39
|
+
pushInt20(length);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
pushByte(TAGS.BINARY_8);
|
|
43
|
+
pushByte(length);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const writeStringRaw = (str) => {
|
|
47
|
+
const bytes = Buffer.from(str, 'utf-8');
|
|
48
|
+
writeByteLength(bytes.length);
|
|
49
|
+
pushBytes(bytes);
|
|
50
|
+
};
|
|
51
|
+
const writeJid = ({ domainType, device, user, server }) => {
|
|
52
|
+
if (typeof device !== 'undefined') {
|
|
53
|
+
pushByte(TAGS.AD_JID);
|
|
54
|
+
pushByte(domainType || 0);
|
|
55
|
+
pushByte(device || 0);
|
|
56
|
+
writeString(user);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
pushByte(TAGS.JID_PAIR);
|
|
60
|
+
if (user.length) {
|
|
61
|
+
writeString(user);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
pushByte(TAGS.LIST_EMPTY);
|
|
65
|
+
}
|
|
66
|
+
writeString(server);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const packNibble = (char) => {
|
|
70
|
+
switch (char) {
|
|
71
|
+
case '-':
|
|
72
|
+
return 10;
|
|
73
|
+
case '.':
|
|
74
|
+
return 11;
|
|
75
|
+
case '\0':
|
|
76
|
+
return 15;
|
|
77
|
+
default:
|
|
78
|
+
if (char >= '0' && char <= '9') {
|
|
79
|
+
return char.charCodeAt(0) - '0'.charCodeAt(0);
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`invalid byte for nibble "${char}"`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const packHex = (char) => {
|
|
85
|
+
if (char >= '0' && char <= '9') {
|
|
86
|
+
return char.charCodeAt(0) - '0'.charCodeAt(0);
|
|
87
|
+
}
|
|
88
|
+
if (char >= 'A' && char <= 'F') {
|
|
89
|
+
return 10 + char.charCodeAt(0) - 'A'.charCodeAt(0);
|
|
90
|
+
}
|
|
91
|
+
if (char >= 'a' && char <= 'f') {
|
|
92
|
+
return 10 + char.charCodeAt(0) - 'a'.charCodeAt(0);
|
|
93
|
+
}
|
|
94
|
+
if (char === '\0') {
|
|
95
|
+
return 15;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Invalid hex char "${char}"`);
|
|
98
|
+
};
|
|
99
|
+
const writePackedBytes = (str, type) => {
|
|
100
|
+
if (str.length > TAGS.PACKED_MAX) {
|
|
101
|
+
throw new Error('Too many bytes to pack');
|
|
102
|
+
}
|
|
103
|
+
pushByte(type === 'nibble' ? TAGS.NIBBLE_8 : TAGS.HEX_8);
|
|
104
|
+
let roundedLength = Math.ceil(str.length / 2.0);
|
|
105
|
+
if (str.length % 2 !== 0) {
|
|
106
|
+
roundedLength |= 128;
|
|
107
|
+
}
|
|
108
|
+
pushByte(roundedLength);
|
|
109
|
+
const packFunction = type === 'nibble' ? packNibble : packHex;
|
|
110
|
+
const packBytePair = (v1, v2) => {
|
|
111
|
+
const result = (packFunction(v1) << 4) | packFunction(v2);
|
|
112
|
+
return result;
|
|
113
|
+
};
|
|
114
|
+
const strLengthHalf = Math.floor(str.length / 2);
|
|
115
|
+
for (let i = 0; i < strLengthHalf; i++) {
|
|
116
|
+
pushByte(packBytePair(str[2 * i], str[2 * i + 1]));
|
|
117
|
+
}
|
|
118
|
+
if (str.length % 2 !== 0) {
|
|
119
|
+
pushByte(packBytePair(str[str.length - 1], '\x00'));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const isNibble = (str) => {
|
|
123
|
+
if (!str || str.length > TAGS.PACKED_MAX) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
for (const char of str) {
|
|
127
|
+
const isInNibbleRange = char >= '0' && char <= '9';
|
|
128
|
+
if (!isInNibbleRange && char !== '-' && char !== '.') {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
};
|
|
134
|
+
const isHex = (str) => {
|
|
135
|
+
if (!str || str.length > TAGS.PACKED_MAX) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
for (const char of str) {
|
|
139
|
+
const isInNibbleRange = char >= '0' && char <= '9';
|
|
140
|
+
if (!isInNibbleRange && !(char >= 'A' && char <= 'F')) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
};
|
|
146
|
+
const writeString = (str) => {
|
|
147
|
+
if (str === undefined || str === null) {
|
|
148
|
+
pushByte(TAGS.LIST_EMPTY);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (str === '') {
|
|
152
|
+
writeStringRaw(str);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const tokenIndex = TOKEN_MAP[str];
|
|
156
|
+
if (tokenIndex) {
|
|
157
|
+
if (typeof tokenIndex.dict === 'number') {
|
|
158
|
+
pushByte(TAGS.DICTIONARY_0 + tokenIndex.dict);
|
|
159
|
+
}
|
|
160
|
+
pushByte(tokenIndex.index);
|
|
161
|
+
}
|
|
162
|
+
else if (isNibble(str)) {
|
|
163
|
+
writePackedBytes(str, 'nibble');
|
|
164
|
+
}
|
|
165
|
+
else if (isHex(str)) {
|
|
166
|
+
writePackedBytes(str, 'hex');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
const decodedJid = jidDecode(str);
|
|
170
|
+
if (decodedJid) {
|
|
171
|
+
writeJid(decodedJid);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
writeStringRaw(str);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const writeListStart = (listSize) => {
|
|
179
|
+
if (listSize === 0) {
|
|
180
|
+
pushByte(TAGS.LIST_EMPTY);
|
|
181
|
+
}
|
|
182
|
+
else if (listSize < 256) {
|
|
183
|
+
pushBytes([TAGS.LIST_8, listSize]);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
pushByte(TAGS.LIST_16);
|
|
187
|
+
pushInt16(listSize);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
if (!tag) {
|
|
191
|
+
throw new Error('Invalid node: tag cannot be undefined');
|
|
192
|
+
}
|
|
193
|
+
const validAttributes = Object.keys(attrs || {}).filter(k => typeof attrs[k] !== 'undefined' && attrs[k] !== null);
|
|
194
|
+
writeListStart(2 * validAttributes.length + 1 + (typeof content !== 'undefined' ? 1 : 0));
|
|
195
|
+
writeString(tag);
|
|
196
|
+
for (const key of validAttributes) {
|
|
197
|
+
if (typeof attrs[key] === 'string') {
|
|
198
|
+
writeString(key);
|
|
199
|
+
writeString(attrs[key]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (typeof content === 'string') {
|
|
203
|
+
writeString(content);
|
|
204
|
+
}
|
|
205
|
+
else if (Buffer.isBuffer(content) || content instanceof Uint8Array) {
|
|
206
|
+
writeByteLength(content.length);
|
|
207
|
+
pushBytes(content);
|
|
208
|
+
}
|
|
209
|
+
else if (Array.isArray(content)) {
|
|
210
|
+
const validContent = content.filter(item => item && (item.tag || Buffer.isBuffer(item) || item instanceof Uint8Array || typeof item === 'string'));
|
|
211
|
+
writeListStart(validContent.length);
|
|
212
|
+
for (const item of validContent) {
|
|
213
|
+
encodeBinaryNodeInner(item, opts, buffer);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (typeof content === 'undefined') {
|
|
217
|
+
// do nothing
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw new Error(`invalid children for header "${tag}": ${content} (${typeof content})`);
|
|
221
|
+
}
|
|
222
|
+
return buffer;
|
|
223
|
+
};
|
|
58
224
|
|
|
225
|
+
const encodeBinaryNode = (node, opts, buffer) => {
|
|
226
|
+
return encodeBinaryNodeJS(node, opts || constants, buffer || [0])
|
|
227
|
+
}
|
|
228
|
+
exports.encodeBinaryNode = encodeBinaryNode
|