@hansaka02/baileys 7.3.2 → 7.3.4
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 +247 -203
- package/lib/Defaults/connection.js +51 -0
- package/lib/Defaults/constants.js +62 -0
- package/lib/Defaults/history.js +17 -0
- package/lib/Defaults/index.js +36 -142
- package/lib/Defaults/media.js +48 -0
- package/lib/Defaults/prefix.js +18 -0
- package/lib/Signal/Group/group-session-builder.js +10 -42
- package/lib/Signal/Group/group_cipher.js +9 -6
- package/lib/Signal/Group/index.js +39 -53
- package/lib/Signal/Group/keyhelper.js +8 -41
- package/lib/Signal/Group/sender-chain-key.js +4 -4
- package/lib/Signal/Group/sender-key-distribution-message.js +5 -5
- package/lib/Signal/Group/sender-key-message.js +12 -8
- package/lib/Signal/Group/sender-key-record.js +7 -7
- package/lib/Signal/Group/sender-key-state.js +4 -4
- package/lib/Signal/Group/sender-message-key.js +2 -2
- package/lib/Signal/libsignal.js +45 -69
- package/lib/Signal/lid-mapping.js +15 -11
- package/lib/Socket/Client/types.js +2 -2
- package/lib/Socket/Client/websocket.js +16 -14
- package/lib/Socket/business.js +41 -32
- package/lib/Socket/chats.js +123 -98
- package/lib/Socket/community.js +50 -40
- package/lib/Socket/groups.js +59 -47
- package/lib/Socket/index.js +4 -4
- package/lib/Socket/messages-recv.js +219 -172
- package/lib/Socket/messages-send.js +187 -143
- package/lib/Socket/newsletter.js +61 -47
- package/lib/Socket/socket.js +133 -90
- package/lib/Socket/usync.js +6 -6
- package/lib/Store/index.js +27 -11
- package/lib/Store/make-cache-manager-store.js +14 -15
- package/lib/Store/make-in-memory-store.js +28 -24
- package/lib/Types/LabelAssociation.js +2 -2
- package/lib/Types/Message.js +6 -6
- package/lib/Types/MexUpdates.js +5 -5
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +28 -12
- package/lib/Utils/auth-utils.js +28 -26
- package/lib/Utils/baileys-event-stream.js +68 -69
- package/lib/Utils/business.js +63 -53
- package/lib/Utils/chat-utils.js +81 -71
- package/lib/Utils/crypto.js +25 -45
- package/lib/Utils/decode-wa-message.js +319 -311
- package/lib/Utils/event-buffer.js +21 -22
- package/lib/Utils/generics.js +65 -82
- package/lib/Utils/history.js +21 -21
- package/lib/Utils/index.js +27 -13
- package/lib/Utils/link-preview.js +7 -30
- package/lib/Utils/logger.js +5 -5
- package/lib/Utils/lt-hash.js +3 -3
- package/lib/Utils/message-retry-manager.js +4 -4
- package/lib/Utils/messages-media.js +104 -109
- package/lib/Utils/messages.js +203 -171
- package/lib/Utils/noise-handler.js +28 -19
- package/lib/Utils/process-message.js +111 -96
- package/lib/Utils/signal.js +36 -25
- package/lib/Utils/use-multi-file-auth-state.js +18 -22
- package/lib/Utils/validate-connection.js +52 -45
- package/lib/WABinary/decode.js +6 -32
- package/lib/WABinary/encode.js +3 -29
- package/lib/WABinary/generic-utils.js +4 -4
- package/lib/WABinary/index.js +27 -11
- package/lib/WAM/encode.js +16 -8
- package/lib/WAM/index.js +27 -11
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
- package/lib/WAUSync/Protocols/index.js +27 -11
- package/lib/WAUSync/USyncQuery.js +17 -10
- package/lib/WAUSync/index.js +27 -11
- package/lib/index.js +60 -31
- package/package.json +8 -14
- package/WAProto/AICommon/AICommon.d.ts +0 -11702
- package/WAProto/Adv/Adv.d.ts +0 -643
- package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
- package/WAProto/Cert/Cert.d.ts +0 -613
- package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
- package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
- package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
- package/WAProto/E2E/E2E.d.ts +0 -41724
- package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
- package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
- package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
- package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
- package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
- package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
- package/WAProto/Protocol/Protocol.d.ts +0 -270
- package/WAProto/Reporting/Reporting.d.ts +0 -371
- package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
- package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
- package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
- package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
- package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
- package/WAProto/UserPassword/UserPassword.d.ts +0 -363
- package/WAProto/VnameCert/VnameCert.d.ts +0 -821
- package/WAProto/Wa6/Wa6.d.ts +0 -2128
- package/WAProto/Web/Web.d.ts +0 -46383
- package/WAProto/index.d.ts +0 -55
- package/lib/Defaults/index.d.ts +0 -77
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -17
- package/lib/Signal/Group/group_cipher.d.ts +0 -19
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -16
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
- package/lib/Signal/Group/sender-key-message.d.ts +0 -19
- package/lib/Signal/Group/sender-key-name.d.ts +0 -19
- package/lib/Signal/Group/sender-key-record.d.ts +0 -32
- package/lib/Signal/Group/sender-key-state.d.ts +0 -44
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -8
- package/lib/Signal/lid-mapping.d.ts +0 -28
- package/lib/Socket/Client/index.d.ts +0 -2
- package/lib/Socket/Client/types.d.ts +0 -16
- package/lib/Socket/Client/websocket.d.ts +0 -13
- package/lib/Socket/business.d.ts +0 -187
- package/lib/Socket/chats.d.ts +0 -97
- package/lib/Socket/community.d.ts +0 -129
- package/lib/Socket/groups.d.ts +0 -129
- package/lib/Socket/index.d.ts +0 -191
- package/lib/Socket/messages-recv.d.ts +0 -174
- package/lib/Socket/messages-send.d.ts +0 -165
- package/lib/Socket/newsletter.d.ts +0 -145
- package/lib/Socket/socket.d.ts +0 -45
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Store/index.d.ts +0 -4
- package/lib/Store/make-cache-manager-store.d.ts +0 -14
- package/lib/Store/make-in-memory-store.d.ts +0 -123
- package/lib/Store/make-ordered-dictionary.d.ts +0 -12
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -121
- package/lib/Types/Bussiness.d.ts +0 -28
- package/lib/Types/Call.d.ts +0 -14
- package/lib/Types/Chat.d.ts +0 -143
- package/lib/Types/Contact.d.ts +0 -23
- package/lib/Types/Events.d.ts +0 -226
- package/lib/Types/GroupMetadata.d.ts +0 -66
- package/lib/Types/Label.d.ts +0 -48
- package/lib/Types/LabelAssociation.d.ts +0 -35
- package/lib/Types/Message.d.ts +0 -484
- package/lib/Types/MexUpdates.d.ts +0 -9
- package/lib/Types/Newsletter.d.ts +0 -109
- package/lib/Types/Product.d.ts +0 -92
- package/lib/Types/Signal.d.ts +0 -98
- package/lib/Types/Socket.d.ts +0 -141
- package/lib/Types/State.d.ts +0 -41
- package/lib/Types/USync.d.ts +0 -26
- package/lib/Types/index.d.ts +0 -80
- package/lib/Utils/auth-utils.d.ts +0 -21
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/business.d.ts +0 -29
- package/lib/Utils/chat-utils.d.ts +0 -82
- package/lib/Utils/crypto.d.ts +0 -56
- package/lib/Utils/decode-wa-message.d.ts +0 -53
- package/lib/Utils/event-buffer.d.ts +0 -39
- package/lib/Utils/generics.d.ts +0 -117
- package/lib/Utils/history.d.ts +0 -23
- package/lib/Utils/index.d.ts +0 -20
- package/lib/Utils/link-preview.d.ts +0 -23
- package/lib/Utils/logger.d.ts +0 -13
- package/lib/Utils/lt-hash.d.ts +0 -14
- package/lib/Utils/make-mutex.d.ts +0 -9
- package/lib/Utils/message-retry-manager.d.ts +0 -88
- package/lib/Utils/messages-media.d.ts +0 -135
- package/lib/Utils/messages.d.ts +0 -105
- package/lib/Utils/noise-handler.d.ts +0 -20
- package/lib/Utils/process-message.d.ts +0 -49
- package/lib/Utils/signal.d.ts +0 -42
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
- package/lib/Utils/validate-connection.d.ts +0 -13
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -9
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -28
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -58
- package/lib/WABinary/types.d.ts +0 -22
- package/lib/WAM/BinaryInfo.d.ts +0 -16
- package/lib/WAM/constants.d.ts +0 -47
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/index.d.ts +0 -6
- package/lib/WAUSync/USyncQuery.d.ts +0 -31
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -13
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const { LRUCache } = require("lru-cache")
|
|
6
6
|
|
|
7
7
|
/** Number of sent messages to cache in memory for handling retry receipts */
|
|
8
8
|
const RECENT_MESSAGES_SIZE = 512
|
|
@@ -15,14 +15,14 @@ const PHONE_REQUEST_DELAY = 3000
|
|
|
15
15
|
class MessageRetryManager {
|
|
16
16
|
constructor(logger, maxMsgRetryCount) {
|
|
17
17
|
this.logger = logger
|
|
18
|
-
this.recentMessagesMap = new
|
|
18
|
+
this.recentMessagesMap = new LRUCache({
|
|
19
19
|
max: RECENT_MESSAGES_SIZE
|
|
20
20
|
})
|
|
21
|
-
this.sessionRecreateHistory = new
|
|
21
|
+
this.sessionRecreateHistory = new LRUCache({
|
|
22
22
|
ttl: RECREATE_SESSION_TIMEOUT * 2,
|
|
23
23
|
ttlAutopurge: true
|
|
24
24
|
})
|
|
25
|
-
this.retryCounters = new
|
|
25
|
+
this.retryCounters = new LRUCache({
|
|
26
26
|
ttl: 15 * 60 * 1000,
|
|
27
27
|
ttlAutopurge: true,
|
|
28
28
|
updateAgeOnGet: true
|
|
@@ -1,51 +1,46 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k)
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k] } }
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc)
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k
|
|
12
|
-
o[k2] = m[k]
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v })
|
|
17
|
-
}) : function(o, v) {
|
|
18
|
-
o["default"] = v
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
-
if (mod && mod.__esModule) return mod
|
|
23
|
-
var result = {}
|
|
24
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k)
|
|
25
|
-
__setModuleDefault(result, mod)
|
|
26
|
-
return result
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
-
return (mod && mod.__esModule) ? mod : { "default": mod }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
34
4
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
5
|
+
const { Boom } = require("@hapi/boom")
|
|
6
|
+
const { spawn } = require("child_process")
|
|
7
|
+
const { once } = require("events")
|
|
8
|
+
const {
|
|
9
|
+
createHash,
|
|
10
|
+
randomBytes,
|
|
11
|
+
createHmac,
|
|
12
|
+
createCipheriv,
|
|
13
|
+
createDecipheriv
|
|
14
|
+
} = require("crypto")
|
|
15
|
+
const {
|
|
16
|
+
promises,
|
|
17
|
+
createReadStream,
|
|
18
|
+
createWriteStream
|
|
19
|
+
} = require("fs")
|
|
20
|
+
const { tmpdir } = require("os")
|
|
21
|
+
const { join } = require("path")
|
|
22
|
+
const {
|
|
23
|
+
Readable,
|
|
24
|
+
Transform
|
|
25
|
+
} = require("stream")
|
|
26
|
+
const axios_1 = require("axios")
|
|
27
|
+
const { proto } = require("../../WAProto")
|
|
28
|
+
const {
|
|
29
|
+
MEDIA_PATH_MAP,
|
|
30
|
+
MEDIA_HKDF_KEY_MAPPING
|
|
31
|
+
} = require("../Defaults/media")
|
|
32
|
+
const { DEFAULT_ORIGIN } = require("../Defaults/constants")
|
|
33
|
+
const {
|
|
34
|
+
getBinaryNodeChild,
|
|
35
|
+
getBinaryNodeChildBuffer,
|
|
36
|
+
jidNormalizedUser
|
|
37
|
+
} = require("../WABinary")
|
|
38
|
+
const {
|
|
39
|
+
aesDecryptGCM,
|
|
40
|
+
aesEncryptGCM,
|
|
41
|
+
hkdf
|
|
42
|
+
} = require("./crypto")
|
|
43
|
+
const { generateMessageID } = require("./generics")
|
|
49
44
|
|
|
50
45
|
const getImageProcessingLibrary = async () => {
|
|
51
46
|
const [_jimp, sharp] = await Promise.all([
|
|
@@ -65,11 +60,11 @@ const getImageProcessingLibrary = async () => {
|
|
|
65
60
|
if (jimp) {
|
|
66
61
|
return { jimp }
|
|
67
62
|
}
|
|
68
|
-
throw new
|
|
63
|
+
throw new Boom('No image processing library available')
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
const hkdfInfoKey = (type) => {
|
|
72
|
-
const hkdfInfo =
|
|
67
|
+
const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type]
|
|
73
68
|
return `WhatsApp ${hkdfInfo} Keys`
|
|
74
69
|
}
|
|
75
70
|
|
|
@@ -78,9 +73,9 @@ const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
|
78
73
|
|
|
79
74
|
logger?.debug('got stream for raw upload')
|
|
80
75
|
|
|
81
|
-
const hasher =
|
|
82
|
-
const filePath =
|
|
83
|
-
const fileWriteStream =
|
|
76
|
+
const hasher = createHash('sha256')
|
|
77
|
+
const filePath = join(tmpdir(), mediaType + generateMessageID())
|
|
78
|
+
const fileWriteStream = createWriteStream(filePath)
|
|
84
79
|
|
|
85
80
|
let fileLength = 0
|
|
86
81
|
|
|
@@ -90,12 +85,12 @@ const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
|
90
85
|
hasher.update(data)
|
|
91
86
|
|
|
92
87
|
if (!fileWriteStream.write(data)) {
|
|
93
|
-
await
|
|
88
|
+
await once(fileWriteStream, 'drain')
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
91
|
|
|
97
92
|
fileWriteStream.end()
|
|
98
|
-
await
|
|
93
|
+
await once(fileWriteStream, 'finish')
|
|
99
94
|
stream.destroy()
|
|
100
95
|
|
|
101
96
|
const fileSha256 = hasher.digest()
|
|
@@ -113,7 +108,7 @@ const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
|
113
108
|
stream.destroy()
|
|
114
109
|
|
|
115
110
|
try {
|
|
116
|
-
await
|
|
111
|
+
await promises.unlink(filePath)
|
|
117
112
|
}
|
|
118
113
|
catch {
|
|
119
114
|
//
|
|
@@ -125,13 +120,13 @@ const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
|
125
120
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
126
121
|
async function getMediaKeys(buffer, mediaType) {
|
|
127
122
|
if (!buffer) {
|
|
128
|
-
throw new
|
|
123
|
+
throw new Boom('Cannot derive from empty media key')
|
|
129
124
|
}
|
|
130
125
|
if (typeof buffer === 'string') {
|
|
131
126
|
buffer = Buffer.from(buffer.replace('data:base64,', ''), 'base64')
|
|
132
127
|
}
|
|
133
128
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
134
|
-
const expandedMediaKey = await
|
|
129
|
+
const expandedMediaKey = await hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
|
|
135
130
|
return {
|
|
136
131
|
iv: expandedMediaKey.slice(0, 16),
|
|
137
132
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
@@ -153,7 +148,7 @@ const extractVideoThumb = (videoPath, time = '00:00:00', size = { width: 320 })
|
|
|
153
148
|
'pipe:1'
|
|
154
149
|
]
|
|
155
150
|
|
|
156
|
-
const ffmpeg =
|
|
151
|
+
const ffmpeg = spawn('ffmpeg', args)
|
|
157
152
|
const chunks = []
|
|
158
153
|
let errorOutput = ''
|
|
159
154
|
|
|
@@ -174,7 +169,7 @@ const extractImageThumb = async (bufferOrFilePath, width = 32, quality = 50) =>
|
|
|
174
169
|
const response = await axios_1.default.get(bufferOrFilePath, { responseType: "arraybuffer" })
|
|
175
170
|
bufferOrFilePath = Buffer.from(response.data)
|
|
176
171
|
}
|
|
177
|
-
if (bufferOrFilePath instanceof
|
|
172
|
+
if (bufferOrFilePath instanceof Readable) {
|
|
178
173
|
bufferOrFilePath = await toBuffer(bufferOrFilePath)
|
|
179
174
|
}
|
|
180
175
|
const lib = await getImageProcessingLibrary()
|
|
@@ -215,7 +210,7 @@ const extractImageThumb = async (bufferOrFilePath, width = 32, quality = 50) =>
|
|
|
215
210
|
}
|
|
216
211
|
}
|
|
217
212
|
else {
|
|
218
|
-
throw new
|
|
213
|
+
throw new Boom('No image processing library available')
|
|
219
214
|
}
|
|
220
215
|
}
|
|
221
216
|
|
|
@@ -254,7 +249,7 @@ const generateProfilePicture = async (mediaUpload) => {
|
|
|
254
249
|
img = await cropped.scaleToFit(720, 720).getBufferAsync(MIME_JPEG)
|
|
255
250
|
}
|
|
256
251
|
else {
|
|
257
|
-
throw new
|
|
252
|
+
throw new Boom('No image processing library available')
|
|
258
253
|
}
|
|
259
254
|
return {
|
|
260
255
|
img: await img,
|
|
@@ -299,7 +294,7 @@ async function getAudioWaveform(buffer, logger) {
|
|
|
299
294
|
audioData = buffer
|
|
300
295
|
}
|
|
301
296
|
else if (typeof buffer === 'string') {
|
|
302
|
-
const rStream =
|
|
297
|
+
const rStream = createReadStream(buffer)
|
|
303
298
|
audioData = await toBuffer(rStream)
|
|
304
299
|
}
|
|
305
300
|
else {
|
|
@@ -331,7 +326,7 @@ async function getAudioWaveform(buffer, logger) {
|
|
|
331
326
|
}
|
|
332
327
|
|
|
333
328
|
const toReadable = (buffer) => {
|
|
334
|
-
const readable = new
|
|
329
|
+
const readable = new Readable({ read: () => { } })
|
|
335
330
|
readable.push(buffer)
|
|
336
331
|
readable.push(null)
|
|
337
332
|
return readable
|
|
@@ -366,7 +361,7 @@ const getStream = async (item, opts) => {
|
|
|
366
361
|
return { stream: await getHttpStream(item.url, opts), type: 'remote' }
|
|
367
362
|
}
|
|
368
363
|
|
|
369
|
-
return { stream:
|
|
364
|
+
return { stream: createReadStream(item.url), type: 'file' }
|
|
370
365
|
}
|
|
371
366
|
|
|
372
367
|
/** generates a thumbnail for a given media, if required */
|
|
@@ -407,8 +402,8 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
407
402
|
const { stream, type } = await getStream(media, opts)
|
|
408
403
|
logger?.debug('fetched media stream')
|
|
409
404
|
|
|
410
|
-
const encFilePath =
|
|
411
|
-
const encFileWriteStream =
|
|
405
|
+
const encFilePath = join(tmpdir(), mediaType + generateMessageID() + '-plain')
|
|
406
|
+
const encFileWriteStream = createWriteStream(encFilePath)
|
|
412
407
|
|
|
413
408
|
let originalFilePath
|
|
414
409
|
let originalFileStream
|
|
@@ -416,12 +411,12 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
416
411
|
if (type === 'file') {
|
|
417
412
|
originalFilePath = media.url.toString()
|
|
418
413
|
} else if (saveOriginalFileIfRequired) {
|
|
419
|
-
originalFilePath =
|
|
420
|
-
originalFileStream =
|
|
414
|
+
originalFilePath = join(tmpdir(), mediaType + generateMessageID() + '-original')
|
|
415
|
+
originalFileStream = createWriteStream(originalFilePath)
|
|
421
416
|
}
|
|
422
417
|
|
|
423
418
|
let fileLength = 0
|
|
424
|
-
const sha256 =
|
|
419
|
+
const sha256 = createHash('sha256')
|
|
425
420
|
|
|
426
421
|
try {
|
|
427
422
|
for await (const data of stream) {
|
|
@@ -430,7 +425,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
430
425
|
if (type === 'remote'
|
|
431
426
|
&& opts?.maxContentLength
|
|
432
427
|
&& fileLength + data.length > opts.maxContentLength) {
|
|
433
|
-
throw new
|
|
428
|
+
throw new Boom(`content length exceeded when preparing "${type}"`, {
|
|
434
429
|
data: { media, type }
|
|
435
430
|
})
|
|
436
431
|
}
|
|
@@ -439,7 +434,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
439
434
|
encFileWriteStream.write(data)
|
|
440
435
|
|
|
441
436
|
if (originalFileStream && !originalFileStream.write(data)) {
|
|
442
|
-
await
|
|
437
|
+
await once(originalFileStream, 'drain')
|
|
443
438
|
}
|
|
444
439
|
}
|
|
445
440
|
|
|
@@ -466,9 +461,9 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
466
461
|
sha256.destroy()
|
|
467
462
|
stream.destroy()
|
|
468
463
|
try {
|
|
469
|
-
await
|
|
464
|
+
await promises.unlink(encFilePath)
|
|
470
465
|
if (originalFilePath && didSaveToTmpPath) {
|
|
471
|
-
await
|
|
466
|
+
await promises.unlink(originalFilePath)
|
|
472
467
|
}
|
|
473
468
|
} catch (err) {
|
|
474
469
|
logger?.error({ err }, 'failed deleting tmp files')
|
|
@@ -480,21 +475,21 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
480
475
|
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
481
476
|
const { stream, type } = await getStream(media, opts)
|
|
482
477
|
logger?.debug('fetched media stream')
|
|
483
|
-
const mediaKey =
|
|
478
|
+
const mediaKey = randomBytes(32)
|
|
484
479
|
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
|
|
485
|
-
const encFilePath =
|
|
486
|
-
const encFileWriteStream =
|
|
480
|
+
const encFilePath = join(tmpdir(), mediaType + generateMessageID() + '-enc')
|
|
481
|
+
const encFileWriteStream = createWriteStream(encFilePath)
|
|
487
482
|
let originalFileStream
|
|
488
483
|
let originalFilePath
|
|
489
484
|
if (saveOriginalFileIfRequired) {
|
|
490
|
-
originalFilePath =
|
|
491
|
-
originalFileStream =
|
|
485
|
+
originalFilePath = join(tmpdir(), mediaType + generateMessageID() + '-original')
|
|
486
|
+
originalFileStream = createWriteStream(originalFilePath)
|
|
492
487
|
}
|
|
493
488
|
let fileLength = 0
|
|
494
|
-
const aes =
|
|
495
|
-
const hmac =
|
|
496
|
-
const sha256Plain =
|
|
497
|
-
const sha256Enc =
|
|
489
|
+
const aes = createCipheriv('aes-256-cbc', cipherKey, iv)
|
|
490
|
+
const hmac = createHmac('sha256', macKey).update(iv)
|
|
491
|
+
const sha256Plain = createHash('sha256')
|
|
492
|
+
const sha256Enc = createHash('sha256')
|
|
498
493
|
const onChunk = (buff) => {
|
|
499
494
|
sha256Enc.update(buff)
|
|
500
495
|
hmac.update(buff)
|
|
@@ -506,13 +501,13 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
506
501
|
if (type === 'remote'
|
|
507
502
|
&& opts?.maxContentLength
|
|
508
503
|
&& fileLength + data.length > opts.maxContentLength) {
|
|
509
|
-
throw new
|
|
504
|
+
throw new Boom(`content length exceeded when encrypting "${type}"`, {
|
|
510
505
|
data: { media, type }
|
|
511
506
|
})
|
|
512
507
|
}
|
|
513
508
|
if (originalFileStream) {
|
|
514
509
|
if (!originalFileStream.write(data)) {
|
|
515
|
-
await
|
|
510
|
+
await once(originalFileStream, 'drain')
|
|
516
511
|
}
|
|
517
512
|
}
|
|
518
513
|
sha256Plain.update(data)
|
|
@@ -548,9 +543,9 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
548
543
|
sha256Enc.destroy()
|
|
549
544
|
stream.destroy()
|
|
550
545
|
try {
|
|
551
|
-
await
|
|
546
|
+
await promises.unlink(encFilePath)
|
|
552
547
|
if (originalFilePath) {
|
|
553
|
-
await
|
|
548
|
+
await promises.unlink(originalFilePath)
|
|
554
549
|
}
|
|
555
550
|
}
|
|
556
551
|
catch (err) {
|
|
@@ -574,7 +569,7 @@ const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, o
|
|
|
574
569
|
const downloadUrl = isValidMediaUrl ? url : getUrlFromDirectPath(directPath)
|
|
575
570
|
|
|
576
571
|
if (!downloadUrl) {
|
|
577
|
-
throw new
|
|
572
|
+
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 })
|
|
578
573
|
}
|
|
579
574
|
|
|
580
575
|
const keys = await getMediaKeys(mediaKey, type)
|
|
@@ -601,7 +596,7 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
601
596
|
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined
|
|
602
597
|
const headers = {
|
|
603
598
|
...(options?.headers) || {},
|
|
604
|
-
Origin:
|
|
599
|
+
Origin: DEFAULT_ORIGIN
|
|
605
600
|
}
|
|
606
601
|
if (startChunk || endChunk) {
|
|
607
602
|
headers.Range = `bytes=${startChunk}-`
|
|
@@ -629,7 +624,7 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
629
624
|
push(bytes)
|
|
630
625
|
}
|
|
631
626
|
}
|
|
632
|
-
const output = new
|
|
627
|
+
const output = new Transform({
|
|
633
628
|
transform(chunk, _, callback) {
|
|
634
629
|
let data = Buffer.concat([remainingBytes, chunk])
|
|
635
630
|
const decryptLength = toSmallestChunkSize(data.length)
|
|
@@ -641,7 +636,7 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
641
636
|
ivValue = data.slice(0, AES_CHUNK_SIZE)
|
|
642
637
|
data = data.slice(AES_CHUNK_SIZE)
|
|
643
638
|
}
|
|
644
|
-
aes =
|
|
639
|
+
aes = createDecipheriv('aes-256-cbc', cipherKey, ivValue)
|
|
645
640
|
// if an end byte that is not EOF is specified
|
|
646
641
|
// stop auto padding (PKCS7) -- otherwise throws an error for decryption
|
|
647
642
|
if (endByte) {
|
|
@@ -690,7 +685,7 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
690
685
|
// send a query JSON to obtain the url & auth token to upload our media
|
|
691
686
|
let uploadInfo = await refreshMediaConn(false)
|
|
692
687
|
let urls
|
|
693
|
-
let media =
|
|
688
|
+
let media = MEDIA_PATH_MAP[mediaType]
|
|
694
689
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts]
|
|
695
690
|
fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
|
|
696
691
|
if (newsletter) {
|
|
@@ -703,13 +698,13 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
703
698
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
704
699
|
let result
|
|
705
700
|
try {
|
|
706
|
-
const body = await axios_1.default.post(url,
|
|
701
|
+
const body = await axios_1.default.post(url, createReadStream(filePath), {
|
|
707
702
|
...options,
|
|
708
703
|
maxRedirects: 0,
|
|
709
704
|
headers: {
|
|
710
705
|
...options.headers || {},
|
|
711
706
|
'Content-Type': 'application/octet-stream',
|
|
712
|
-
'Origin':
|
|
707
|
+
'Origin': DEFAULT_ORIGIN
|
|
713
708
|
},
|
|
714
709
|
httpsAgent: fetchAgent,
|
|
715
710
|
timeout: timeoutMs,
|
|
@@ -742,29 +737,29 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
742
737
|
}
|
|
743
738
|
}
|
|
744
739
|
if (!urls) {
|
|
745
|
-
throw new
|
|
740
|
+
throw new Boom('Media upload failed on all hosts', { statusCode: 500 })
|
|
746
741
|
}
|
|
747
742
|
return urls
|
|
748
743
|
}
|
|
749
744
|
}
|
|
750
745
|
|
|
751
746
|
const getMediaRetryKey = (mediaKey) => {
|
|
752
|
-
return
|
|
747
|
+
return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' })
|
|
753
748
|
}
|
|
754
749
|
/**
|
|
755
750
|
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
756
751
|
*/
|
|
757
752
|
const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
758
753
|
const recp = { stanzaId: key.id }
|
|
759
|
-
const recpBuffer =
|
|
760
|
-
const iv =
|
|
754
|
+
const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish()
|
|
755
|
+
const iv = randomBytes(12)
|
|
761
756
|
const retryKey = await getMediaRetryKey(mediaKey)
|
|
762
|
-
const ciphertext =
|
|
757
|
+
const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id))
|
|
763
758
|
const req = {
|
|
764
759
|
tag: 'receipt',
|
|
765
760
|
attrs: {
|
|
766
761
|
id: key.id,
|
|
767
|
-
to:
|
|
762
|
+
to: jidNormalizedUser(meId),
|
|
768
763
|
type: 'server-error'
|
|
769
764
|
},
|
|
770
765
|
content: [
|
|
@@ -794,7 +789,7 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
794
789
|
}
|
|
795
790
|
|
|
796
791
|
const decodeMediaRetryNode = (node) => {
|
|
797
|
-
const rmrNode =
|
|
792
|
+
const rmrNode = getBinaryNodeChild(node, 'rmr')
|
|
798
793
|
const event = {
|
|
799
794
|
key: {
|
|
800
795
|
id: node.attrs.id,
|
|
@@ -803,20 +798,20 @@ const decodeMediaRetryNode = (node) => {
|
|
|
803
798
|
participant: rmrNode.attrs.participant
|
|
804
799
|
}
|
|
805
800
|
}
|
|
806
|
-
const errorNode =
|
|
801
|
+
const errorNode = getBinaryNodeChild(node, 'error')
|
|
807
802
|
if (errorNode) {
|
|
808
803
|
const errorCode = +errorNode.attrs.code
|
|
809
|
-
event.error = new
|
|
804
|
+
event.error = new Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: getStatusCodeForMediaRetry(errorCode) })
|
|
810
805
|
}
|
|
811
806
|
else {
|
|
812
|
-
const encryptedInfoNode =
|
|
813
|
-
const ciphertext =
|
|
814
|
-
const iv =
|
|
807
|
+
const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt')
|
|
808
|
+
const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p')
|
|
809
|
+
const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv')
|
|
815
810
|
if (ciphertext && iv) {
|
|
816
811
|
event.media = { ciphertext, iv }
|
|
817
812
|
}
|
|
818
813
|
else {
|
|
819
|
-
event.error = new
|
|
814
|
+
event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 })
|
|
820
815
|
}
|
|
821
816
|
}
|
|
822
817
|
return event
|
|
@@ -824,17 +819,17 @@ const decodeMediaRetryNode = (node) => {
|
|
|
824
819
|
|
|
825
820
|
const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
826
821
|
const retryKey = await getMediaRetryKey(mediaKey)
|
|
827
|
-
const plaintext =
|
|
828
|
-
return
|
|
822
|
+
const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId))
|
|
823
|
+
return proto.MediaRetryNotification.decode(plaintext)
|
|
829
824
|
}
|
|
830
825
|
|
|
831
826
|
const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code]
|
|
832
827
|
|
|
833
828
|
const MEDIA_RETRY_STATUS_MAP = {
|
|
834
|
-
[
|
|
835
|
-
[
|
|
836
|
-
[
|
|
837
|
-
[
|
|
829
|
+
[proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
|
|
830
|
+
[proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
|
|
831
|
+
[proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
832
|
+
[proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
|
|
838
833
|
}
|
|
839
834
|
|
|
840
835
|
module.exports = {
|