mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Merge branch 'master' of https://github.com/WhiskeySockets/Baileys into fix-eslint-prettier-editorconfig-rules
This commit is contained in:
@@ -1,18 +1,12 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { createHash } from 'crypto'
|
||||
import {
|
||||
CatalogCollection,
|
||||
CatalogStatus,
|
||||
OrderDetails,
|
||||
OrderProduct,
|
||||
Product,
|
||||
ProductCreate,
|
||||
ProductUpdate,
|
||||
WAMediaUpload,
|
||||
WAMediaUploadFunction
|
||||
} from '../Types'
|
||||
import { createWriteStream, promises as fs } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { CatalogCollection, CatalogStatus, OrderDetails, OrderProduct, Product, ProductCreate, ProductUpdate, WAMediaUpload, WAMediaUploadFunction } from '../Types'
|
||||
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../WABinary'
|
||||
import { getStream, getUrlFromDirectPath, toReadable } from './messages-media'
|
||||
import { generateMessageIDV2 } from './generics'
|
||||
import { getStream, getUrlFromDirectPath } from './messages-media'
|
||||
|
||||
export const parseCatalogNode = (node: BinaryNode) => {
|
||||
const catalogNode = getBinaryNodeChild(node, 'product_catalog')
|
||||
@@ -238,23 +232,35 @@ export const uploadingNecessaryImages = async (
|
||||
}
|
||||
}
|
||||
|
||||
const { stream } = await getStream(img)
|
||||
const hasher = createHash('sha256')
|
||||
const contentBlocks: Buffer[] = []
|
||||
for await (const block of stream) {
|
||||
hasher.update(block)
|
||||
contentBlocks.push(block)
|
||||
}
|
||||
const { stream } = await getStream(img)
|
||||
const hasher = createHash('sha256')
|
||||
|
||||
const filePath = join(tmpdir(), 'img' + generateMessageIDV2())
|
||||
const encFileWriteStream = createWriteStream(filePath)
|
||||
|
||||
for await (const block of stream) {
|
||||
hasher.update(block)
|
||||
encFileWriteStream.write(block)
|
||||
}
|
||||
|
||||
const sha = hasher.digest('base64')
|
||||
|
||||
const { directPath } = await waUploadToServer(toReadable(Buffer.concat(contentBlocks)), {
|
||||
mediaType: 'product-catalog-image',
|
||||
fileEncSha256B64: sha,
|
||||
timeoutMs
|
||||
})
|
||||
return { url: getUrlFromDirectPath(directPath) }
|
||||
})
|
||||
const { directPath } = await waUploadToServer(
|
||||
filePath,
|
||||
{
|
||||
mediaType: 'product-catalog-image',
|
||||
fileEncSha256B64: sha,
|
||||
timeoutMs
|
||||
}
|
||||
)
|
||||
|
||||
await fs
|
||||
.unlink(filePath)
|
||||
.catch(err => console.log('Error deleting temp file ', err))
|
||||
|
||||
return { url: getUrlFromDirectPath(directPath) }
|
||||
}
|
||||
)
|
||||
)
|
||||
return results
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const Browsers: BrowsersMap = {
|
||||
|
||||
export const getPlatformId = (browser: string) => {
|
||||
const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase()]
|
||||
return platformType ? platformType.toString().charCodeAt(0).toString() : '49' //chrome
|
||||
return platformType ? platformType.toString() : '1' //chrome
|
||||
}
|
||||
|
||||
export const BufferJSON = {
|
||||
|
||||
@@ -11,20 +11,7 @@ import { Readable, Transform } from 'stream'
|
||||
import { URL } from 'url'
|
||||
import { proto } from '../../WAProto'
|
||||
import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults'
|
||||
import {
|
||||
BaileysEventMap,
|
||||
DownloadableMessage,
|
||||
MediaConnInfo,
|
||||
MediaDecryptionKeyInfo,
|
||||
MediaType,
|
||||
MessageType,
|
||||
SocketConfig,
|
||||
WAGenericMediaMessage,
|
||||
WAMediaPayloadURL,
|
||||
WAMediaUpload,
|
||||
WAMediaUploadFunction,
|
||||
WAMessageContent
|
||||
} from '../Types'
|
||||
import { BaileysEventMap, DownloadableMessage, MediaConnInfo, MediaDecryptionKeyInfo, MediaType, MessageType, SocketConfig, WAGenericMediaMessage, WAMediaUpload, WAMediaUploadFunction, WAMessageContent } from '../Types'
|
||||
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary'
|
||||
import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto'
|
||||
import { generateMessageIDV2 } from './generics'
|
||||
@@ -339,24 +326,35 @@ export const encryptedStream = async (
|
||||
|
||||
const mediaKey = Crypto.randomBytes(32)
|
||||
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
|
||||
const encWriteStream = new Readable({ read: () => {} })
|
||||
|
||||
let bodyPath: string | undefined
|
||||
let writeStream: WriteStream | undefined
|
||||
let didSaveToTmpPath = false
|
||||
if (type === 'file') {
|
||||
bodyPath = (media as WAMediaPayloadURL).url.toString()
|
||||
} else if (saveOriginalFileIfRequired) {
|
||||
bodyPath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2())
|
||||
writeStream = createWriteStream(bodyPath)
|
||||
didSaveToTmpPath = true
|
||||
const encFilePath = join(
|
||||
getTmpFilesDirectory(),
|
||||
mediaType + generateMessageIDV2() + '-enc'
|
||||
)
|
||||
const encFileWriteStream = createWriteStream(encFilePath)
|
||||
|
||||
let originalFileStream: WriteStream | undefined
|
||||
let originalFilePath: string | undefined
|
||||
|
||||
if(saveOriginalFileIfRequired) {
|
||||
originalFilePath = join(
|
||||
getTmpFilesDirectory(),
|
||||
mediaType + generateMessageIDV2() + '-original'
|
||||
)
|
||||
originalFileStream = createWriteStream(originalFilePath)
|
||||
}
|
||||
|
||||
let fileLength = 0
|
||||
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
|
||||
let hmac = Crypto.createHmac('sha256', macKey!).update(iv)
|
||||
let sha256Plain = Crypto.createHash('sha256')
|
||||
let sha256Enc = Crypto.createHash('sha256')
|
||||
const hmac = Crypto.createHmac('sha256', macKey!).update(iv)
|
||||
const sha256Plain = Crypto.createHash('sha256')
|
||||
const sha256Enc = Crypto.createHash('sha256')
|
||||
|
||||
const onChunk = (buff: Buffer) => {
|
||||
sha256Enc.update(buff)
|
||||
hmac.update(buff)
|
||||
encFileWriteStream.write(buff)
|
||||
}
|
||||
|
||||
try {
|
||||
for await (const data of stream) {
|
||||
@@ -368,66 +366,63 @@ export const encryptedStream = async (
|
||||
})
|
||||
}
|
||||
|
||||
sha256Plain = sha256Plain.update(data)
|
||||
if (writeStream && !writeStream.write(data)) {
|
||||
await once(writeStream, 'drain')
|
||||
if(originalFileStream) {
|
||||
if(!originalFileStream.write(data)) {
|
||||
await once(originalFileStream, 'drain')
|
||||
}
|
||||
}
|
||||
|
||||
sha256Plain.update(data)
|
||||
onChunk(aes.update(data))
|
||||
}
|
||||
|
||||
onChunk(aes.final())
|
||||
|
||||
const mac = hmac.digest().slice(0, 10)
|
||||
sha256Enc = sha256Enc.update(mac)
|
||||
sha256Enc.update(mac)
|
||||
|
||||
const fileSha256 = sha256Plain.digest()
|
||||
const fileEncSha256 = sha256Enc.digest()
|
||||
|
||||
encWriteStream.push(mac)
|
||||
encWriteStream.push(null)
|
||||
encFileWriteStream.write(mac)
|
||||
|
||||
writeStream?.end()
|
||||
encFileWriteStream.end()
|
||||
originalFileStream?.end?.()
|
||||
stream.destroy()
|
||||
|
||||
logger?.debug('encrypted data successfully')
|
||||
|
||||
return {
|
||||
mediaKey,
|
||||
encWriteStream,
|
||||
bodyPath,
|
||||
originalFilePath,
|
||||
encFilePath,
|
||||
mac,
|
||||
fileEncSha256,
|
||||
fileSha256,
|
||||
fileLength,
|
||||
didSaveToTmpPath
|
||||
fileLength
|
||||
}
|
||||
} catch (error) {
|
||||
// destroy all streams with error
|
||||
encWriteStream.destroy()
|
||||
writeStream?.destroy()
|
||||
encFileWriteStream.destroy()
|
||||
originalFileStream?.destroy?.()
|
||||
aes.destroy()
|
||||
hmac.destroy()
|
||||
sha256Plain.destroy()
|
||||
sha256Enc.destroy()
|
||||
stream.destroy()
|
||||
|
||||
if (didSaveToTmpPath) {
|
||||
try {
|
||||
await fs.unlink(bodyPath!)
|
||||
} catch (err) {
|
||||
logger?.error({ err }, 'failed to save to tmp path')
|
||||
|
||||
try {
|
||||
await fs.unlink(encFilePath)
|
||||
if(originalFilePath) {
|
||||
await fs.unlink(originalFilePath)
|
||||
}
|
||||
} catch(err) {
|
||||
logger?.error({ err }, 'failed deleting tmp files')
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
function onChunk(buff: Buffer) {
|
||||
sha256Enc = sha256Enc.update(buff)
|
||||
hmac = hmac.update(buff)
|
||||
encWriteStream.push(buff)
|
||||
}
|
||||
}
|
||||
|
||||
const DEF_HOST = 'mmg.whatsapp.net'
|
||||
@@ -577,7 +572,7 @@ export const getWAUploadToServer = (
|
||||
{ customUploadHosts, fetchAgent, logger, options }: SocketConfig,
|
||||
refreshMediaConn: (force: boolean) => Promise<MediaConnInfo>
|
||||
): WAMediaUploadFunction => {
|
||||
return async (stream, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
||||
return async(filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
||||
// send a query JSON to obtain the url & auth token to upload our media
|
||||
let uploadInfo = await refreshMediaConn(false)
|
||||
|
||||
@@ -594,19 +589,25 @@ export const getWAUploadToServer = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let result: any
|
||||
try {
|
||||
const body = await axios.post(url, stream, {
|
||||
...options,
|
||||
headers: {
|
||||
...(options.headers || {}),
|
||||
'Content-Type': 'application/octet-stream',
|
||||
Origin: DEFAULT_ORIGIN
|
||||
},
|
||||
httpsAgent: fetchAgent,
|
||||
timeout: timeoutMs,
|
||||
responseType: 'json',
|
||||
maxBodyLength: Infinity,
|
||||
maxContentLength: Infinity
|
||||
})
|
||||
|
||||
const body = await axios.post(
|
||||
url,
|
||||
createReadStream(filePath),
|
||||
{
|
||||
...options,
|
||||
maxRedirects: 0,
|
||||
headers: {
|
||||
...options.headers || { },
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Origin': DEFAULT_ORIGIN
|
||||
},
|
||||
httpsAgent: fetchAgent,
|
||||
timeout: timeoutMs,
|
||||
responseType: 'json',
|
||||
maxBodyLength: Infinity,
|
||||
maxContentLength: Infinity,
|
||||
}
|
||||
)
|
||||
result = body.data
|
||||
|
||||
if (result?.url || result?.directPath) {
|
||||
|
||||
@@ -165,8 +165,17 @@ export const prepareWAMessageMedia = async (message: AnyMediaMessageContent, opt
|
||||
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
|
||||
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
|
||||
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
||||
const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath } =
|
||||
await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
||||
const {
|
||||
mediaKey,
|
||||
encFilePath,
|
||||
originalFilePath,
|
||||
fileEncSha256,
|
||||
fileSha256,
|
||||
fileLength
|
||||
} = await encryptedStream(
|
||||
uploadData.media,
|
||||
options.mediaTypeOverride || mediaType,
|
||||
{
|
||||
logger,
|
||||
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
||||
opts: options.options
|
||||
@@ -174,23 +183,21 @@ export const prepareWAMessageMedia = async (message: AnyMediaMessageContent, opt
|
||||
// url safe Base64 encode the SHA256 hash of the body
|
||||
const fileEncSha256B64 = fileEncSha256.toString('base64')
|
||||
const [{ mediaUrl, directPath }] = await Promise.all([
|
||||
(async () => {
|
||||
const result = await options.upload(encWriteStream, {
|
||||
fileEncSha256B64,
|
||||
mediaType,
|
||||
timeoutMs: options.mediaUploadTimeoutMs
|
||||
})
|
||||
(async() => {
|
||||
const result = await options.upload(
|
||||
encFilePath,
|
||||
{ fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs }
|
||||
)
|
||||
logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
|
||||
return result
|
||||
})(),
|
||||
(async () => {
|
||||
try {
|
||||
if (requiresThumbnailComputation) {
|
||||
const { thumbnail, originalImageDimensions } = await generateThumbnail(
|
||||
bodyPath!,
|
||||
mediaType as 'image' | 'video',
|
||||
options
|
||||
)
|
||||
if(requiresThumbnailComputation) {
|
||||
const {
|
||||
thumbnail,
|
||||
originalImageDimensions
|
||||
} = await generateThumbnail(originalFilePath!, mediaType as 'image' | 'video', options)
|
||||
uploadData.jpegThumbnail = thumbnail
|
||||
if (!uploadData.width && originalImageDimensions) {
|
||||
uploadData.width = originalImageDimensions.width
|
||||
@@ -201,13 +208,13 @@ export const prepareWAMessageMedia = async (message: AnyMediaMessageContent, opt
|
||||
logger?.debug('generated thumbnail')
|
||||
}
|
||||
|
||||
if (requiresDurationComputation) {
|
||||
uploadData.seconds = await getAudioDuration(bodyPath!)
|
||||
if(requiresDurationComputation) {
|
||||
uploadData.seconds = await getAudioDuration(originalFilePath!)
|
||||
logger?.debug('computed audio duration')
|
||||
}
|
||||
|
||||
if (requiresWaveformProcessing) {
|
||||
uploadData.waveform = await getAudioWaveform(bodyPath!, logger)
|
||||
if(requiresWaveformProcessing) {
|
||||
uploadData.waveform = await getAudioWaveform(originalFilePath!, logger)
|
||||
logger?.debug('processed waveform')
|
||||
}
|
||||
|
||||
@@ -218,20 +225,22 @@ export const prepareWAMessageMedia = async (message: AnyMediaMessageContent, opt
|
||||
} catch (error) {
|
||||
logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
|
||||
}
|
||||
})()
|
||||
]).finally(async () => {
|
||||
encWriteStream.destroy()
|
||||
// remove tmp files
|
||||
if (didSaveToTmpPath && bodyPath) {
|
||||
try {
|
||||
await fs.access(bodyPath)
|
||||
await fs.unlink(bodyPath)
|
||||
logger?.debug('removed tmp file')
|
||||
} catch (error) {
|
||||
logger?.warn('failed to remove tmp file')
|
||||
})(),
|
||||
])
|
||||
.finally(
|
||||
async() => {
|
||||
try {
|
||||
await fs.unlink(encFilePath)
|
||||
if(originalFilePath) {
|
||||
await fs.unlink(originalFilePath)
|
||||
}
|
||||
|
||||
logger?.debug('removed tmp files')
|
||||
} catch(error) {
|
||||
logger?.warn('failed to remove tmp file')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const obj = WAProto.Message.fromObject({
|
||||
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
||||
|
||||
Reference in New Issue
Block a user