project: Move to ESM Modules

This commit is contained in:
Rajeh Taher
2025-07-17 13:54:17 +03:00
parent 19124426b2
commit 787aed88b8
69 changed files with 5143 additions and 4757 deletions
+10 -9
View File
@@ -12,7 +12,7 @@ import type {
} from '../Types'
import { Curve, signedKeyPair } from './crypto'
import { delay, generateRegistrationId } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
/**
* Adds caching capability to a SignalKeyStore
@@ -67,8 +67,8 @@ export function makeCacheableSignalKeyStore(
async set(data) {
let keys = 0
for (const type in data) {
for (const id in data[type]) {
cache.set(getUniqueId(type, id), data[type][id])
for (const id in data[type as keyof SignalDataTypeMap]) {
cache.set(getUniqueId(type, id), data[type as keyof SignalDataTypeMap]![id])
keys += 1
}
}
@@ -118,7 +118,7 @@ export const addTransactionCapability = (
Object.assign(transactionCache[type]!, result)
}
return ids.reduce((dict, id) => {
return ids.reduce((dict: { [T in string]: any }, id) => {
const value = transactionCache[type]?.[id]
if (value) {
dict[id] = value
@@ -133,12 +133,13 @@ export const addTransactionCapability = (
set: data => {
if (isInTransaction()) {
logger.trace({ types: Object.keys(data) }, 'caching in transaction')
for (const key in data) {
transactionCache[key] = transactionCache[key] || {}
Object.assign(transactionCache[key], data[key])
for (const key_ in data) {
const key = key_ as keyof SignalDataTypeMap
transactionCache[key] = transactionCache[key] || ({} as any)
Object.assign(transactionCache[key]!, data[key])
mutations[key] = mutations[key] || {}
Object.assign(mutations[key], data[key])
mutations[key] = mutations[key] || ({} as any)
Object.assign(mutations[key]!, data[key])
}
} else {
return state.set(data)
+1 -1
View File
@@ -18,7 +18,7 @@ export const captureEventStream = (ev: BaileysEventEmitter, filename: string) =>
// monkey patch eventemitter to capture all events
ev.emit = function (...args: any[]) {
const content = JSON.stringify({ timestamp: Date.now(), event: args[0], data: args[1] }) + '\n'
const result = oldEmit.apply(ev, args)
const result = oldEmit.apply(ev, args as any)
writeMutex.mutex(async () => {
await writeFile(filename, content, { flag: 'a' })
+2 -2
View File
@@ -3,7 +3,7 @@ import { createHash } from 'crypto'
import { createWriteStream, promises as fs } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import {
import type {
CatalogCollection,
CatalogStatus,
OrderDetails,
@@ -14,7 +14,7 @@ import {
WAMediaUpload,
WAMediaUploadFunction
} from '../Types'
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../WABinary'
import { type BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../WABinary'
import { generateMessageIDV2 } from './generics'
import { getStream, getUrlFromDirectPath } from './messages-media'
+18 -14
View File
@@ -1,7 +1,7 @@
import { Boom } from '@hapi/boom'
import { AxiosRequestConfig } from 'axios'
import type { AxiosRequestConfig } from 'axios'
import { proto } from '../../WAProto'
import {
import type {
BaileysEventEmitter,
Chat,
ChatModification,
@@ -14,9 +14,13 @@ import {
WAPatchCreate,
WAPatchName
} from '../Types'
import { ChatLabelAssociation, LabelAssociationType, MessageLabelAssociation } from '../Types/LabelAssociation'
import {
BinaryNode,
type ChatLabelAssociation,
LabelAssociationType,
type MessageLabelAssociation
} from '../Types/LabelAssociation'
import {
type BinaryNode,
getBinaryNodeChild,
getBinaryNodeChildren,
isJidGroup,
@@ -25,7 +29,7 @@ import {
} from '../WABinary'
import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto'
import { toNumber } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
import { LT_HASH_ANTI_TAMPERING } from './lt-hash'
import { downloadContentFromMessage } from './messages-media'
@@ -343,7 +347,7 @@ export const extractSyncdPatches = async (result: BinaryNode, options: AxiosRequ
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)
@@ -616,7 +620,7 @@ export const chatModificationToAppPatch = (mod: ChatModification, jid: string) =
operation: mod.contact ? OP.SET : OP.REMOVE
}
} else if ('star' in mod) {
const key = mod.star.messages[0]
const key = mod.star.messages[0]!
patch = {
syncAction: {
starAction: {
@@ -753,7 +757,7 @@ export const processSyncAction = (
{
id,
muteEndTime: action.muteAction?.muted ? toNumber(action.muteAction.muteEndTimestamp) : null,
conditional: getChatUpdateConditional(id, undefined)
conditional: getChatUpdateConditional(id!, undefined)
}
])
} else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
@@ -782,7 +786,7 @@ export const processSyncAction = (
{
id,
archived: isArchived,
conditional: getChatUpdateConditional(id, msgRange)
conditional: getChatUpdateConditional(id!, msgRange)
}
])
} else if (action?.markChatAsReadAction) {
@@ -796,7 +800,7 @@ export const processSyncAction = (
{
id,
unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
conditional: getChatUpdateConditional(id, markReadAction?.messageRange)
conditional: getChatUpdateConditional(id!, markReadAction?.messageRange)
}
])
} else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
@@ -812,7 +816,7 @@ export const processSyncAction = (
} else if (action?.contactAction) {
ev.emit('contacts.upsert', [
{
id: id,
id: id!,
name: action.contactAction.fullName!,
lid: action.contactAction.lidJid || undefined,
jid: isJidUser(id) ? id : undefined
@@ -828,7 +832,7 @@ export const processSyncAction = (
{
id,
pinned: action.pinAction?.pinned ? toNumber(action.timestamp) : null,
conditional: getChatUpdateConditional(id, undefined)
conditional: getChatUpdateConditional(id!, undefined)
}
])
} else if (action?.unarchiveChatsSetting) {
@@ -853,13 +857,13 @@ export const processSyncAction = (
])
} else if (action?.deleteChatAction || type === 'deleteChat') {
if (!isInitialSync) {
ev.emit('chats.delete', [id])
ev.emit('chats.delete', [id!])
}
} else if (action?.labelEditAction) {
const { name, color, deleted, predefinedId } = action.labelEditAction
ev.emit('labels.edit', {
id,
id: id!,
name: name!,
color: color!,
deleted: deleted!,
+2 -1
View File
@@ -1,7 +1,8 @@
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from 'crypto'
/* @ts-ignore */
import * as libsignal from 'libsignal'
import { KEY_BUNDLE_TYPE } from '../Defaults'
import { KeyPair } from '../Types'
import type { KeyPair } from '../Types'
// insure browser & node compatibility
const { subtle } = globalThis.crypto
+14 -14
View File
@@ -1,9 +1,9 @@
import { Boom } from '@hapi/boom'
import { proto } from '../../WAProto'
import { SignalRepository, WAMessage, WAMessageKey } from '../Types'
import type { SignalRepository, WAMessage, WAMessageKey } from '../Types'
import {
areJidsSameUser,
BinaryNode,
type BinaryNode,
isJidBroadcast,
isJidGroup,
isJidMetaIa,
@@ -13,7 +13,7 @@ import {
isLidUser
} from '../WABinary'
import { unpadRandomMax16 } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
export const NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node'
export const MISSING_KEYS_ERROR_TEXT = 'Key used already or never filled'
@@ -62,17 +62,17 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
if (isJidUser(from) || isLidUser(from)) {
if (recipient && !isJidMetaIa(recipient)) {
if (!isMe(from) && !isMeLid(from)) {
if (!isMe(from!) && !isMeLid(from!)) {
throw new Boom('receipient present, but msg not from me', { data: stanza })
}
chatId = recipient
} else {
chatId = from
chatId = from!
}
msgType = 'chat'
author = from
author = from!
} else if (isJidGroup(from)) {
if (!participant) {
throw new Boom('No participant in group message')
@@ -80,30 +80,30 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
msgType = 'group'
author = participant
chatId = from
chatId = from!
} else if (isJidBroadcast(from)) {
if (!participant) {
throw new Boom('No participant in group message')
}
const isParticipantMe = isMe(participant)
if (isJidStatusBroadcast(from)) {
if (isJidStatusBroadcast(from!)) {
msgType = isParticipantMe ? 'direct_peer_status' : 'other_status'
} else {
msgType = isParticipantMe ? 'peer_broadcast' : 'other_broadcast'
}
chatId = from
chatId = from!
author = participant
} else if (isJidNewsletter(from)) {
msgType = 'newsletter'
chatId = from
author = from
chatId = from!
author = from!
} else {
throw new Boom('Unknown message type', { data: stanza })
}
const fromMe = (isLidUser(from) ? isMeLid : isMe)(stanza.attrs.participant || stanza.attrs.from)
const fromMe = (isLidUser(from) ? isMeLid : isMe)((stanza.attrs.participant || stanza.attrs.from)!)
const pushname = stanza?.attrs?.notify
const key: WAMessageKey = {
@@ -120,7 +120,7 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
const fullMessage: WAMessage = {
key,
messageTimestamp: +stanza.attrs.t,
messageTimestamp: +stanza.attrs.t!,
pushName: pushname,
broadcast: isJidBroadcast(from)
}
@@ -221,7 +221,7 @@ export const decryptMessageNode = (
} else {
fullMessage.message = msg
}
} catch (err) {
} catch (err: any) {
logger.error({ key: fullMessage.key, err }, 'failed to decrypt message')
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
fullMessage.messageStubParameters = [err.message]
+8 -8
View File
@@ -1,6 +1,6 @@
import EventEmitter from 'events'
import { proto } from '../../WAProto'
import {
import type {
BaileysEvent,
BaileysEventEmitter,
BaileysEventMap,
@@ -8,11 +8,11 @@ import {
Chat,
ChatUpdate,
Contact,
WAMessage,
WAMessageStatus
WAMessage
} from '../Types'
import { WAMessageStatus } from '../Types'
import { trimUndefined } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
import { updateMessageWithReaction, updateMessageWithReceipt } from './messages'
import { isRealMessage, shouldIncrementChatUnread } from './process-message'
@@ -79,7 +79,7 @@ export const makeEventBuffer = (logger: ILogger): BaileysBufferableEventEmitter
// take the generic event and fire it as a baileys event
ev.on('event', (map: BaileysEventData) => {
for (const event in map) {
ev.emit(event, map[event])
ev.emit(event, map[event as keyof BaileysEventMap])
}
})
@@ -249,7 +249,7 @@ function append<E extends BufferableEvent>(
for (const chat of eventData as Chat[]) {
let upsert = data.chatUpserts[chat.id]
if (!upsert) {
upsert = data.historySets[chat.id]
upsert = data.historySets.chats[chat.id]
if (upsert) {
logger.debug({ chatId: chat.id }, 'absorbed chat upsert in chat set')
}
@@ -339,7 +339,7 @@ function append<E extends BufferableEvent>(
}
if (data.contactUpdates[contact.id]) {
upsert = Object.assign(data.contactUpdates[contact.id], trimUndefined(contact)) as Contact
upsert = Object.assign(data.contactUpdates[contact.id]!, trimUndefined(contact)) as Contact
delete data.contactUpdates[contact.id]
}
}
@@ -550,7 +550,7 @@ function consolidateEvents(data: BufferedEventData) {
const messageUpsertList = Object.values(data.messageUpserts)
if (messageUpsertList.length) {
const type = messageUpsertList[0].type
const type = messageUpsertList[0]!.type
map['messages.upsert'] = {
messages: messageUpsertList.map(m => m.message),
type
+20 -14
View File
@@ -1,19 +1,19 @@
import { Boom } from '@hapi/boom'
import axios, { AxiosRequestConfig } from 'axios'
import axios, { type AxiosRequestConfig } from 'axios'
import { createHash, randomBytes } from 'crypto'
import { platform, release } from 'os'
import { proto } from '../../WAProto'
import { version as baileysVersion } from '../Defaults/baileys-version.json'
import {
import type {
BaileysEventEmitter,
BaileysEventMap,
BrowsersMap,
ConnectionState,
DisconnectReason,
WACallUpdateType,
WAVersion
} from '../Types'
import { BinaryNode, getAllBinaryNodeChildren, jidDecode } from '../WABinary'
import { DisconnectReason } from '../Types'
import { type BinaryNode, getAllBinaryNodeChildren, jidDecode } from '../WABinary'
const PLATFORM_MAP = {
aix: 'AIX',
@@ -22,7 +22,11 @@ const PLATFORM_MAP = {
android: 'Android',
freebsd: 'FreeBSD',
openbsd: 'OpenBSD',
sunos: 'Solaris'
sunos: 'Solaris',
linux: undefined,
haiku: undefined,
cygwin: undefined,
netbsd: undefined
}
export const Browsers: BrowsersMap = {
@@ -35,13 +39,13 @@ export const Browsers: BrowsersMap = {
}
export const getPlatformId = (browser: string) => {
const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase()]
const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase() as any]
return platformType ? platformType.toString() : '1' //chrome
}
export const BufferJSON = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
replacer: (k, value: any) => {
replacer: (k: any, value: any) => {
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') }
}
@@ -50,7 +54,7 @@ export const BufferJSON = {
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reviver: (_, value: any) => {
reviver: (_: any, value: any) => {
if (typeof value === 'object' && !!value && (value.buffer === true || value.type === 'Buffer')) {
const val = value.data || value.value
return typeof val === 'string' ? Buffer.from(val, 'base64') : Buffer.from(val || [])
@@ -65,8 +69,10 @@ export const getKeyAuthor = (key: proto.IMessageKey | undefined | null, meId = '
export const writeRandomPadMax16 = (msg: Uint8Array) => {
const pad = randomBytes(1)
pad[0] &= 0xf
if (!pad[0]) {
if (pad[0]) {
pad[0] &= 0xf
} else {
pad[0] = 0xf
}
@@ -79,7 +85,7 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
throw new Error('unpadPkcs7 given empty bytes')
}
var r = t[t.length - 1]
var r = t[t.length - 1]!
if (r > t.length) {
throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`)
}
@@ -90,7 +96,7 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
export const encodeWAMessage = (message: proto.IMessage) => writeRandomPadMax16(proto.Message.encode(message).finish())
export const generateRegistrationId = (): number => {
return Uint16Array.from(randomBytes(2))[0] & 16383
return Uint16Array.from(randomBytes(2))[0]! & 16383
}
export const encodeBigEndian = (e: number, t = 4) => {
@@ -135,7 +141,7 @@ export const delay = (ms: number) => delayCancellable(ms).delay
export const delayCancellable = (ms: number) => {
const stack = new Error().stack
let timeout: NodeJS.Timeout
let reject: (error) => void
let reject: (error: any) => void
const delay: Promise<void> = new Promise((resolve, _reject) => {
timeout = setTimeout(resolve, ms)
reject = _reject
@@ -157,7 +163,7 @@ export const delayCancellable = (ms: number) => {
export async function promiseTimeout<T>(
ms: number | undefined,
promise: (resolve: (v: T) => void, reject: (error) => void) => void
promise: (resolve: (v: T) => void, reject: (error: any) => void) => void
) {
if (!ms) {
return new Promise(promise)
+3 -2
View File
@@ -1,8 +1,9 @@
import { AxiosRequestConfig } from 'axios'
import type { AxiosRequestConfig } from 'axios'
import { promisify } from 'util'
import { inflate } from 'zlib'
import { proto } from '../../WAProto'
import { Chat, Contact, WAMessageStubType } from '../Types'
import type { Chat, Contact } from '../Types'
import { WAMessageStubType } from '../Types'
import { isJidUser } from '../WABinary'
import { toNumber } from './generics'
import { normalizeMessageContent } from './messages'
+6 -6
View File
@@ -1,6 +1,6 @@
import { AxiosRequestConfig } from 'axios'
import { WAMediaUploadFunction, WAUrlInfo } from '../Types'
import { ILogger } from './logger'
import type { AxiosRequestConfig } from 'axios'
import type { WAMediaUploadFunction, WAUrlInfo } from '../Types'
import type { ILogger } from './logger'
import { prepareWAMessageMedia } from './messages'
import { extractImageThumb, getHttpStream } from './messages-media'
@@ -85,7 +85,7 @@ export const getUrlInfo = async (
if (opts.uploadImage) {
const { imageMessage } = await prepareWAMessageMedia(
{ image: { url: image } },
{ image: { url: image! } },
{
upload: opts.uploadImage,
mediaTypeOverride: 'thumbnail-link',
@@ -97,14 +97,14 @@ export const getUrlInfo = async (
} else {
try {
urlInfo.jpegThumbnail = image ? (await getCompressedJpegThumbnail(image, opts)).buffer : undefined
} catch (error) {
} catch (error: any) {
opts.logger?.debug({ err: error.stack, url: previewLink }, 'error in generating thumbnail')
}
}
return urlInfo
}
} catch (error) {
} catch (error: any) {
if (!error.message.includes('receive a valid')) {
throw error
}
+5 -5
View File
@@ -3,11 +3,11 @@ import P from 'pino'
export interface ILogger {
level: string
child(obj: Record<string, unknown>): ILogger
trace(obj: unknown, msg?: string)
debug(obj: unknown, msg?: string)
info(obj: unknown, msg?: string)
warn(obj: unknown, msg?: string)
error(obj: unknown, msg?: string)
trace(obj: unknown, msg?: string): void
debug(obj: unknown, msg?: string): void
info(obj: unknown, msg?: string): void
warn(obj: unknown, msg?: string): void
error(obj: unknown, msg?: string): void
}
export default P({ timestamp: () => `,"time":"${new Date().toJSON()}"` })
+35 -30
View File
@@ -5,56 +5,61 @@ import { hkdf } from './crypto'
* 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.
*/
const o = 128
class d {
class LTHash {
salt: string
constructor(e: string) {
this.salt = e
}
add(e, t) {
var r = this
async add(e: ArrayBuffer, t: ArrayBuffer[]): Promise<ArrayBuffer> {
for (const item of t) {
e = r._addSingle(e, item)
e = await this._addSingle(e, item)
}
return e
}
subtract(e, t) {
var r = this
async subtract(e: ArrayBuffer, t: ArrayBuffer[]): Promise<ArrayBuffer> {
for (const item of t) {
e = r._subtractSingle(e, item)
e = await this._subtractSingle(e, item)
}
return e
}
subtractThenAdd(e, t, r) {
var n = this
return n.add(n.subtract(e, r), t)
}
async _addSingle(e, t) {
var r = this
const n = new Uint8Array(await hkdf(Buffer.from(t), o, { info: r.salt })).buffer
return r.performPointwiseWithOverflow(await e, n, (e, t) => e + t)
}
async _subtractSingle(e, t) {
var r = this
const n = new Uint8Array(await hkdf(Buffer.from(t), o, { info: r.salt })).buffer
return r.performPointwiseWithOverflow(await e, n, (e, t) => e - t)
async subtractThenAdd(e: ArrayBuffer, addList: ArrayBuffer[], subtractList: ArrayBuffer[]): Promise<ArrayBuffer> {
const subtracted = await this.subtract(e, subtractList)
return this.add(subtracted, addList)
}
performPointwiseWithOverflow(e, t, r) {
const n = new DataView(e),
i = new DataView(t),
a = new ArrayBuffer(n.byteLength),
s = new DataView(a)
for (let e = 0; e < n.byteLength; e += 2) {
s.setUint16(e, r(n.getUint16(e, !0), i.getUint16(e, !0)), !0)
private async _addSingle(e: ArrayBuffer, t: ArrayBuffer): Promise<ArrayBuffer> {
const derived = new Uint8Array(await hkdf(Buffer.from(t), o, { info: this.salt })).buffer
return this.performPointwiseWithOverflow(e, derived, (a, b) => a + b)
}
private async _subtractSingle(e: ArrayBuffer, t: ArrayBuffer): Promise<ArrayBuffer> {
const derived = new Uint8Array(await hkdf(Buffer.from(t), o, { info: this.salt })).buffer
return this.performPointwiseWithOverflow(e, derived, (a, b) => a - b)
}
private performPointwiseWithOverflow(
e: ArrayBuffer,
t: ArrayBuffer,
op: (a: number, b: number) => number
): ArrayBuffer {
const n = new DataView(e)
const i = new DataView(t)
const out = new ArrayBuffer(n.byteLength)
const s = new DataView(out)
for (let offset = 0; offset < n.byteLength; offset += 2) {
s.setUint16(offset, op(n.getUint16(offset, true), i.getUint16(offset, true)), true)
}
return a
return out
}
}
export const LT_HASH_ANTI_TAMPERING = new d('WhatsApp Patch Integrity')
export const LT_HASH_ANTI_TAMPERING = new LTHash('WhatsApp Patch Integrity')
+15 -12
View File
@@ -1,5 +1,5 @@
import { Boom } from '@hapi/boom'
import axios, { AxiosRequestConfig } from 'axios'
import axios, { type AxiosRequestConfig } from 'axios'
import { exec } from 'child_process'
import * as Crypto from 'crypto'
import { once } from 'events'
@@ -11,7 +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 {
import type {
BaileysEventMap,
DownloadableMessage,
MediaConnInfo,
@@ -24,10 +24,10 @@ import {
WAMediaUploadFunction,
WAMessageContent
} from '../Types'
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary'
import { type BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary'
import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto'
import { generateMessageIDV2 } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
const getTmpFilesDirectory = () => tmpdir()
@@ -132,6 +132,8 @@ const extractVideoThumb = async (
})
export const extractImageThumb = async (bufferOrFilePath: Readable | Buffer | string, width = 32) => {
// TODO: Move entirely to sharp, removing jimp as it supports readable streams
// This will have positive speed and performance impacts as well as minimizing RAM usage.
if (bufferOrFilePath instanceof Readable) {
bufferOrFilePath = await toBuffer(bufferOrFilePath)
}
@@ -590,7 +592,7 @@ export const downloadEncryptedContent = async (
try {
pushBytes(aes.update(data), b => this.push(b))
callback()
} catch (error) {
} catch (error: any) {
callback(error)
}
},
@@ -598,7 +600,7 @@ export const downloadEncryptedContent = async (
try {
pushBytes(aes.final(), b => this.push(b))
callback()
} catch (error) {
} catch (error: any) {
callback(error)
}
}
@@ -607,14 +609,14 @@ export const downloadEncryptedContent = async (
}
export function extensionForMediaMessage(message: WAMessageContent) {
const getExtension = (mimetype: string) => mimetype.split(';')[0].split('/')[1]
const type = Object.keys(message)[0] as MessageType
const getExtension = (mimetype: string) => mimetype.split(';')[0]?.split('/')[1]
const type = Object.keys(message)[0] as Exclude<MessageType, 'toJSON'>
let extension: string
if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
extension = '.jpeg'
} else {
const messageContent = message[type] as WAGenericMediaMessage
extension = getExtension(messageContent.mimetype!)
extension = getExtension(messageContent.mimetype!)!
}
return extension
@@ -667,7 +669,7 @@ export const getWAUploadToServer = (
uploadInfo = await refreshMediaConn(true)
throw new Error(`upload failed, reason: ${JSON.stringify(result)}`)
}
} catch (error) {
} catch (error: any) {
if (axios.isAxiosError(error)) {
result = error.response?.data
}
@@ -751,7 +753,7 @@ export const decodeMediaRetryNode = (node: BinaryNode) => {
const errorNode = getBinaryNodeChild(node, 'error')
if (errorNode) {
const errorCode = +errorNode.attrs.code
const errorCode = +errorNode.attrs.code!
event.error = new Boom(`Failed to re-upload media (${errorCode})`, {
data: errorNode.attrs,
statusCode: getStatusCodeForMediaRetry(errorCode)
@@ -780,7 +782,8 @@ export const decryptMediaRetryData = async (
return proto.MediaRetryNotification.decode(plaintext)
}
export const getStatusCodeForMediaRetry = (code: number) => MEDIA_RETRY_STATUS_MAP[code]
export const getStatusCodeForMediaRetry = (code: number) =>
MEDIA_RETRY_STATUS_MAP[code as proto.MediaRetryNotification.ResultType]
const MEDIA_RETRY_STATUS_MAP = {
[proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
+41 -32
View File
@@ -5,7 +5,7 @@ import { promises as fs } from 'fs'
import { type Transform } from 'stream'
import { proto } from '../../WAProto'
import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults'
import {
import type {
AnyMediaMessageContent,
AnyMessageContent,
DownloadableMessage,
@@ -13,27 +13,25 @@ import {
MessageContentGenerationOptions,
MessageGenerationOptions,
MessageGenerationOptionsFromContent,
MessageType,
MessageUserReceipt,
WAMediaUpload,
WAMessage,
WAMessageContent,
WAMessageStatus,
WAProto,
WATextMessage
} from '../Types'
import { WAMessageStatus, WAProto } from '../Types'
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary'
import { sha256 } from './crypto'
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics'
import { ILogger } from './logger'
import type { ILogger } from './logger'
import {
downloadContentFromMessage,
encryptedStream,
generateThumbnail,
getAudioDuration,
getAudioWaveform,
getRawMediaUploadData,
MediaDownloadOptions
getRawMediaUploadData,
type MediaDownloadOptions
} from './messages-media'
type MediaUploadData = {
@@ -86,14 +84,14 @@ export const generateLinkPreviewIfRequired = async (
try {
const urlInfo = await getUrlInfo(url)
return urlInfo
} catch (error) {
} catch (error: any) {
// ignore if fails
logger?.warn({ trace: error.stack }, 'url generation failed')
}
}
}
const assertColor = async color => {
const assertColor = async (color: any) => {
let assertedColor
if (typeof color === 'number') {
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
@@ -127,10 +125,10 @@ export const prepareWAMessageMedia = async (
const uploadData: MediaUploadData = {
...message,
media: message[mediaType]
media: (message as any)[mediaType]
}
delete uploadData[mediaType]
delete (uploadData as any)[mediaType]
// check if cacheable + generate cache key
const cacheableKey =
typeof uploadData.media === 'object' &&
'url' in uploadData.media &&
@@ -154,7 +152,7 @@ export const prepareWAMessageMedia = async (
const obj = WAProto.Message.decode(mediaBuff)
const key = `${mediaType}Message`
Object.assign(obj[key], { ...uploadData, media: undefined })
Object.assign(obj[key as keyof proto.Message]!, { ...uploadData, media: undefined })
return obj
}
@@ -262,7 +260,7 @@ export const prepareWAMessageMedia = async (
logger?.debug('computed backgroundColor audio status')
}
} catch (error) {
logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
logger?.warn({ trace: (error as any).stack }, 'failed to obtain extra info')
}
})()
]).finally(async () => {
@@ -279,7 +277,7 @@ export const prepareWAMessageMedia = async (
})
const obj = WAProto.Message.fromObject({
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
[`${mediaType}Message`]: MessageTypeProto[mediaType as keyof typeof MessageTypeProto].fromObject({
url: mediaUrl,
directPath,
mediaKey,
@@ -335,9 +333,9 @@ export const generateForwardMessageContent = (message: WAMessage, forceForward?:
content = normalizeMessageContent(content)
content = proto.Message.decode(proto.Message.encode(content!).finish())
let key = Object.keys(content)[0] as MessageType
let key = Object.keys(content)[0] as keyof proto.IMessage
let score = content[key].contextInfo?.forwardingScore || 0
let score = (content?.[key] as { contextInfo: proto.IContextInfo })?.contextInfo?.forwardingScore || 0
score += message.key.fromMe && !forceForward ? 0 : 1
if (key === 'conversation') {
content.extendedTextMessage = { text: content[key] }
@@ -346,10 +344,11 @@ export const generateForwardMessageContent = (message: WAMessage, forceForward?:
key = 'extendedTextMessage'
}
const key_ = content?.[key] as { contextInfo: proto.IContextInfo }
if (score > 0) {
content[key].contextInfo = { forwardingScore: score, isForwarded: true }
key_.contextInfo = { forwardingScore: score, isForwarded: true }
} else {
content[key].contextInfo = {}
key_.contextInfo = {}
}
return content
@@ -403,7 +402,7 @@ export const generateWAMessageContent = async (
}
if (contactLen === 1) {
m.contactMessage = WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0])
m.contactMessage = WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0]!)
} else {
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.fromObject(message.contacts)
}
@@ -541,9 +540,12 @@ export const generateWAMessageContent = async (
}
if ('mentions' in message && message.mentions?.length) {
const [messageType] = Object.keys(m)
m[messageType].contextInfo = m[messageType] || {}
m[messageType].contextInfo.mentionedJid = message.mentions
const messageType = Object.keys(m)[0]! as Exclude<keyof proto.IMessage, 'conversation'>
const key = m[messageType]
if ('contextInfo' in key!) {
key.contextInfo = key.contextInfo || {}
key.contextInfo.mentionedJid = message.mentions
}
}
if ('edit' in message) {
@@ -558,9 +560,11 @@ export const generateWAMessageContent = async (
}
if ('contextInfo' in message && !!message.contextInfo) {
const [messageType] = Object.keys(m)
m[messageType] = m[messageType] || {}
m[messageType].contextInfo = message.contextInfo
const messageType = Object.keys(m)[0]! as Exclude<keyof proto.IMessage, 'conversation'>
const key = m[messageType]
if ('contextInfo' in key!) {
key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
}
}
return WAProto.Message.fromObject(m)
@@ -578,7 +582,7 @@ export const generateWAMessageFromContent = (
}
const innerMessage = normalizeMessageContent(message)!
const key: string = getContentType(innerMessage)!
const key = getContentType(innerMessage)! as Exclude<keyof proto.IMessage, 'conversation'>
const timestamp = unixTimestampSeconds(options.timestamp)
const { quoted, userJid } = options
@@ -597,7 +601,8 @@ export const generateWAMessageFromContent = (
delete quotedContent.contextInfo
}
const contextInfo: proto.IContextInfo = innerMessage[key].contextInfo || {}
const contextInfo: proto.IContextInfo =
('contextInfo' in innerMessage[key]! && innerMessage[key]?.contextInfo) || {}
contextInfo.participant = jidNormalizedUser(participant!)
contextInfo.stanzaId = quoted.key.id
contextInfo.quotedMessage = quotedMsg
@@ -608,7 +613,10 @@ export const generateWAMessageFromContent = (
contextInfo.remoteJid = quoted.key.remoteJid
}
innerMessage[key].contextInfo = contextInfo
if (contextInfo && innerMessage[key]) {
/* @ts-ignore */
innerMessage[key].contextInfo = contextInfo
}
}
if (
@@ -621,8 +629,9 @@ export const generateWAMessageFromContent = (
// newsletters don't support ephemeral messages
!isJidNewsletter(jid)
) {
/* @ts-ignore */
innerMessage[key].contextInfo = {
...(innerMessage[key].contextInfo || {}),
...((innerMessage[key] as any).contextInfo || {}),
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
}
@@ -653,7 +662,7 @@ export const generateWAMessage = async (jid: string, content: AnyMessageContent,
}
/** Get the key to access the true type of content */
export const getContentType = (content: WAProto.IMessage | undefined) => {
export const getContentType = (content: proto.IMessage | undefined) => {
if (content) {
const keys = Object.keys(content)
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
@@ -837,7 +846,7 @@ export function getAggregateVotesInPollMessage(
data = voteHashMap[hash]
}
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId))
voteHashMap[hash]!.voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId))
}
}
+9 -8
View File
@@ -1,10 +1,11 @@
import { Boom } from '@hapi/boom'
import { proto } from '../../WAProto'
import { NOISE_MODE, WA_CERT_DETAILS } from '../Defaults'
import { KeyPair } from '../Types'
import { BinaryNode, decodeBinaryNode } from '../WABinary'
import type { KeyPair } from '../Types'
import type { BinaryNode } from '../WABinary'
import { decodeBinaryNode } from '../WABinary'
import { aesDecryptGCM, aesEncryptGCM, Curve, hkdf, sha256 } from './crypto'
import { ILogger } from './logger'
import type { ILogger } from './logger'
const generateIV = (counter: number) => {
const iv = new ArrayBuffer(12)
@@ -64,17 +65,17 @@ export const makeNoiseHandler = ({
const mixIntoKey = async (data: Uint8Array) => {
const [write, read] = await localHKDF(data)
salt = write
encKey = read
decKey = read
salt = write!
encKey = read!
decKey = read!
readCounter = 0
writeCounter = 0
}
const finishInit = async () => {
const [write, read] = await localHKDF(new Uint8Array(0))
encKey = write
decKey = read
encKey = write!
decKey = read!
hash = Buffer.from([])
readCounter = 0
writeCounter = 0
+6 -5
View File
@@ -1,6 +1,6 @@
import { AxiosRequestConfig } from 'axios'
import type { AxiosRequestConfig } from 'axios'
import { proto } from '../../WAProto'
import {
import type {
AuthenticationCreds,
BaileysEventEmitter,
CacheStore,
@@ -9,15 +9,15 @@ import {
ParticipantAction,
RequestJoinAction,
RequestJoinMethod,
SignalKeyStoreWithTransaction,
WAMessageStubType
SignalKeyStoreWithTransaction
} from '../Types'
import { WAMessageStubType } from '../Types'
import { getContentType, normalizeMessageContent } from '../Utils/messages'
import { areJidsSameUser, isJidBroadcast, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary'
import { aesDecryptGCM, hmacSign } from './crypto'
import { toNumber } from './generics'
import { downloadAndProcessHistorySyncNotification } from './history'
import { ILogger } from './logger'
import type { ILogger } from './logger'
type ProcessMessageContext = {
shouldProcessHistoryMsg: boolean
@@ -273,6 +273,7 @@ const processMessage = async (
}
}
break
case proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
ev.emit('messages.update', [
{
+17 -9
View File
@@ -1,7 +1,6 @@
import { chunk } from 'lodash'
import { KEY_BUNDLE_TYPE } from '../Defaults'
import { SignalRepository } from '../Types'
import {
import type { SignalRepository } from '../Types'
import type {
AuthenticationCreds,
AuthenticationState,
KeyPair,
@@ -11,19 +10,28 @@ import {
} from '../Types/Auth'
import {
assertNodeErrorFree,
BinaryNode,
type BinaryNode,
getBinaryNodeChild,
getBinaryNodeChildBuffer,
getBinaryNodeChildren,
getBinaryNodeChildUInt,
jidDecode,
JidWithDevice,
type JidWithDevice,
S_WHATSAPP_NET
} from '../WABinary'
import { DeviceListData, ParsedDeviceInfo, USyncQueryResultList } from '../WAUSync'
import type { DeviceListData, ParsedDeviceInfo, USyncQueryResultList } from '../WAUSync'
import { Curve, generateSignalPubKey } from './crypto'
import { encodeBigEndian } from './generics'
function chunk<T>(array: T[], size: number): T[][] {
const chunks: T[][] = []
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size))
}
return chunks
}
export const createSignalIdentity = (wid: string, accountSignatureKey: Uint8Array): SignalIdentity => {
return {
identifier: { name: wid, deviceId: 0 },
@@ -100,11 +108,11 @@ export const parseAndInjectE2ESessions = async (node: BinaryNode, repository: Si
const chunks = chunk(nodes, chunkSize)
for (const nodesChunk of chunks) {
await Promise.all(
nodesChunk.map(async node => {
nodesChunk.map(async (node: BinaryNode) => {
const signedKey = getBinaryNodeChild(node, 'skey')!
const key = getBinaryNodeChild(node, 'key')!
const identity = getBinaryNodeChildBuffer(node, 'identity')!
const jid = node.attrs.jid
const jid = node.attrs.jid!
const registrationId = getBinaryNodeChildUInt(node, 'registration', 4)
await repository.injectE2ESession({
@@ -180,7 +188,7 @@ export const getNextPreKeysNode = async (state: AuthenticationState, count: numb
{ tag: 'registration', attrs: {}, content: encodeBigEndian(creds.registrationId) },
{ tag: 'type', attrs: {}, content: KEY_BUNDLE_TYPE },
{ tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public },
{ tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k], +k)) },
{ tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k]!, +k)) },
xmppSignedPreKey(creds.signedPreKey)
]
}
+3 -3
View File
@@ -2,7 +2,7 @@ import { Mutex } from 'async-mutex'
import { mkdir, readFile, stat, unlink, writeFile } from 'fs/promises'
import { join } from 'path'
import { proto } from '../../WAProto'
import { AuthenticationCreds, AuthenticationState, SignalDataTypeMap } from '../Types'
import type { AuthenticationCreds, AuthenticationState, SignalDataTypeMap } from '../Types'
import { initAuthCreds } from './auth-utils'
import { BufferJSON } from './generics'
@@ -118,8 +118,8 @@ export const useMultiFileAuthState = async (
set: async data => {
const tasks: Promise<void>[] = []
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id]
for (const id in data[category as keyof SignalDataTypeMap]) {
const value = data[category as keyof SignalDataTypeMap]![id]
const file = `${category}-${id}.json`
tasks.push(value ? writeData(value, file) : removeData(file))
}
+10 -7
View File
@@ -3,7 +3,7 @@ import { createHash } from 'crypto'
import { proto } from '../../WAProto'
import { KEY_BUNDLE_TYPE } from '../Defaults'
import type { AuthenticationCreds, SignalCreds, SocketConfig } from '../Types'
import { BinaryNode, getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary'
import { type BinaryNode, getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary'
import { Curve, hmacSign } from './crypto'
import { encodeBigEndian } from './generics'
import { createSignalIdentity } from './signal'
@@ -34,8 +34,8 @@ const PLATFORM_MAP = {
const getWebInfo = (config: SocketConfig): proto.ClientPayload.IWebInfo => {
let webSubPlatform = proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
if (config.syncFullHistory && PLATFORM_MAP[config.browser[0]]) {
webSubPlatform = PLATFORM_MAP[config.browser[0]]
if (config.syncFullHistory && PLATFORM_MAP[config.browser[0] as keyof typeof PLATFORM_MAP]) {
webSubPlatform = PLATFORM_MAP[config.browser[0] as keyof typeof PLATFORM_MAP]
}
return { webSubPlatform }
@@ -67,7 +67,10 @@ export const generateLoginNode = (userJid: string, config: SocketConfig): proto.
const getPlatformType = (platform: string): proto.DeviceProps.PlatformType => {
const platformType = platform.toUpperCase()
return proto.DeviceProps.PlatformType[platformType] || proto.DeviceProps.PlatformType.DESKTOP
return (
proto.DeviceProps.PlatformType[platformType as keyof typeof proto.DeviceProps.PlatformType] ||
proto.DeviceProps.PlatformType.DESKTOP
)
}
export const generateRegistrationNode = (
@@ -151,7 +154,7 @@ export const configureSuccessfulPairing = (
const deviceMsg = Buffer.concat([devicePrefix, deviceDetails!, signedIdentityKey.public, accountSignatureKey!])
account.deviceSignature = Curve.sign(signedIdentityKey.private, deviceMsg)
const identity = createSignalIdentity(jid, accountSignatureKey!)
const identity = createSignalIdentity(jid!, accountSignatureKey!)
const accountEnc = encodeSignedDeviceIdentity(account, false)
const deviceIdentity = proto.ADVDeviceIdentity.decode(account.details!)
@@ -161,7 +164,7 @@ export const configureSuccessfulPairing = (
attrs: {
to: S_WHATSAPP_NET,
type: 'result',
id: msgId
id: msgId!
},
content: [
{
@@ -180,7 +183,7 @@ export const configureSuccessfulPairing = (
const authUpdate: Partial<AuthenticationCreds> = {
account,
me: { id: jid, name: bizName },
me: { id: jid!, name: bizName },
signalIdentities: [...(signalIdentities || []), identity],
platform: platformNode?.attrs.name
}