chore: format everything

This commit is contained in:
canove
2025-05-06 12:10:19 -03:00
parent 04afa20244
commit fa706d0b50
76 changed files with 8241 additions and 7142 deletions
+117 -150
View File
@@ -28,7 +28,7 @@ import {
getPlatformId,
makeEventBuffer,
makeNoiseHandler,
promiseTimeout,
promiseTimeout
} from '../Utils'
import {
assertNodeErrorFree,
@@ -61,21 +61,22 @@ export const makeSocket = (config: SocketConfig) => {
defaultQueryTimeoutMs,
transactionOpts,
qrTimeout,
makeSignalRepository,
makeSignalRepository
} = config
if(printQRInTerminal) {
console.warn('⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.')
if (printQRInTerminal) {
console.warn(
'⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.'
)
}
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
if(config.mobile || url.protocol === 'tcp:') {
if (config.mobile || url.protocol === 'tcp:') {
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut })
}
if(url.protocol === 'wss' && authState?.creds?.routingInfo) {
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
}
@@ -110,28 +111,25 @@ export const makeSocket = (config: SocketConfig) => {
const sendPromise = promisify(ws.send)
/** send a raw buffer */
const sendRawMessage = async(data: Uint8Array | Buffer) => {
if(!ws.isOpen) {
const sendRawMessage = async (data: Uint8Array | Buffer) => {
if (!ws.isOpen) {
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
}
const bytes = noise.encodeFrame(data)
await promiseTimeout<void>(
connectTimeoutMs,
async(resolve, reject) => {
try {
await sendPromise.call(ws, bytes)
resolve()
} catch(error) {
reject(error)
}
await promiseTimeout<void>(connectTimeoutMs, async (resolve, reject) => {
try {
await sendPromise.call(ws, bytes)
resolve()
} catch (error) {
reject(error)
}
)
})
}
/** send a binary node */
const sendNode = (frame: BinaryNode) => {
if(logger.level === 'trace') {
if (logger.level === 'trace') {
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
}
@@ -141,15 +139,12 @@ export const makeSocket = (config: SocketConfig) => {
/** log & process any unexpected errors */
const onUnexpectedError = (err: Error | Boom, msg: string) => {
logger.error(
{ err },
`unexpected error in '${msg}'`
)
logger.error({ err }, `unexpected error in '${msg}'`)
}
/** await the next incoming message */
const awaitNextMessage = async<T>(sendMsg?: Uint8Array) => {
if(!ws.isOpen) {
const awaitNextMessage = async <T>(sendMsg?: Uint8Array) => {
if (!ws.isOpen) {
throw new Boom('Connection Closed', {
statusCode: DisconnectReason.connectionClosed
})
@@ -164,14 +159,13 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('frame', onOpen)
ws.on('close', onClose)
ws.on('error', onClose)
}).finally(() => {
ws.off('frame', onOpen)
ws.off('close', onClose)
ws.off('error', onClose)
})
.finally(() => {
ws.off('frame', onOpen)
ws.off('close', onClose)
ws.off('error', onClose)
})
if(sendMsg) {
if (sendMsg) {
sendRawMessage(sendMsg).catch(onClose!)
}
@@ -183,22 +177,20 @@ export const makeSocket = (config: SocketConfig) => {
* @param msgId the message tag to await
* @param timeoutMs timeout after which the promise will reject
*/
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
const waitForMessage = async <T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
let onRecv: (json) => void
let onErr: (err) => void
try {
const result = await promiseTimeout<T>(timeoutMs,
(resolve, reject) => {
onRecv = resolve
onErr = err => {
reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }))
}
const result = await promiseTimeout<T>(timeoutMs, (resolve, reject) => {
onRecv = resolve
onErr = err => {
reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }))
}
ws.on(`TAG:${msgId}`, onRecv)
ws.on('close', onErr) // if the socket closes, you'll never receive the message
ws.off('error', onErr)
},
)
ws.on(`TAG:${msgId}`, onRecv)
ws.on('close', onErr) // if the socket closes, you'll never receive the message
ws.off('error', onErr)
})
return result as any
} finally {
@@ -209,19 +201,16 @@ export const makeSocket = (config: SocketConfig) => {
}
/** send a query, and wait for its response. auto-generates message ID if not provided */
const query = async(node: BinaryNode, timeoutMs?: number) => {
if(!node.attrs.id) {
const query = async (node: BinaryNode, timeoutMs?: number) => {
if (!node.attrs.id) {
node.attrs.id = generateMessageTag()
}
const msgId = node.attrs.id
const [result] = await Promise.all([
waitForMessage(msgId, timeoutMs),
sendNode(node)
])
const [result] = await Promise.all([waitForMessage(msgId, timeoutMs), sendNode(node)])
if('tag' in result) {
if ('tag' in result) {
assertNodeErrorFree(result)
}
@@ -229,7 +218,7 @@ export const makeSocket = (config: SocketConfig) => {
}
/** connection handshake */
const validateConnection = async() => {
const validateConnection = async () => {
let helloMsg: proto.IHandshakeMessage = {
clientHello: { ephemeral: ephemeralKeyPair.public }
}
@@ -247,7 +236,7 @@ export const makeSocket = (config: SocketConfig) => {
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey)
let node: proto.IClientPayload
if(!creds.me) {
if (!creds.me) {
node = generateRegistrationNode(creds, config)
logger.info({ node }, 'not logged in, attempting registration...')
} else {
@@ -255,22 +244,20 @@ export const makeSocket = (config: SocketConfig) => {
logger.info({ node }, 'logging in...')
}
const payloadEnc = noise.encrypt(
proto.ClientPayload.encode(node).finish()
)
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish())
await sendRawMessage(
proto.HandshakeMessage.encode({
clientFinish: {
static: keyEnc,
payload: payloadEnc,
},
payload: payloadEnc
}
}).finish()
)
noise.finishInit()
startKeepAliveRequest()
}
const getAvailablePreKeysOnServer = async() => {
const getAvailablePreKeysOnServer = async () => {
const result = await query({
tag: 'iq',
attrs: {
@@ -279,33 +266,29 @@ export const makeSocket = (config: SocketConfig) => {
type: 'get',
to: S_WHATSAPP_NET
},
content: [
{ tag: 'count', attrs: {} }
]
content: [{ tag: 'count', attrs: {} }]
})
const countChild = getBinaryNodeChild(result, 'count')
return +countChild!.attrs.value
}
/** generates and uploads a set of pre-keys to the server */
const uploadPreKeys = async(count = INITIAL_PREKEY_COUNT) => {
await keys.transaction(
async() => {
logger.info({ count }, 'uploading pre-keys')
const { update, node } = await getNextPreKeysNode({ creds, keys }, count)
const uploadPreKeys = async (count = INITIAL_PREKEY_COUNT) => {
await keys.transaction(async () => {
logger.info({ count }, 'uploading pre-keys')
const { update, node } = await getNextPreKeysNode({ creds, keys }, count)
await query(node)
ev.emit('creds.update', update)
await query(node)
ev.emit('creds.update', update)
logger.info({ count }, 'uploaded pre-keys')
}
)
logger.info({ count }, 'uploaded pre-keys')
})
}
const uploadPreKeysToServerIfRequired = async() => {
const uploadPreKeysToServerIfRequired = async () => {
const preKeyCount = await getAvailablePreKeysOnServer()
logger.info(`${preKeyCount} pre-keys found on server`)
if(preKeyCount <= MIN_PREKEY_COUNT) {
if (preKeyCount <= MIN_PREKEY_COUNT) {
await uploadPreKeys()
}
}
@@ -319,10 +302,10 @@ export const makeSocket = (config: SocketConfig) => {
anyTriggered = ws.emit('frame', frame)
// if it's a binary node
if(!(frame instanceof Uint8Array)) {
if (!(frame instanceof Uint8Array)) {
const msgId = frame.attrs.id
if(logger.level === 'trace') {
if (logger.level === 'trace') {
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' })
}
@@ -333,7 +316,7 @@ export const makeSocket = (config: SocketConfig) => {
const l1 = frame.attrs || {}
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
for(const key of Object.keys(l1)) {
for (const key of Object.keys(l1)) {
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
@@ -342,7 +325,7 @@ export const makeSocket = (config: SocketConfig) => {
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
if(!anyTriggered && logger.level === 'debug') {
if (!anyTriggered && logger.level === 'debug') {
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
}
}
@@ -350,16 +333,13 @@ export const makeSocket = (config: SocketConfig) => {
}
const end = (error: Error | undefined) => {
if(closed) {
if (closed) {
logger.trace({ trace: error?.stack }, 'connection already closed')
return
}
closed = true
logger.info(
{ trace: error?.stack },
error ? 'connection errored' : 'connection closed'
)
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed')
clearInterval(keepAliveReq)
clearTimeout(qrTimer)
@@ -369,10 +349,10 @@ export const makeSocket = (config: SocketConfig) => {
ws.removeAllListeners('open')
ws.removeAllListeners('message')
if(!ws.isClosed && !ws.isClosing) {
if (!ws.isClosed && !ws.isClosing) {
try {
ws.close()
} catch{ }
} catch {}
}
ev.emit('connection.update', {
@@ -385,12 +365,12 @@ export const makeSocket = (config: SocketConfig) => {
ev.removeAllListeners('connection.update')
}
const waitForSocketOpen = async() => {
if(ws.isOpen) {
const waitForSocketOpen = async () => {
if (ws.isOpen) {
return
}
if(ws.isClosed || ws.isClosing) {
if (ws.isClosed || ws.isClosing) {
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
}
@@ -402,17 +382,16 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('open', onOpen)
ws.on('close', onClose)
ws.on('error', onClose)
}).finally(() => {
ws.off('open', onOpen)
ws.off('close', onClose)
ws.off('error', onClose)
})
.finally(() => {
ws.off('open', onOpen)
ws.off('close', onClose)
ws.off('error', onClose)
})
}
const startKeepAliveRequest = () => (
keepAliveReq = setInterval(() => {
if(!lastDateRecv) {
const startKeepAliveRequest = () =>
(keepAliveReq = setInterval(() => {
if (!lastDateRecv) {
lastDateRecv = new Date()
}
@@ -421,49 +400,42 @@ export const makeSocket = (config: SocketConfig) => {
check if it's been a suspicious amount of time since the server responded with our last seen
it could be that the network is down
*/
if(diff > keepAliveIntervalMs + 5000) {
if (diff > keepAliveIntervalMs + 5000) {
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
} else if(ws.isOpen) {
} else if (ws.isOpen) {
// if its all good, send a keep alive request
query(
{
tag: 'iq',
attrs: {
id: generateMessageTag(),
to: S_WHATSAPP_NET,
type: 'get',
xmlns: 'w:p',
},
content: [{ tag: 'ping', attrs: {} }]
}
)
.catch(err => {
logger.error({ trace: err.stack }, 'error in sending keep alive')
})
query({
tag: 'iq',
attrs: {
id: generateMessageTag(),
to: S_WHATSAPP_NET,
type: 'get',
xmlns: 'w:p'
},
content: [{ tag: 'ping', attrs: {} }]
}).catch(err => {
logger.error({ trace: err.stack }, 'error in sending keep alive')
})
} else {
logger.warn('keep alive called when WS not open')
}
}, keepAliveIntervalMs)
)
}, keepAliveIntervalMs))
/** i have no idea why this exists. pls enlighten me */
const sendPassiveIq = (tag: 'passive' | 'active') => (
const sendPassiveIq = (tag: 'passive' | 'active') =>
query({
tag: 'iq',
attrs: {
to: S_WHATSAPP_NET,
xmlns: 'passive',
type: 'set',
type: 'set'
},
content: [
{ tag, attrs: {} }
]
content: [{ tag, attrs: {} }]
})
)
/** logout & invalidate connection */
const logout = async(msg?: string) => {
const logout = async (msg?: string) => {
const jid = authState.creds.me?.id
if(jid) {
if (jid) {
await sendNode({
tag: 'iq',
attrs: {
@@ -487,7 +459,7 @@ export const makeSocket = (config: SocketConfig) => {
end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }))
}
const requestPairingCode = async(phoneNumber: string): Promise<string> => {
const requestPairingCode = async (phoneNumber: string): Promise<string> => {
authState.creds.pairingCode = bytesToCrockford(randomBytes(5))
authState.creds.me = {
id: jidEncode(phoneNumber, 's.whatsapp.net'),
@@ -572,10 +544,10 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('message', onMessageReceived)
ws.on('open', async() => {
ws.on('open', async () => {
try {
await validateConnection()
} catch(err) {
} catch (err) {
logger.error({ err }, 'error in validating connection')
end(err)
}
@@ -583,15 +555,17 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('error', mapWebSocketError(end))
ws.on('close', () => end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })))
// the server terminated the connection
ws.on('CB:xmlstreamend', () => end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })))
ws.on('CB:xmlstreamend', () =>
end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed }))
)
// QR gen
ws.on('CB:iq,type:set,pair-device', async(stanza: BinaryNode) => {
ws.on('CB:iq,type:set,pair-device', async (stanza: BinaryNode) => {
const iq: BinaryNode = {
tag: 'iq',
attrs: {
to: S_WHATSAPP_NET,
type: 'result',
id: stanza.attrs.id,
id: stanza.attrs.id
}
}
await sendNode(iq)
@@ -604,12 +578,12 @@ export const makeSocket = (config: SocketConfig) => {
let qrMs = qrTimeout || 60_000 // time to let a QR live
const genPairQR = () => {
if(!ws.isOpen) {
if (!ws.isOpen) {
return
}
const refNode = refNodes.shift()
if(!refNode) {
if (!refNode) {
end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }))
return
}
@@ -627,7 +601,7 @@ export const makeSocket = (config: SocketConfig) => {
})
// device paired for the first time
// if device pairs successfully, the server asks to restart the connection
ws.on('CB:iq,,pair-success', async(stanza: BinaryNode) => {
ws.on('CB:iq,,pair-success', async (stanza: BinaryNode) => {
logger.debug('pair success recv')
try {
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds)
@@ -641,13 +615,13 @@ export const makeSocket = (config: SocketConfig) => {
ev.emit('connection.update', { isNewLogin: true, qr: undefined })
await sendNode(reply)
} catch(error) {
} catch (error) {
logger.info({ trace: error.stack }, 'error in pairing')
end(error)
}
})
// login complete
ws.on('CB:success', async(node: BinaryNode) => {
ws.on('CB:success', async (node: BinaryNode) => {
await uploadPreKeysToServerIfRequired()
await sendPassiveIq('active')
@@ -677,7 +651,7 @@ export const makeSocket = (config: SocketConfig) => {
})
ws.on('CB:ib,,offline_preview', (node: BinaryNode) => {
logger.info('offline preview received', JSON.stringify(node))
logger.info('offline preview received', JSON.stringify(node))
sendNode({
tag: 'ib',
attrs: {},
@@ -688,7 +662,7 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('CB:ib,,edge_routing', (node: BinaryNode) => {
const edgeRoutingNode = getBinaryNodeChild(node, 'edge_routing')
const routingInfo = getBinaryNodeChild(edgeRoutingNode, 'routing_info')
if(routingInfo?.content) {
if (routingInfo?.content) {
authState.creds.routingInfo = Buffer.from(routingInfo?.content as Uint8Array)
ev.emit('creds.update', authState.creds)
}
@@ -696,7 +670,7 @@ export const makeSocket = (config: SocketConfig) => {
let didStartBuffer = false
process.nextTick(() => {
if(creds.me?.id) {
if (creds.me?.id) {
// start buffering important events
// if we're logged in
ev.buffer()
@@ -712,7 +686,7 @@ export const makeSocket = (config: SocketConfig) => {
const offlineNotifs = +(child?.attrs.count || 0)
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
if(didStartBuffer) {
if (didStartBuffer) {
ev.flush()
logger.trace('flushed events for initial buffer')
}
@@ -724,21 +698,19 @@ export const makeSocket = (config: SocketConfig) => {
ev.on('creds.update', update => {
const name = update.me?.name
// if name has just been received
if(creds.me?.name !== name) {
if (creds.me?.name !== name) {
logger.debug({ name }, 'updated pushName')
sendNode({
tag: 'presence',
attrs: { name: name! }
}).catch(err => {
logger.warn({ trace: err.stack }, 'error in sending presence update on name change')
})
.catch(err => {
logger.warn({ trace: err.stack }, 'error in sending presence update on name change')
})
}
Object.assign(creds, update)
})
return {
type: 'md' as 'md',
ws,
@@ -762,7 +734,7 @@ export const makeSocket = (config: SocketConfig) => {
requestPairingCode,
/** Waits for the connection to WA to reach a state */
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
sendWAMBuffer,
sendWAMBuffer
}
}
@@ -772,11 +744,6 @@ export const makeSocket = (config: SocketConfig) => {
* */
function mapWebSocketError(handler: (err: Error) => void) {
return (error: Error) => {
handler(
new Boom(
`WebSocket Error (${error?.message})`,
{ statusCode: getCodeFromWSError(error), data: error }
)
)
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }))
}
}