mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
lint: stricter linting rules
This commit is contained in:
@@ -128,10 +128,10 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
JSON.stringify({ creds, keys }, BufferJSON.replacer, 2)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if(existsSync(filename)) {
|
||||
const result = JSON.parse(
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
creds = result.creds
|
||||
@@ -141,8 +141,8 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
keys = { }
|
||||
}
|
||||
|
||||
return {
|
||||
state: {
|
||||
return {
|
||||
state: {
|
||||
creds,
|
||||
keys: {
|
||||
get: (type, ids) => {
|
||||
@@ -172,7 +172,7 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
saveState()
|
||||
}
|
||||
}
|
||||
},
|
||||
saveState
|
||||
},
|
||||
saveState
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren } from '../WABina
|
||||
import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto'
|
||||
import { toNumber } from './generics'
|
||||
import { LT_HASH_ANTI_TAMPERING } from './lt-hash'
|
||||
import { downloadContentFromMessage, } from './messages-media'
|
||||
import { downloadContentFromMessage, } from './messages-media'
|
||||
|
||||
type FetchAppStateSyncKey = (keyId: string) => Promise<proto.IAppStateSyncKeyData> | proto.IAppStateSyncKeyData
|
||||
|
||||
@@ -39,7 +39,7 @@ const generateMac = (operation: proto.SyncdMutation.SyncdMutationSyncdOperation,
|
||||
const keyData = getKeyData()
|
||||
|
||||
const last = Buffer.alloc(8) // 8 bytes
|
||||
last.set([ keyData.length ], last.length-1)
|
||||
last.set([ keyData.length ], last.length - 1)
|
||||
|
||||
const total = Buffer.concat([ keyData, data, last ])
|
||||
const hmac = hmacSign(total, key, 'sha512')
|
||||
@@ -180,7 +180,7 @@ export const encodeSyncdPatch = async(
|
||||
}
|
||||
|
||||
export const decodeSyncdMutations = async(
|
||||
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
||||
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
||||
initialState: LTHashState,
|
||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||
validateMacs: boolean
|
||||
@@ -214,7 +214,7 @@ export const decodeSyncdMutations = async(
|
||||
// otherwise, if it's only a record -- it'll be a SET mutation
|
||||
const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdMutationSyncdOperation.SET
|
||||
const record = ('record' in msgMutation && !!msgMutation.record) ? msgMutation.record : msgMutation as proto.ISyncdRecord
|
||||
|
||||
|
||||
const key = await getKey(record.keyId!.id!)
|
||||
const content = Buffer.from(record.value!.blob!)
|
||||
const encContent = content.slice(0, -32)
|
||||
@@ -236,12 +236,12 @@ export const decodeSyncdMutations = async(
|
||||
}
|
||||
}
|
||||
|
||||
const indexStr = Buffer.from(syncAction.index).toString()
|
||||
const indexStr = Buffer.from(syncAction.index).toString()
|
||||
mutations.push({
|
||||
syncAction,
|
||||
index: JSON.parse(indexStr),
|
||||
})
|
||||
ltGenerator.mix({
|
||||
ltGenerator.mix({
|
||||
indexMac: record.index!.blob!,
|
||||
valueMac: ogValueMac,
|
||||
operation: operation
|
||||
@@ -252,7 +252,7 @@ export const decodeSyncdMutations = async(
|
||||
}
|
||||
|
||||
export const decodeSyncdPatch = async(
|
||||
msg: proto.ISyncdPatch,
|
||||
msg: proto.ISyncdPatch,
|
||||
name: WAPatchName,
|
||||
initialState: LTHashState,
|
||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||
@@ -263,11 +263,11 @@ export const decodeSyncdPatch = async(
|
||||
const mainKeyObj = await getAppStateSyncKey(base64Key)
|
||||
const mainKey = mutationKeys(mainKeyObj.keyData!)
|
||||
const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32))
|
||||
|
||||
|
||||
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, toNumber(msg.version!.version), name, mainKey.patchMacKey)
|
||||
if(Buffer.compare(patchMac, msg.patchMac) !== 0) {
|
||||
throw new Boom('Invalid patch mac')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await decodeSyncdMutations(msg!.mutations!, initialState, getAppStateSyncKey, validateMacs)
|
||||
@@ -277,7 +277,7 @@ export const decodeSyncdPatch = async(
|
||||
export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
const syncNode = getBinaryNodeChild(result, 'sync')
|
||||
const collectionNodes = getBinaryNodeChildren(syncNode, 'collection')
|
||||
|
||||
|
||||
const final = { } as { [T in WAPatchName]: { patches: proto.ISyncdPatch[], hasMorePatches: boolean, snapshot?: proto.ISyncdSnapshot } }
|
||||
await Promise.all(
|
||||
collectionNodes.map(
|
||||
@@ -291,7 +291,7 @@ export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
const name = collectionNode.attrs.name as WAPatchName
|
||||
|
||||
const hasMorePatches = collectionNode.attrs.has_more_patches === 'true'
|
||||
|
||||
|
||||
let snapshot: proto.ISyncdSnapshot | undefined = undefined
|
||||
if(snapshotNode && !!snapshotNode.content) {
|
||||
if(!Buffer.isBuffer(snapshotNode)) {
|
||||
@@ -313,13 +313,13 @@ export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
|
||||
const syncd = proto.SyncdPatch.decode(content! as Uint8Array)
|
||||
if(!syncd.version) {
|
||||
syncd.version = { version: +collectionNode.attrs.version+1 }
|
||||
syncd.version = { version: +collectionNode.attrs.version + 1 }
|
||||
}
|
||||
|
||||
syncds.push(syncd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final[name] = { patches: syncds, hasMorePatches, snapshot }
|
||||
}
|
||||
)
|
||||
@@ -354,7 +354,7 @@ export const decodeSyncdSnapshot = async(
|
||||
) => {
|
||||
const newState = newLTHashState()
|
||||
newState.version = toNumber(snapshot.version!.version!)
|
||||
|
||||
|
||||
const { hash, indexValueMap, mutations } = await decodeSyncdMutations(snapshot.records!, newState, getAppStateSyncKey, validateMacs)
|
||||
newState.hash = hash
|
||||
newState.indexValueMap = indexValueMap
|
||||
@@ -408,7 +408,7 @@ export const decodePatches = async(
|
||||
}
|
||||
|
||||
const patchVersion = toNumber(version.version!)
|
||||
|
||||
|
||||
newState.version = patchVersion
|
||||
|
||||
const decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, validateMacs)
|
||||
@@ -418,7 +418,7 @@ export const decodePatches = async(
|
||||
if(typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber) {
|
||||
successfulMutations.push(...decodeResult.mutations)
|
||||
}
|
||||
|
||||
|
||||
if(validateMacs) {
|
||||
const base64Key = Buffer.from(keyId!.id!).toString('base64')
|
||||
const keyEnc = await getAppStateSyncKey(base64Key)
|
||||
@@ -450,7 +450,7 @@ export const chatModificationToAppPatch = (
|
||||
throw new Boom('Expected last message to be not from me', { statusCode: 400 })
|
||||
}
|
||||
|
||||
const lastMsg = lastMessages[lastMessages.length-1]
|
||||
const lastMsg = lastMessages[lastMessages.length - 1]
|
||||
if(lastMsg.key.fromMe) {
|
||||
throw new Boom('Expected last message in array to be not from me', { statusCode: 400 })
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const signedKeyPair = (keyPair: KeyPair, keyId: number) => {
|
||||
pubKey.set(signKeys.public, 1)
|
||||
|
||||
const signature = Curve.sign(keyPair.private, pubKey)
|
||||
|
||||
|
||||
return { keyPair: signKeys, signature, keyId }
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ export function hkdf(buffer: Uint8Array, expandedLength: number, { info, salt }:
|
||||
let prev = Buffer.from([])
|
||||
const buffers = []
|
||||
const num_blocks = Math.ceil(expandedLength / hashLength)
|
||||
|
||||
|
||||
const infoBuff = Buffer.from(info || [])
|
||||
|
||||
for(var i=0; i<num_blocks; i++) {
|
||||
for(var i = 0; i < num_blocks; i++) {
|
||||
const hmac = createHmac(hashAlg, prk)
|
||||
// XXX is there a more optimal way to build up buffers?
|
||||
const input = Buffer.concat([
|
||||
|
||||
@@ -62,7 +62,7 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
|
||||
const fromMe = isMe(stanza.attrs.participant || stanza.attrs.from)
|
||||
const pushname = stanza.attrs.notify
|
||||
|
||||
|
||||
const key: WAMessageKey = {
|
||||
remoteJid: chatId,
|
||||
fromMe,
|
||||
@@ -83,21 +83,21 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
return {
|
||||
fullMessage,
|
||||
decryptionTask: (async() => {
|
||||
let decryptables = 0
|
||||
let decryptables = 0
|
||||
if(Array.isArray(stanza.content)) {
|
||||
for(const { tag, attrs, content } of stanza.content) {
|
||||
if(tag !== 'enc') {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if(!(content instanceof Uint8Array)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
decryptables += 1
|
||||
|
||||
|
||||
let msgBuffer: Buffer
|
||||
|
||||
|
||||
try {
|
||||
const e2eType = attrs.type
|
||||
switch (e2eType) {
|
||||
@@ -110,13 +110,13 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer))
|
||||
msg = msg.deviceSentMessage?.message || msg
|
||||
if(msg.senderKeyDistributionMessage) {
|
||||
await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth)
|
||||
}
|
||||
|
||||
|
||||
if(fullMessage.message) {
|
||||
Object.assign(fullMessage.message, msg)
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const BufferJSON = {
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const writeRandomPadMax16 = (e: Binary) => {
|
||||
@@ -47,7 +47,7 @@ export const writeRandomPadMax16 = (e: Binary) => {
|
||||
e.writeUint8(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var t = randomBytes(1)
|
||||
r(e, 1 + (15 & t[0]))
|
||||
return e
|
||||
@@ -58,12 +58,12 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
|
||||
if(0 === t.length) {
|
||||
throw new Error('unpadPkcs7 given empty bytes')
|
||||
}
|
||||
|
||||
|
||||
var r = t[t.length - 1]
|
||||
if(r > t.length) {
|
||||
throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`)
|
||||
}
|
||||
|
||||
|
||||
return new Uint8Array(t.buffer, t.byteOffset, t.length - r)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ export const encodeInt = (e: number, t: number) => {
|
||||
return a
|
||||
}
|
||||
|
||||
export const encodeBigEndian = (e: number, t=4) => {
|
||||
export const encodeBigEndian = (e: number, t = 4) => {
|
||||
let r = e
|
||||
const a = new Uint8Array(t)
|
||||
for(let i = t - 1; i >= 0; i--) {
|
||||
@@ -121,7 +121,7 @@ export function shallowChanges <T>(old: T, current: T, { lookForDeletedKeys }: {
|
||||
}
|
||||
|
||||
/** unix timestamp of a date in seconds */
|
||||
export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime()/1000)
|
||||
export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime() / 1000)
|
||||
|
||||
export type DebouncedTimeout = ReturnType<typeof debouncedTimeout>
|
||||
|
||||
@@ -175,7 +175,7 @@ export async function promiseTimeout<T>(ms: number, promise: (resolve: (v?: T)=>
|
||||
|
||||
const stack = new Error().stack
|
||||
// Create a promise that rejects in <ms> milliseconds
|
||||
const { delay, cancel } = delayCancellable (ms)
|
||||
const { delay, cancel } = delayCancellable (ms)
|
||||
const p = new Promise ((resolve, reject) => {
|
||||
delay
|
||||
.then(() => reject(
|
||||
@@ -186,8 +186,8 @@ export async function promiseTimeout<T>(ms: number, promise: (resolve: (v?: T)=>
|
||||
}
|
||||
})
|
||||
))
|
||||
.catch (err => reject(err))
|
||||
|
||||
.catch (err => reject(err))
|
||||
|
||||
promise (resolve, reject)
|
||||
})
|
||||
.finally (cancel)
|
||||
@@ -202,7 +202,7 @@ export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter<any>)
|
||||
let listener: (item: Partial<ConnectionState>) => void
|
||||
await (
|
||||
promiseTimeout(
|
||||
timeoutMs,
|
||||
timeoutMs,
|
||||
(resolve, reject) => {
|
||||
listener = (update) => {
|
||||
if(check(update)) {
|
||||
@@ -236,7 +236,7 @@ export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter<any>, l
|
||||
|
||||
/**
|
||||
* utility that fetches latest baileys version from the master branch.
|
||||
* Use to ensure your WA connection is always on the latest version
|
||||
* Use to ensure your WA connection is always on the latest version
|
||||
*/
|
||||
export const fetchLatestBaileysVersion = async() => {
|
||||
const URL = 'https://raw.githubusercontent.com/adiwajshing/Baileys/master/src/Defaults/baileys-version.json'
|
||||
|
||||
@@ -10,21 +10,21 @@ export const newLegacyAuthCreds = () => ({
|
||||
}) as LegacyAuthenticationCreds
|
||||
|
||||
export const decodeWAMessage = (
|
||||
message: Buffer | string,
|
||||
auth: { macKey: Buffer, encKey: Buffer },
|
||||
fromMe: boolean=false
|
||||
message: Buffer | string,
|
||||
auth: { macKey: Buffer, encKey: Buffer },
|
||||
fromMe: boolean = false
|
||||
) => {
|
||||
let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message
|
||||
if(commaIndex < 0) {
|
||||
throw new Boom('invalid message', { data: message })
|
||||
} // if there was no comma, then this message must be not be valid
|
||||
|
||||
if(message[commaIndex+1] === ',') {
|
||||
|
||||
if(message[commaIndex + 1] === ',') {
|
||||
commaIndex += 1
|
||||
}
|
||||
|
||||
let data = message.slice(commaIndex+1, message.length)
|
||||
|
||||
let data = message.slice(commaIndex + 1, message.length)
|
||||
|
||||
// get the message tag.
|
||||
// If a query was done, the server will respond with the same message tag we sent the query with
|
||||
const messageTag: string = message.slice(0, commaIndex).toString()
|
||||
@@ -43,7 +43,7 @@ export const decodeWAMessage = (
|
||||
throw new Boom('recieved encrypted buffer when auth creds unavailable', { data: message, statusCode: DisconnectReason.badSession })
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
If the data recieved was not a JSON, then it must be an encrypted message.
|
||||
Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys
|
||||
*/
|
||||
@@ -51,11 +51,11 @@ export const decodeWAMessage = (
|
||||
tags = [data[0], data[1]]
|
||||
data = data.slice(2, data.length)
|
||||
}
|
||||
|
||||
|
||||
const checksum = data.slice(0, 32) // the first 32 bytes of the buffer are the HMAC sign of the message
|
||||
data = data.slice(32, data.length) // the actual message
|
||||
const computedChecksum = hmacSign(data, macKey) // compute the sign of the message we recieved using our macKey
|
||||
|
||||
|
||||
if(checksum.equals(computedChecksum)) {
|
||||
// the checksum the server sent, must match the one we computed for the message to be valid
|
||||
const decrypted = aesDecrypt(data, encKey) // decrypt using AES
|
||||
@@ -73,7 +73,7 @@ export const decodeWAMessage = (
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [messageTag, json, tags] as const
|
||||
@@ -85,7 +85,7 @@ export const decodeWAMessage = (
|
||||
* @param json
|
||||
*/
|
||||
export const validateNewConnection = (
|
||||
json: { [_: string]: any },
|
||||
json: { [_: string]: any },
|
||||
auth: LegacyAuthenticationCreds,
|
||||
curveKeys: CurveKeyPair
|
||||
) => {
|
||||
@@ -164,7 +164,7 @@ export const useSingleFileLegacyAuthState = (file: string) => {
|
||||
|
||||
if(existsSync(file)) {
|
||||
state = JSON.parse(
|
||||
readFileSync(file, { encoding: 'utf-8' }),
|
||||
readFileSync(file, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
if(typeof state.encKey === 'string') {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { hkdf } from './crypto'
|
||||
|
||||
/**
|
||||
* LT Hash is a summation based hash algorithm that maintains the integrity of a piece of data
|
||||
* over a series of mutations. You can add/remove mutations and it'll return a hash equal to
|
||||
* over a series of mutations. You can add/remove mutations and it'll return a hash equal to
|
||||
* if the same series of mutations was made sequentially.
|
||||
*/
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export const hkdfInfoKey = (type: MediaType) => {
|
||||
if(type === 'md-app-state') {
|
||||
str = 'App State'
|
||||
}
|
||||
|
||||
|
||||
const hkdfInfo = str[0].toUpperCase() + str.slice(1)
|
||||
return `WhatsApp ${hkdfInfo} Keys`
|
||||
}
|
||||
@@ -145,7 +145,7 @@ export const generateProfilePicture = async(mediaUpload: WAMediaUpload) => {
|
||||
.resize(640, 640, RESIZE_BILINEAR)
|
||||
.getBufferAsync(MIME_JPEG)
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
img: await img,
|
||||
}
|
||||
@@ -207,8 +207,8 @@ export const getStream = async(item: WAMediaUpload) => {
|
||||
|
||||
/** generates a thumbnail for a given media, if required */
|
||||
export async function generateThumbnail(
|
||||
file: string,
|
||||
mediaType: 'video' | 'image',
|
||||
file: string,
|
||||
mediaType: 'video' | 'image',
|
||||
options: {
|
||||
logger?: Logger
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export async function generateThumbnail(
|
||||
options.logger?.debug('could not generate video thumb: ' + err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
@@ -238,9 +238,9 @@ export const getHttpStream = async(url: string | URL, options: AxiosRequestConfi
|
||||
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' })
|
||||
return fetched.data as Readable
|
||||
}
|
||||
|
||||
|
||||
export const encryptedStream = async(
|
||||
media: WAMediaUpload,
|
||||
media: WAMediaUpload,
|
||||
mediaType: MediaType,
|
||||
saveOriginalFileIfRequired = true,
|
||||
logger?: Logger
|
||||
@@ -266,7 +266,7 @@ export const encryptedStream = async(
|
||||
writeStream = createWriteStream(bodyPath)
|
||||
didSaveToTmpPath = true
|
||||
}
|
||||
|
||||
|
||||
let fileLength = 0
|
||||
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
|
||||
let hmac = Crypto.createHmac('sha256', macKey).update(iv)
|
||||
@@ -278,7 +278,7 @@ export const encryptedStream = async(
|
||||
hmac = hmac.update(buff)
|
||||
encWriteStream.push(buff)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
for await (const data of stream) {
|
||||
fileLength += data.length
|
||||
@@ -293,21 +293,21 @@ export const encryptedStream = async(
|
||||
}
|
||||
|
||||
onChunk(aes.final())
|
||||
|
||||
|
||||
const mac = hmac.digest().slice(0, 10)
|
||||
sha256Enc = sha256Enc.update(mac)
|
||||
|
||||
|
||||
const fileSha256 = sha256Plain.digest()
|
||||
const fileEncSha256 = sha256Enc.digest()
|
||||
|
||||
|
||||
encWriteStream.push(mac)
|
||||
encWriteStream.push(null)
|
||||
|
||||
|
||||
writeStream && writeStream.end()
|
||||
stream.destroy()
|
||||
|
||||
logger?.debug('encrypted data successfully')
|
||||
|
||||
|
||||
return {
|
||||
mediaKey,
|
||||
encWriteStream,
|
||||
@@ -356,14 +356,14 @@ export const downloadContentFromMessage = async(
|
||||
if(startByte) {
|
||||
const chunk = toSmallestChunkSize(startByte || 0)
|
||||
if(chunk) {
|
||||
startChunk = chunk-AES_CHUNK_SIZE
|
||||
startChunk = chunk - AES_CHUNK_SIZE
|
||||
bytesFetched = chunk
|
||||
|
||||
firstBlockIsIV = true
|
||||
}
|
||||
}
|
||||
|
||||
const endChunk = endByte ? toSmallestChunkSize(endByte || 0)+AES_CHUNK_SIZE : undefined
|
||||
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined
|
||||
|
||||
const headers: { [_: string]: string } = {
|
||||
Origin: DEFAULT_ORIGIN,
|
||||
@@ -377,7 +377,7 @@ export const downloadContentFromMessage = async(
|
||||
|
||||
// download the message
|
||||
const fetched = await getHttpStream(
|
||||
downloadUrl,
|
||||
downloadUrl,
|
||||
{
|
||||
headers,
|
||||
maxBodyLength: Infinity,
|
||||
@@ -392,11 +392,11 @@ export const downloadContentFromMessage = async(
|
||||
|
||||
const pushBytes = (bytes: Buffer, push: (bytes: Buffer) => void) => {
|
||||
if(startByte || endByte) {
|
||||
const start = bytesFetched >= startByte ? undefined : Math.max(startByte-bytesFetched, 0)
|
||||
const end = bytesFetched+bytes.length < endByte ? undefined : Math.max(endByte-bytesFetched, 0)
|
||||
|
||||
const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
|
||||
const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0)
|
||||
|
||||
push(bytes.slice(start, end))
|
||||
|
||||
|
||||
bytesFetched += bytes.length
|
||||
} else {
|
||||
push(bytes)
|
||||
@@ -406,7 +406,7 @@ export const downloadContentFromMessage = async(
|
||||
const output = new Transform({
|
||||
transform(chunk, _, callback) {
|
||||
let data = Buffer.concat([remainingBytes, chunk])
|
||||
|
||||
|
||||
const decryptLength = toSmallestChunkSize(data.length)
|
||||
remainingBytes = data.slice(decryptLength)
|
||||
data = data.slice(0, decryptLength)
|
||||
@@ -424,7 +424,7 @@ export const downloadContentFromMessage = async(
|
||||
if(endByte) {
|
||||
aes.setAutoPadding(false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -432,7 +432,7 @@ export const downloadContentFromMessage = async(
|
||||
callback()
|
||||
} catch(error) {
|
||||
callback(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
final(callback) {
|
||||
try {
|
||||
@@ -451,14 +451,14 @@ export const downloadContentFromMessage = async(
|
||||
* @param message the media message you want to decode
|
||||
*/
|
||||
export async function decryptMediaMessageBuffer(message: WAMessageContent): Promise<Readable> {
|
||||
/*
|
||||
/*
|
||||
One can infer media type from the key in the message
|
||||
it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc.
|
||||
*/
|
||||
const type = Object.keys(message)[0] as MessageType
|
||||
if(
|
||||
!type ||
|
||||
type === 'conversation' ||
|
||||
type === 'conversation' ||
|
||||
type === 'extendedTextMessage'
|
||||
) {
|
||||
throw new Boom(`no media message for "${type}"`, { statusCode: 400 })
|
||||
@@ -492,8 +492,8 @@ export function extensionForMediaMessage(message: WAMessageContent) {
|
||||
const type = Object.keys(message)[0] as MessageType
|
||||
let extension: string
|
||||
if(
|
||||
type === 'locationMessage' ||
|
||||
type === 'liveLocationMessage' ||
|
||||
type === 'locationMessage' ||
|
||||
type === 'liveLocationMessage' ||
|
||||
type === 'productMessage'
|
||||
) {
|
||||
extension = '.jpeg'
|
||||
@@ -539,8 +539,8 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
const body = await axios.post(
|
||||
url,
|
||||
reqBody,
|
||||
{
|
||||
headers: {
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Origin': DEFAULT_ORIGIN
|
||||
},
|
||||
@@ -552,7 +552,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
}
|
||||
)
|
||||
result = body.data
|
||||
|
||||
|
||||
if(result?.url || result?.directPath) {
|
||||
urls = {
|
||||
mediaUrl: result.url,
|
||||
@@ -568,7 +568,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
result = error.response?.data
|
||||
}
|
||||
|
||||
const isLast = hostname === hosts[uploadInfo.hosts.length-1]?.hostname
|
||||
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname
|
||||
logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@ import { Boom } from '@hapi/boom'
|
||||
import { promises as fs } from 'fs'
|
||||
import { proto } from '../../WAProto'
|
||||
import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults'
|
||||
import {
|
||||
AnyMediaMessageContent,
|
||||
AnyMessageContent,
|
||||
MediaGenerationOptions,
|
||||
MediaType,
|
||||
MessageContentGenerationOptions,
|
||||
MessageGenerationOptions,
|
||||
import {
|
||||
AnyMediaMessageContent,
|
||||
AnyMessageContent,
|
||||
MediaGenerationOptions,
|
||||
MediaType,
|
||||
MessageContentGenerationOptions,
|
||||
MessageGenerationOptions,
|
||||
MessageGenerationOptionsFromContent,
|
||||
MessageType,
|
||||
MessageType,
|
||||
MessageUserReceipt,
|
||||
WAMediaUpload,
|
||||
WAMessage,
|
||||
WAMessageContent,
|
||||
WAMediaUpload,
|
||||
WAMessage,
|
||||
WAMessageContent,
|
||||
WAMessageStatus,
|
||||
WAProto,
|
||||
WATextMessage
|
||||
WAProto,
|
||||
WATextMessage
|
||||
} from '../Types'
|
||||
import { generateMessageID, unixTimestampSeconds } from './generics'
|
||||
import { encryptedStream, generateThumbnail, getAudioDuration } from './messages-media'
|
||||
@@ -54,7 +54,7 @@ const MessageTypeProto = {
|
||||
const ButtonType = proto.ButtonsMessage.ButtonsMessageHeaderType
|
||||
|
||||
export const prepareWAMessageMedia = async(
|
||||
message: AnyMediaMessageContent,
|
||||
message: AnyMediaMessageContent,
|
||||
options: MediaGenerationOptions
|
||||
) => {
|
||||
const logger = options.logger
|
||||
@@ -66,15 +66,15 @@ export const prepareWAMessageMedia = async(
|
||||
}
|
||||
}
|
||||
|
||||
const uploadData: MediaUploadData = {
|
||||
const uploadData: MediaUploadData = {
|
||||
...message,
|
||||
media: message[mediaType]
|
||||
}
|
||||
delete uploadData[mediaType]
|
||||
// check if cacheable + generate cache key
|
||||
const cacheableKey = typeof uploadData.media === 'object' &&
|
||||
('url' in uploadData.media) &&
|
||||
!!uploadData.media.url &&
|
||||
const cacheableKey = typeof uploadData.media === 'object' &&
|
||||
('url' in uploadData.media) &&
|
||||
!!uploadData.media.url &&
|
||||
!!options.mediaCache && (
|
||||
// generate the key
|
||||
mediaType + ':' + uploadData.media.url!.toString()
|
||||
@@ -93,7 +93,7 @@ export const prepareWAMessageMedia = async(
|
||||
const mediaBuff: Buffer = options.mediaCache!.get(cacheableKey)
|
||||
if(mediaBuff) {
|
||||
logger?.debug({ cacheableKey }, 'got media cache hit')
|
||||
|
||||
|
||||
const obj = WAProto.Message.decode(mediaBuff)
|
||||
const key = `${mediaType}Message`
|
||||
|
||||
@@ -105,7 +105,7 @@ export const prepareWAMessageMedia = async(
|
||||
}
|
||||
|
||||
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
|
||||
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
||||
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
||||
(typeof uploadData['jpegThumbnail'] === 'undefined')
|
||||
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
||||
const {
|
||||
@@ -118,7 +118,7 @@ export const prepareWAMessageMedia = async(
|
||||
didSaveToTmpPath
|
||||
} = await encryptedStream(uploadData.media, mediaType, requiresOriginalForSomeProcessing)
|
||||
// url safe Base64 encode the SHA256 hash of the body
|
||||
const fileEncSha256B64 = encodeURIComponent(
|
||||
const fileEncSha256B64 = encodeURIComponent(
|
||||
fileEncSha256.toString('base64')
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
@@ -239,7 +239,7 @@ export const generateForwardMessageContent = (
|
||||
}
|
||||
|
||||
export const generateWAMessageContent = async(
|
||||
message: AnyMessageContent,
|
||||
message: AnyMessageContent,
|
||||
options: MessageContentGenerationOptions
|
||||
) => {
|
||||
let m: WAMessageContent = {}
|
||||
@@ -256,7 +256,7 @@ export const generateWAMessageContent = async(
|
||||
extContent.previewType = 0
|
||||
} catch(error) { // ignore if fails
|
||||
options.logger?.warn({ trace: error.stack }, 'url generation failed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.extendedTextMessage = extContent
|
||||
@@ -265,7 +265,7 @@ export const generateWAMessageContent = async(
|
||||
if(!contactLen) {
|
||||
throw new Boom('require atleast 1 contact', { statusCode: 400 })
|
||||
}
|
||||
|
||||
|
||||
if(contactLen === 1) {
|
||||
m.contactMessage = WAProto.ContactMessage.fromObject(message.contacts.contacts[0])
|
||||
} else {
|
||||
@@ -284,7 +284,7 @@ export const generateWAMessageContent = async(
|
||||
message.force
|
||||
)
|
||||
} else if('disappearingMessagesInChat' in message) {
|
||||
const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
|
||||
const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
|
||||
(message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) :
|
||||
message.disappearingMessagesInChat
|
||||
m = prepareDisappearingMessageSettingContent(exp)
|
||||
@@ -309,7 +309,7 @@ export const generateWAMessageContent = async(
|
||||
|
||||
const type = Object.keys(m)[0].replace('Message', '').toUpperCase()
|
||||
buttonsMessage.headerType = ButtonType[type]
|
||||
|
||||
|
||||
Object.assign(buttonsMessage, m)
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ export const generateWAMessageContent = async(
|
||||
hydratedButtons: message.templateButtons
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if('text' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.text
|
||||
} else {
|
||||
@@ -332,7 +332,7 @@ export const generateWAMessageContent = async(
|
||||
if('caption' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.caption
|
||||
}
|
||||
|
||||
|
||||
Object.assign(templateMessage.hydratedTemplate, m)
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ export const generateWAMessageContent = async(
|
||||
|
||||
m = { templateMessage }
|
||||
}
|
||||
|
||||
|
||||
if('sections' in message && !!message.sections) {
|
||||
const listMessage: proto.IListMessage = {
|
||||
sections: message.sections,
|
||||
@@ -370,8 +370,8 @@ export const generateWAMessageContent = async(
|
||||
}
|
||||
|
||||
export const generateWAMessageFromContent = (
|
||||
jid: string,
|
||||
message: WAMessageContent,
|
||||
jid: string,
|
||||
message: WAMessageContent,
|
||||
options: MessageGenerationOptionsFromContent
|
||||
) => {
|
||||
if(!options.timestamp) {
|
||||
@@ -389,7 +389,7 @@ export const generateWAMessageFromContent = (
|
||||
message[key].contextInfo.participant = participant
|
||||
message[key].contextInfo.stanzaId = quoted.key.id
|
||||
message[key].contextInfo.quotedMessage = quoted.message
|
||||
|
||||
|
||||
// if a participant is quoted, then it must be a group
|
||||
// hence, remoteJid of group must also be entered
|
||||
if(quoted.key.participant || quoted.participant) {
|
||||
@@ -403,7 +403,7 @@ export const generateWAMessageFromContent = (
|
||||
// and it's not a protocol message -- delete, toggle disappear message
|
||||
key !== 'protocolMessage' &&
|
||||
// already not converted to disappearing message
|
||||
key !== 'ephemeralMessage'
|
||||
key !== 'ephemeralMessage'
|
||||
) {
|
||||
message[key].contextInfo = {
|
||||
...(message[key].contextInfo || {}),
|
||||
@@ -478,10 +478,10 @@ export const extractMessageContent = (content: WAMessageContent | undefined | nu
|
||||
return { conversation: 'contentText' in msg ? msg.contentText : ('hydratedContentText' in msg ? msg.hydratedContentText : '') }
|
||||
}
|
||||
}
|
||||
|
||||
content = content?.ephemeralMessage?.message ||
|
||||
|
||||
content = content?.ephemeralMessage?.message ||
|
||||
content?.viewOnceMessage?.message ||
|
||||
content ||
|
||||
content ||
|
||||
undefined
|
||||
|
||||
if(content?.buttonsMessage) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const result = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()])
|
||||
|
||||
writeCounter += 1
|
||||
|
||||
|
||||
authenticate(result)
|
||||
return result
|
||||
}
|
||||
@@ -42,8 +42,8 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const cipher = createDecipheriv('aes-256-gcm', decKey, iv)
|
||||
// decrypt additional adata
|
||||
const tagLength = 128 >> 3
|
||||
const enc = ciphertext.slice(0, ciphertext.length-tagLength)
|
||||
const tag = ciphertext.slice(ciphertext.length-tagLength)
|
||||
const enc = ciphertext.slice(0, ciphertext.length - tagLength)
|
||||
const tag = ciphertext.slice(ciphertext.length - tagLength)
|
||||
// set additional data
|
||||
cipher.setAAD(hash)
|
||||
cipher.setAuthTag(tag)
|
||||
@@ -55,7 +55,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
} else {
|
||||
writeCounter += 1
|
||||
}
|
||||
|
||||
|
||||
authenticate(ciphertext)
|
||||
return result
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
writeCounter = 0
|
||||
isFinished = true
|
||||
}
|
||||
|
||||
|
||||
const data = Binary.build(NOISE_MODE).readBuffer()
|
||||
let hash = Buffer.from(data.byteLength === 32 ? data : sha256(Buffer.from(data)))
|
||||
let salt = hash
|
||||
@@ -109,19 +109,19 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => {
|
||||
authenticate(serverHello!.ephemeral!)
|
||||
mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral!))
|
||||
|
||||
|
||||
const decStaticContent = decrypt(serverHello!.static!)
|
||||
mixIntoKey(Curve.sharedKey(privateKey, decStaticContent))
|
||||
|
||||
|
||||
const certDecoded = decrypt(serverHello!.payload!)
|
||||
const { details: certDetails, signature: certSignature } = proto.NoiseCertificate.decode(certDecoded)
|
||||
|
||||
|
||||
const { key: certKey } = proto.NoiseCertificateDetails.decode(certDetails)
|
||||
|
||||
|
||||
if(Buffer.compare(decStaticContent, certKey) !== 0) {
|
||||
throw new Boom('certification match failed', { statusCode: 400 })
|
||||
}
|
||||
|
||||
|
||||
const keyEnc = encrypt(noiseKey.public)
|
||||
mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!))
|
||||
|
||||
@@ -135,16 +135,16 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const introSize = sentIntro ? 0 : NOISE_WA_HEADER.length
|
||||
|
||||
outBinary.ensureAdditionalCapacity(introSize + 3 + data.byteLength)
|
||||
|
||||
|
||||
if(!sentIntro) {
|
||||
outBinary.writeByteArray(NOISE_WA_HEADER)
|
||||
sentIntro = true
|
||||
}
|
||||
|
||||
|
||||
outBinary.writeUint8(data.byteLength >> 16)
|
||||
outBinary.writeUint16(65535 & data.byteLength)
|
||||
outBinary.write(data)
|
||||
|
||||
|
||||
const bytes = outBinary.readByteArray()
|
||||
return bytes as Uint8Array
|
||||
},
|
||||
@@ -175,5 +175,5 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
|
||||
inBinary.peek(peekSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export const createSignalIdentity = (
|
||||
wid: string,
|
||||
accountSignatureKey: Uint8Array
|
||||
): SignalIdentity => {
|
||||
return {
|
||||
return {
|
||||
identifier: { name: wid, deviceId: 0 },
|
||||
identifierKey: generateSignalPubKey(accountSignatureKey)
|
||||
}
|
||||
@@ -145,7 +145,7 @@ export const decryptGroupSignalProto = (group: string, user: string, msg: Buffer
|
||||
|
||||
export const processSenderKeyMessage = async(
|
||||
authorJid: string,
|
||||
item: proto.ISenderKeyDistributionMessage,
|
||||
item: proto.ISenderKeyDistributionMessage,
|
||||
auth: SignalAuthState
|
||||
) => {
|
||||
const builder = new GroupSessionBuilder(signalStorage(auth))
|
||||
@@ -171,7 +171,7 @@ export const decryptSignalProto = async(user: string, type: 'pkmsg' | 'msg', msg
|
||||
break
|
||||
case 'msg':
|
||||
result = await session.decryptWhisperMessage(msg)
|
||||
break
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -231,7 +231,7 @@ export const parseAndInjectE2ESessions = async(node: BinaryNode, auth: SignalAut
|
||||
const identity = getBinaryNodeChildBuffer(node, 'identity')
|
||||
const jid = node.attrs.jid
|
||||
const registrationId = getBinaryNodeChildUInt(node, 'registration', 4)
|
||||
|
||||
|
||||
const device = {
|
||||
registrationId,
|
||||
identityKey: generateSignalPubKey(identity),
|
||||
|
||||
@@ -130,8 +130,8 @@ export const configureSuccessfulPairing = (
|
||||
id: msgId,
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'pair-device-sign',
|
||||
{
|
||||
tag: 'pair-device-sign',
|
||||
attrs: { },
|
||||
content: [
|
||||
{ tag: 'device-identity', attrs: { 'key-index': `${keyIndex}` }, content: accountEnc }
|
||||
@@ -141,9 +141,9 @@ export const configureSuccessfulPairing = (
|
||||
}
|
||||
|
||||
const authUpdate: Partial<AuthenticationCreds> = {
|
||||
account,
|
||||
me: { id: jid, verifiedName },
|
||||
signalIdentities: [...(signalIdentities || []), identity]
|
||||
account,
|
||||
me: { id: jid, verifiedName },
|
||||
signalIdentities: [...(signalIdentities || []), identity]
|
||||
}
|
||||
return {
|
||||
creds: authUpdate,
|
||||
|
||||
Reference in New Issue
Block a user