chore: remove legacy code

This commit is contained in:
Adhiraj Singh
2022-09-19 14:59:35 +05:30
parent d73585ede9
commit e7f6838fa4
23 changed files with 91 additions and 2921 deletions

View File

@@ -2,7 +2,7 @@ import EventEmitter from 'events'
import { createReadStream } from 'fs'
import { writeFile } from 'fs/promises'
import { createInterface } from 'readline'
import type { CommonBaileysEventEmitter } from '../Types'
import type { BaileysEventEmitter } from '../Types'
import { delay } from './generics'
import { makeMutex } from './make-mutex'
@@ -11,7 +11,7 @@ import { makeMutex } from './make-mutex'
* @param ev The event emitter to read events from
* @param filename File to save to
*/
export const captureEventStream = (ev: CommonBaileysEventEmitter<any>, filename: string) => {
export const captureEventStream = (ev: BaileysEventEmitter, filename: string) => {
const oldEmit = ev.emit
// write mutex so data is appended in order
const writeMutex = makeMutex()
@@ -36,7 +36,7 @@ export const captureEventStream = (ev: CommonBaileysEventEmitter<any>, filename:
* @param delayIntervalMs delay between each event emit
*/
export const readAndEmitEventStream = (filename: string, delayIntervalMs: number = 0) => {
const ev = new EventEmitter() as CommonBaileysEventEmitter<any>
const ev = new EventEmitter() as BaileysEventEmitter
const fireEvents = async() => {
// from: https://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js

View File

@@ -1,7 +1,7 @@
import EventEmitter from 'events'
import { Logger } from 'pino'
import { proto } from '../../WAProto'
import { AuthenticationCreds, BaileysEvent, BaileysEventEmitter, BaileysEventMap, BufferedEventData, Chat, Contact, WAMessage, WAMessageStatus } from '../Types'
import { BaileysEvent, BaileysEventEmitter, BaileysEventMap, BufferedEventData, Chat, Contact, WAMessage, WAMessageStatus } from '../Types'
import { updateMessageWithReaction, updateMessageWithReceipt } from './messages'
import { isRealMessage, shouldIncrementChatUnread } from './process-message'
@@ -28,7 +28,7 @@ type BufferableEvent = typeof BUFFERABLE_EVENT[number]
* this can make processing events extremely efficient -- since everything
* can be done in a single transaction
*/
type BaileysEventData = Partial<BaileysEventMap<AuthenticationCreds>>
type BaileysEventData = Partial<BaileysEventMap>
const BUFFERABLE_EVENT_SET = new Set<BaileysEvent>(BUFFERABLE_EVENT)
@@ -109,7 +109,7 @@ export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter =
ev.off('event', listener)
}
},
emit<T extends BaileysEvent>(event: BaileysEvent, evData: BaileysEventMap<AuthenticationCreds>[T]) {
emit<T extends BaileysEvent>(event: BaileysEvent, evData: BaileysEventMap[T]) {
if(isBuffering && BUFFERABLE_EVENT_SET.has(event)) {
append(data, event as any, evData, logger)
return true
@@ -233,7 +233,7 @@ function append<E extends BufferableEvent>(
break
case 'contacts.update':
const contactUpdates = eventData as BaileysEventMap<any>['contacts.update']
const contactUpdates = eventData as BaileysEventMap['contacts.update']
for(const update of contactUpdates) {
const id = update.id!
// merge into prior upsert
@@ -249,7 +249,7 @@ function append<E extends BufferableEvent>(
break
case 'messages.upsert':
const { messages, type } = eventData as BaileysEventMap<any>['messages.upsert']
const { messages, type } = eventData as BaileysEventMap['messages.upsert']
for(const message of messages) {
const key = stringifyMessageKey(message.key)
const existing = data.messageUpserts[key]
@@ -273,7 +273,7 @@ function append<E extends BufferableEvent>(
break
case 'messages.update':
const msgUpdates = eventData as BaileysEventMap<any>['messages.update']
const msgUpdates = eventData as BaileysEventMap['messages.update']
for(const { key, update } of msgUpdates) {
const keyStr = stringifyMessageKey(key)
const existing = data.messageUpserts[keyStr]
@@ -294,7 +294,7 @@ function append<E extends BufferableEvent>(
break
case 'messages.delete':
const deleteData = eventData as BaileysEventMap<any>['messages.delete']
const deleteData = eventData as BaileysEventMap['messages.delete']
if('keys' in deleteData) {
const { keys } = deleteData
for(const key of keys) {
@@ -315,7 +315,7 @@ function append<E extends BufferableEvent>(
break
case 'messages.reaction':
const reactions = eventData as BaileysEventMap<any>['messages.reaction']
const reactions = eventData as BaileysEventMap['messages.reaction']
for(const { key, reaction } of reactions) {
const keyStr = stringifyMessageKey(key)
const existing = data.messageUpserts[keyStr]
@@ -330,7 +330,7 @@ function append<E extends BufferableEvent>(
break
case 'message-receipt.update':
const receipts = eventData as BaileysEventMap<any>['message-receipt.update']
const receipts = eventData as BaileysEventMap['message-receipt.update']
for(const { key, receipt } of receipts) {
const keyStr = stringifyMessageKey(key)
const existing = data.messageUpserts[keyStr]
@@ -345,7 +345,7 @@ function append<E extends BufferableEvent>(
break
case 'groups.update':
const groupUpdates = eventData as BaileysEventMap<any>['groups.update']
const groupUpdates = eventData as BaileysEventMap['groups.update']
for(const update of groupUpdates) {
const id = update.id!
const groupUpdate = data.groupUpdates[id] || { }

View File

@@ -5,7 +5,7 @@ import { platform, release } from 'os'
import { Logger } from 'pino'
import { proto } from '../../WAProto'
import { version as baileysVersion } from '../Defaults/baileys-version.json'
import { BaileysEventMap, CommonBaileysEventEmitter, DisconnectReason, WACallUpdateType, WAVersion } from '../Types'
import { BaileysEventEmitter, BaileysEventMap, DisconnectReason, WACallUpdateType, WAVersion } from '../Types'
import { BinaryNode, getAllBinaryNodeChildren } from '../WABinary'
const PLATFORM_MAP = {
@@ -165,9 +165,9 @@ export async function promiseTimeout<T>(ms: number | undefined, promise: (resolv
// generate a random ID to attach to a message
export const generateMessageID = () => 'BAE5' + randomBytes(6).toString('hex').toUpperCase()
export function bindWaitForEvent<T extends keyof BaileysEventMap<any>>(ev: CommonBaileysEventEmitter<any>, event: T) {
return async(check: (u: BaileysEventMap<any>[T]) => boolean | undefined, timeoutMs?: number) => {
let listener: (item: BaileysEventMap<any>[T]) => void
export function bindWaitForEvent<T extends keyof BaileysEventMap>(ev: BaileysEventEmitter, event: T) {
return async(check: (u: BaileysEventMap[T]) => boolean | undefined, timeoutMs?: number) => {
let listener: (item: BaileysEventMap[T]) => void
let closeListener: any
await (
promiseTimeout(
@@ -200,9 +200,9 @@ export function bindWaitForEvent<T extends keyof BaileysEventMap<any>>(ev: Commo
}
}
export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter<any>) => bindWaitForEvent(ev, 'connection.update')
export const bindWaitForConnectionUpdate = (ev: BaileysEventEmitter) => bindWaitForEvent(ev, 'connection.update')
export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter<any>, logger: Logger) => {
export const printQRIfNecessaryListener = (ev: BaileysEventEmitter, logger: Logger) => {
ev.on('connection.update', async({ qr }) => {
if(qr) {
const QR = await import('qrcode-terminal')

View File

@@ -10,7 +10,6 @@ export * from './history'
export * from './chat-utils'
export * from './lt-hash'
export * from './auth-utils'
export * from './legacy-msgs'
export * from './baileys-event-stream'
export * from './use-single-file-auth-state'
export * from './use-multi-file-auth-state'

View File

@@ -1,198 +0,0 @@
import { Boom } from '@hapi/boom'
import { randomBytes } from 'crypto'
import { AuthenticationCreds, Contact, CurveKeyPair, DisconnectReason, LegacyAuthenticationCreds, WATag } from '../Types'
import { decodeBinaryNodeLegacy, jidNormalizedUser } from '../WABinary'
import { aesDecrypt, Curve, hkdf, hmacSign } from './crypto'
import { BufferJSON } from './generics'
export const newLegacyAuthCreds = () => ({
clientID: randomBytes(16).toString('base64')
}) as LegacyAuthenticationCreds
export const decodeWAMessage = (
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] === ',') {
commaIndex += 1
}
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()
let json: any
let tags: WATag | undefined
if(data.length) {
const possiblyEnc = (data.length > 32 && data.length % 16 === 0)
if(typeof data === 'string' || !possiblyEnc) {
json = JSON.parse(data.toString()) // parse the JSON
} else {
try {
json = JSON.parse(data.toString())
} catch{
const { macKey, encKey } = auth || {}
if(!macKey || !encKey) {
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
*/
if(fromMe) {
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
json = decodeBinaryNodeLegacy(decrypted, { index: 0 }) // decode the binary message into a JSON array
} else {
throw new Boom('Bad checksum', {
data: {
received: checksum.toString('hex'),
computed: computedChecksum.toString('hex'),
data: data.slice(0, 80).toString(),
tag: messageTag,
message: message.slice(0, 80).toString()
},
statusCode: DisconnectReason.badSession
})
}
}
}
}
return [messageTag, json, tags] as const
}
/**
* Once the QR code is scanned and we can validate our connection, or we resolved the challenge when logging back in
* @private
* @param json
*/
export const validateNewConnection = (
json: { [_: string]: any },
auth: LegacyAuthenticationCreds,
curveKeys: CurveKeyPair
) => {
// set metadata: one's WhatsApp ID [cc][number]@s.whatsapp.net, name on WhatsApp, info about the phone
const onValidationSuccess = () => {
const user: Contact = {
id: jidNormalizedUser(json.wid),
name: json.pushname
}
return { user, auth, phone: json.phone }
}
if(!json.secret) {
// if we didn't get a secret, we don't need it, we're validated
if(json.clientToken && json.clientToken !== auth.clientToken) {
auth = { ...auth, clientToken: json.clientToken }
}
if(json.serverToken && json.serverToken !== auth.serverToken) {
auth = { ...auth, serverToken: json.serverToken }
}
return onValidationSuccess()
}
const secret = Buffer.from(json.secret, 'base64')
if(secret.length !== 144) {
throw new Error ('incorrect secret length received: ' + secret.length)
}
// generate shared key from our private key & the secret shared by the server
const sharedKey = Curve.sharedKey(curveKeys.private, secret.slice(0, 32))
// expand the key to 80 bytes using HKDF
const expandedKey = hkdf(sharedKey as Buffer, 80, { })
// perform HMAC validation.
const hmacValidationKey = expandedKey.slice(32, 64)
const hmacValidationMessage = Buffer.concat([secret.slice(0, 32), secret.slice(64, secret.length)])
const hmac = hmacSign(hmacValidationMessage, hmacValidationKey)
if(!hmac.equals(secret.slice(32, 64))) {
// if the checksums didn't match
throw new Boom('HMAC validation failed', { statusCode: 400 })
}
// computed HMAC should equal secret[32:64]
// expandedKey[64:] + secret[64:] are the keys, encrypted using AES, that are used to encrypt/decrypt the messages recieved from WhatsApp
// they are encrypted using key: expandedKey[0:32]
const encryptedAESKeys = Buffer.concat([
expandedKey.slice(64, expandedKey.length),
secret.slice(64, secret.length),
])
const decryptedKeys = aesDecrypt(encryptedAESKeys, expandedKey.slice(0, 32))
// set the credentials
auth = {
encKey: decryptedKeys.slice(0, 32), // first 32 bytes form the key to encrypt/decrypt messages
macKey: decryptedKeys.slice(32, 64), // last 32 bytes from the key to sign messages
clientToken: json.clientToken,
serverToken: json.serverToken,
clientID: auth.clientID,
}
return onValidationSuccess()
}
export const computeChallengeResponse = (challenge: string, auth: LegacyAuthenticationCreds) => {
const bytes = Buffer.from(challenge, 'base64') // decode the base64 encoded challenge string
const signed = hmacSign(bytes, auth.macKey).toString('base64') // sign the challenge string with our macKey
return ['admin', 'challenge', signed, auth.serverToken, auth.clientID] // prepare to send this signed string with the serverToken & clientID
}
export const useSingleFileLegacyAuthState = (file: string) => {
// require fs here so that in case "fs" is not available -- the app does not crash
const { readFileSync, writeFileSync, existsSync } = require('fs')
let state: LegacyAuthenticationCreds
if(existsSync(file)) {
state = JSON.parse(
readFileSync(file, { encoding: 'utf-8' }),
BufferJSON.reviver
)
if(typeof state.encKey === 'string') {
state.encKey = Buffer.from(state.encKey, 'base64')
}
if(typeof state.macKey === 'string') {
state.macKey = Buffer.from(state.macKey, 'base64')
}
} else {
state = newLegacyAuthCreds()
}
return {
state,
saveState: () => {
const str = JSON.stringify(state, BufferJSON.replacer, 2)
writeFileSync(file, str)
}
}
}
export const getAuthenticationCredsType = (creds: LegacyAuthenticationCreds | AuthenticationCreds) => {
if('clientID' in creds && !!creds.clientID) {
return 'legacy'
}
if('noiseKey' in creds && !!creds.noiseKey) {
return 'md'
}
}

View File

@@ -12,7 +12,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, CommonSocketConfig, DownloadableMessage, MediaConnInfo, MediaDecryptionKeyInfo, MediaType, MessageType, WAGenericMediaMessage, 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 { generateMessageID } from './generics'
@@ -514,7 +514,7 @@ export function extensionForMediaMessage(message: WAMessageContent) {
return extension
}
export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: CommonSocketConfig, refreshMediaConn: (force: boolean) => Promise<MediaConnInfo>): WAMediaUploadFunction => {
export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: SocketConfig, refreshMediaConn: (force: boolean) => Promise<MediaConnInfo>): WAMediaUploadFunction => {
return async(stream, { mediaType, fileEncSha256B64, timeoutMs }) => {
const { default: axios } = await import('axios')
// send a query JSON to obtain the url & auth token to upload our media
@@ -646,7 +646,7 @@ export const encryptMediaRetryRequest = (
export const decodeMediaRetryNode = (node: BinaryNode) => {
const rmrNode = getBinaryNodeChild(node, 'rmr')!
const event: BaileysEventMap<any>['messages.media-update'][number] = {
const event: BaileysEventMap['messages.media-update'][number] = {
key: {
id: node.attrs.id,
remoteJid: rmrNode.attrs.jid,