@vreden/meta
Version:
Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.
1,019 lines (879 loc) • 30.9 kB
JavaScript
"use strict"
Object.defineProperty(exports, "__esModule", {
value: true
})
const boom_1 = require("@hapi/boom")
const crypto_1 = require("crypto")
const url_1 = require("url")
const util_1 = require("util")
const WAProto_1 = require("../../WAProto")
const Defaults_1 = require("../Defaults")
const Types_1 = require("../Types")
const Utils_1 = require("../Utils")
const WABinary_1 = require("../WABinary")
const Client_1 = require("./Client")
const WAUSync_1 = require("../WAUSync")
const makeSocket = (config) => {
const {
waWebSocketUrl,
connectTimeoutMs,
logger,
keepAliveIntervalMs,
browser,
auth: authState,
printQRInTerminal,
defaultQueryTimeoutMs,
transactionOpts,
qrTimeout,
makeSignalRepository
} = config
const uqTagId = Utils_1.generateMdTagPrefix()
const generateMessageTag = () => `${uqTagId}${epoch++}`
const url = typeof waWebSocketUrl === 'string' ? new url_1.URL(waWebSocketUrl) : waWebSocketUrl
if (config.mobile || url.protocol === 'tcp:') {
throw new boom_1.Boom('Mobile API is not supported anymore', {
statusCode: Types_1.DisconnectReason.loggedOut
})
}
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
}
const waitForMessage = async (msgId, timeoutMs = defaultQueryTimeoutMs) => {
let onRecv
let onErr
try {
return await Utils_1.promiseTimeout(timeoutMs, (resolve, reject) => {
onRecv = resolve
onErr = err => {
reject(err || new boom_1.Boom('Connection Closed', {
statusCode: Types_1.DisconnectReason.connectionClosed
}))
}
ws.on(`TAG:${msgId}`, onRecv)
ws.on('close', onErr)
ws.off('error', onErr)
})
} finally {
ws.off(`TAG:${msgId}`, onRecv)
ws.off('close', onErr)
ws.off('error', onErr)
}
}
const query = async (node, timeoutMs) => {
if (!node.attrs.id) {
node.attrs.id = generateMessageTag()
}
const msgId = node.attrs.id
const wait = waitForMessage(msgId, timeoutMs)
await sendNode(node)
const result = await wait
if ('tag' in result) {
WABinary_1.assertNodeErrorFree(result)
}
return result
}
const executeUSyncQuery = async (usyncQuery) => {
if (usyncQuery.protocols.length === 0) {
throw new boom_1.Boom('USyncQuery must have at least one protocol');
}
const validUsers = usyncQuery.users
const userNodes = validUsers.map(user => {
return {
tag: 'user',
attrs: {
jid: !user.phone ? user.id : undefined
},
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
}
})
const listNode = {
tag: 'list',
attrs: {},
content: userNodes
}
const queryNode = {
tag: 'query',
attrs: {},
content: usyncQuery.protocols.map(a => a.getQueryElement())
}
const iq = {
tag: 'iq',
attrs: {
to: WABinary_1._WHATSAPP_NET,
type: 'get',
xmlns: 'usync'
},
content: [{
tag: 'usync',
attrs: {
context: usyncQuery.context,
mode: usyncQuery.mode,
sid: generateMessageTag(),
last: 'true',
index: '0'
},
content: [queryNode, listNode]
}]
}
const result = await query(iq)
return usyncQuery.parseUSyncQueryResult(result)
}
const onWhatsApp = async (...jids) => {
const usyncQuery = new WAUSync_1.USyncQuery().withLIDProtocol().withContactProtocol()
for (const jid of jids) {
if (WABinary_1.isLidUser(jid)) {
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid)) // intentional
} else {
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone))
}
}
const results = await executeUSyncQuery(usyncQuery)
if (results) {
if (results.list.filter(a => !!a.lid).length > 0) {
const lidOnly = results.list.filter(a => !!a.lid)
await signalRepository.lidMapping.storeLIDPNMappings(lidOnly.map(a => ({
pn: a.id,
lid: a.lid
})))
}
return results.list
.filter(a => !!a.contact)
.map(({
contact,
id,
lid
}) => ({
jid: id,
exists: contact,
lid: lid
}))
}
}
const ws = new Client_1.WebSocketClient(url, config)
ws.connect()
const ev = Utils_1.makeEventBuffer(logger)
const ephemeralKeyPair = Utils_1.Curve.generateKeyPair()
const noise = Utils_1.makeNoiseHandler({
keyPair: ephemeralKeyPair,
NOISE_HEADER: Defaults_1.NOISE_WA_HEADER,
logger,
routingInfo: authState?.creds?.routingInfo
})
const { creds } = authState
const keys = Utils_1.addTransactionCapability(authState.keys, logger, transactionOpts)
const signalRepository = makeSignalRepository({
creds,
keys
}, onWhatsApp, logger)
let lastDateRecv
let epoch = 1
let keepAliveReq
let qrTimer
let closed = false
const sendPromise = util_1.promisify(ws.send)
const sendRawMessage = async (data) => {
if (!ws.isOpen) {
throw new boom_1.Boom('Connection Closed', {
statusCode: Types_1.DisconnectReason.connectionClosed
})
}
const bytes = noise.encodeFrame(data)
await Utils_1.promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
try {
await sendPromise.call(ws, bytes)
resolve()
} catch (error) {
reject(error)
}
})
}
const sendNode = (frame) => {
if (logger.level === 'trace') {
logger.trace({
xml: WABinary_1.binaryNodeToString(frame),
msg: 'xml send'
})
}
const buff = WABinary_1.encodeBinaryNode(frame)
return sendRawMessage(buff)
}
const onUnexpectedError = (err, msg) => {
logger.error({
err
}, `unexpected error in '${msg}'`)
}
const awaitNextMessage = async (sendMsg) => {
if (!ws.isOpen) {
throw new boom_1.Boom('Connection Closed', {
statusCode: Types_1.DisconnectReason.connectionClosed
})
}
let onOpen
let onClose
const result = Utils_1.promiseTimeout(connectTimeoutMs, (resolve, reject) => {
onOpen = resolve
onClose = mapWebSocketError(reject)
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)
})
if (sendMsg) {
sendRawMessage(sendMsg).catch(onClose)
}
return result
}
const validateConnection = async () => {
let helloMsg = {
clientHello: {
ephemeral: ephemeralKeyPair.public
}
}
helloMsg = WAProto_1.proto.HandshakeMessage.fromObject(helloMsg)
logger.info({
browser,
helloMsg
}, 'connected to WA')
const init = WAProto_1.proto.HandshakeMessage.encode(helloMsg).finish()
const result = await awaitNextMessage(init)
const handshake = WAProto_1.proto.HandshakeMessage.decode(result)
logger.trace({
handshake
}, 'handshake recv from WA')
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey)
let node
if (!creds.me) {
node = Utils_1.generateRegistrationNode(creds, config)
logger.info({
node
}, 'not logged in, attempting registration...')
} else {
node = Utils_1.generateLoginNode(creds.me.id, config)
logger.info({
node
}, 'logging in...')
}
const payloadEnc = noise.encrypt(WAProto_1.proto.ClientPayload.encode(node).finish())
await sendRawMessage(WAProto_1.proto.HandshakeMessage.encode({
clientFinish: {
static: keyEnc,
payload: payloadEnc,
},
}).finish())
noise.finishInit()
startKeepAliveRequest()
}
const getAvailablePreKeysOnServer = async () => {
const result = await query({
tag: 'iq',
attrs: {
id: generateMessageTag(),
xmlns: 'encrypt',
type: 'get',
to: WABinary_1.S_WHATSAPP_NET
},
content: [{
tag: 'count',
attrs: {}
}]
})
const countChild = WABinary_1.getBinaryNodeChild(result, 'count')
return +countChild.attrs.value
}
let uploadPreKeysPromise = null
let lastUploadTime = 0
const uploadPreKeys = async (count = Defaults_1.INITIAL_PREKEY_COUNT, retryCount = 0) => {
if (retryCount === 0) {
const timeSinceLastUpload = Date.now() - lastUploadTime
if (timeSinceLastUpload < Defaults_1.MIN_UPLOAD_INTERVAL) {
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`)
return
}
}
if (uploadPreKeysPromise) {
logger.debug('Pre-key upload already in progress, waiting for completion')
return uploadPreKeysPromise
}
const uploadLogic = async () => {
logger.info({
count,
retryCount
}, 'uploading pre-keys')
const node = await keys.transaction(async () => {
logger.debug({
requestedCount: count
}, 'generating pre-keys with requested count')
const {
update,
node
} = await Utils_1.getNextPreKeysNode({
creds,
keys
}, count)
ev.emit('creds.update', update)
return node
}, creds?.me?.id || 'upload-pre-keys')
try {
await query(node)
logger.info({
count
}, 'uploaded pre-keys successfully')
lastUploadTime = Date.now()
} catch (uploadError) {
logger.error({
uploadError,
count
}, 'Failed to upload pre-keys to server')
if (retryCount < 3) {
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000)
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`)
await new Promise(resolve => setTimeout(resolve, backoffDelay))
return uploadPreKeys(count, retryCount + 1)
}
throw uploadError
}
}
uploadPreKeysPromise = Promise.race([
uploadLogic(),
new Promise((_, reject) => setTimeout(() => reject(new boom_1.Boom('Pre-key upload timeout', {
statusCode: 408
})), Defaults_1.UPLOAD_TIMEOUT))
])
try {
await uploadPreKeysPromise
} finally {
uploadPreKeysPromise = null
}
}
const verifyCurrentPreKeyExists = async () => {
const currentPreKeyId = creds.nextPreKeyId - 1
if (currentPreKeyId <= 0) {
return {
exists: false,
currentPreKeyId: 0
}
}
const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()])
const exists = !!preKeys[currentPreKeyId.toString()]
return {
exists,
currentPreKeyId
}
}
const uploadPreKeysToServerIfRequired = async () => {
try {
const preKeyCount = await getAvailablePreKeysOnServer()
const {
exists: currentPreKeyExists,
currentPreKeyId
} = await verifyCurrentPreKeyExists()
logger.info(`${preKeyCount} pre-keys found on server`)
logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`)
const lowServerCount = preKeyCount <= Defaults_1.MIN_PREKEY_COUNT
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0
const shouldUpload = lowServerCount || missingCurrentPreKey
if (shouldUpload) {
const reasons = []
if (lowServerCount)
reasons.push(`server count low (${preKeyCount})`)
if (missingCurrentPreKey)
reasons.push(`current prekey ${currentPreKeyId} missing from storage`)
logger.info(`Uploading PreKeys due to: ${reasons.join(', ')}`)
await uploadPreKeys()
} else {
logger.info(`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`)
}
} catch (error) {
logger.error({
error
}, 'Failed to check/upload pre-keys during initialization')
}
}
const onMessageReceived = (data) => {
noise.decodeFrame(data, frame => {
lastDateRecv = new Date()
let anyTriggered = false
anyTriggered = ws.emit('frame', frame)
if (!(frame instanceof Uint8Array)) {
const msgId = frame.attrs.id
if (logger.level === 'trace') {
logger.trace({
xml: WABinary_1.binaryNodeToString(frame),
msg: 'recv xml'
})
}
anyTriggered = ws.emit(`${Defaults_1.DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
const l0 = frame.tag
const l1 = frame.attrs || {}
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
for (const key of Object.keys(l1)) {
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
}
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
if (!anyTriggered && logger.level === 'debug') {
logger.debug({
unhandled: true,
msgId,
fromMe: false,
frame
}, 'communication recv')
}
}
})
}
const end = (error) => {
if (closed) {
logger.trace({
trace: error?.stack
}, 'connection already closed')
return
}
closed = true
logger.info({
trace: error?.stack
}, error ? 'connection errored' : 'connection closed')
clearInterval(keepAliveReq)
clearTimeout(qrTimer)
ws.removeAllListeners('close')
ws.removeAllListeners('open')
ws.removeAllListeners('message')
if (!ws.isClosed && !ws.isClosing) {
try {
ws.close()
} catch (_a) {}
}
ev.emit('connection.update', {
connection: 'close',
lastDisconnect: {
error,
date: new Date()
}
})
ev.removeAllListeners('connection.update')
}
const waitForSocketOpen = async () => {
if (ws.isOpen) {
return
}
if (ws.isClosed || ws.isClosing) {
throw new boom_1.Boom('Connection Closed', {
statusCode: Types_1.DisconnectReason.connectionClosed
})
}
let onOpen
let onClose
await new Promise((resolve, reject) => {
onOpen = () => resolve(undefined)
onClose = mapWebSocketError(reject)
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)
})
}
const startKeepAliveRequest = () => (keepAliveReq = setInterval(() => {
if (!lastDateRecv) {
lastDateRecv = new Date()
}
const diff = Date.now() - lastDateRecv.getTime()
if (diff > keepAliveIntervalMs + 5000) {
end(new boom_1.Boom('Connection was lost', {
statusCode: Types_1.DisconnectReason.connectionLost
}))
} else if (ws.isOpen) {
query({
tag: 'iq',
attrs: {
id: generateMessageTag(),
to: WABinary_1.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))
const sendPassiveIq = (tag) => (query({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
xmlns: 'passive',
type: 'set',
},
content: [{
tag,
attrs: {}
}]
}))
const logout = async (msg) => {
const jid = authState.creds.me?.id
if (jid) {
await sendNode({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
id: generateMessageTag(),
xmlns: 'md'
},
content: [{
tag: 'remove-companion-device',
attrs: {
jid,
reason: 'user_initiated'
}
}]
})
}
end(new boom_1.Boom(msg || 'Intentional Logout', {
statusCode: Types_1.DisconnectReason.loggedOut
}))
}
const requestPairingCode = async (phoneNumber, code = "ABCD1234") => {
authState.creds.pairingCode = code?.toUpperCase() || "ABCD1234"
authState.creds.me = {
id: WABinary_1.jidEncode(phoneNumber, 's.whatsapp.net'),
name: '~'
}
ev.emit('creds.update', authState.creds)
await sendNode({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
id: generateMessageTag(),
xmlns: 'md'
},
content: [{
tag: 'link_code_companion_reg',
attrs: {
jid: authState.creds.me.id,
stage: 'companion_hello',
should_show_push_notification: 'true'
},
content: [{
tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
attrs: {},
content: await generatePairingKey()
},
{
tag: 'companion_server_auth_key_pub',
attrs: {},
content: authState.creds.noiseKey.public
},
{
tag: 'companion_platform_id',
attrs: {},
content: Utils_1.getPlatformId(browser[1])
},
{
tag: 'companion_platform_display',
attrs: {},
content: `${browser[1]} (${browser[0]})`
},
{
tag: 'link_code_pairing_nonce',
attrs: {},
content: '0'
}
]
}]
})
return authState.creds.pairingCode
}
async function generatePairingKey() {
const salt = crypto_1.randomBytes(32)
const randomIv = crypto_1.randomBytes(16)
const key = await Utils_1.derivePairingCodeKey(authState.creds.pairingCode, salt)
const ciphered = Utils_1.aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv)
return Buffer.concat([salt, randomIv, ciphered])
}
const sendWAMBuffer = (wamBuffer) => {
return query({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
id: generateMessageTag(),
xmlns: 'w:stats'
},
content: [{
tag: 'add',
attrs: {},
content: wamBuffer
}]
})
}
ws.on('message', onMessageReceived)
ws.on('open', async () => {
try {
await validateConnection()
} catch (err) {
logger.error({
err
}, 'error in validating connection')
end(err)
}
})
ws.on('error', mapWebSocketError(end))
ws.on('close', () => end(new boom_1.Boom('Connection Terminated', {
statusCode: Types_1.DisconnectReason.connectionClosed
})))
ws.on('CB:xmlstreamend', () => end(new boom_1.Boom('Connection Terminated by Server', {
statusCode: Types_1.DisconnectReason.connectionClosed
})))
ws.on('CB:iq,type:set,pair-device', async (stanza) => {
const iq = {
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'result',
id: stanza.attrs.id,
}
}
await sendNode(iq)
const pairDeviceNode = WABinary_1.getBinaryNodeChild(stanza, 'pair-device')
const refNodes = WABinary_1.getBinaryNodeChildren(pairDeviceNode, 'ref')
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64')
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64')
const advB64 = creds.advSecretKey
let qrMs = qrTimeout || 60000 // time to let a QR live
const genPairQR = () => {
if (!ws.isOpen) {
return
}
const refNode = refNodes.shift()
if (!refNode) {
end(new boom_1.Boom('QR refs attempts ended', {
statusCode: Types_1.DisconnectReason.timedOut
}))
return
}
const ref = refNode.content.toString('utf-8')
const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',')
ev.emit('connection.update', {
qr
})
qrTimer = setTimeout(genPairQR, qrMs)
qrMs = qrTimeout || 20000
}
genPairQR()
})
ws.on('CB:iq,,pair-success', async (stanza) => {
logger.debug('pair success recv')
try {
const {
reply,
creds: updatedCreds
} = Utils_1.configureSuccessfulPairing(stanza, creds)
logger.info({
me: updatedCreds.me,
platform: updatedCreds.platform
}, 'pairing configured successfully, expect to restart the connection...')
ev.emit('creds.update', updatedCreds)
ev.emit('connection.update', {
isNewLogin: true,
qr: undefined
})
await sendNode(reply)
} catch (error) {
logger.info({
trace: error.stack
}, 'error in pairing')
end(error)
}
})
ws.on('CB:success', async (node) => {
try {
await uploadPreKeysToServerIfRequired()
await sendPassiveIq('active')
} catch (err) {
logger.warn({
err
}, 'failed to send initial passive iq');
}
logger.info('opened connection to WA')
clearTimeout(qrTimer);
ev.emit('creds.update', {
me: {
...authState.creds.me,
lid: node.attrs.lid
}
})
ev.emit('connection.update', {
connection: 'open'
})
if (node.attrs.lid && authState.creds.me?.id) {
const myLID = node.attrs.lid
process.nextTick(async () => {
try {
const myPN = authState.creds.me.id
await signalRepository.lidMapping.storeLIDPNMappings([{
lid: myLID,
pn: myPN
}])
logger.info({
myPN,
myLID
}, 'Own LID session created successfully')
} catch (error) {
logger.error({
error,
lid: myLID
}, 'Failed to create own LID session')
}
})
}
})
ws.on('CB:stream:error', (node) => {
logger.error({
node
}, 'stream errored out')
const {
reason,
statusCode
} = Utils_1.getErrorCodeFromStreamError(node)
end(new boom_1.Boom(`Stream Errored (${reason})`, {
statusCode,
data: node
}))
})
ws.on('CB:failure', (node) => {
const reason = +(node.attrs.reason || 500)
end(new boom_1.Boom('Connection Failure', {
statusCode: reason,
data: node.attrs
}))
})
ws.on('CB:ib,,downgrade_webclient', () => {
end(new boom_1.Boom('Multi-device beta not joined', {
statusCode: Types_1.DisconnectReason.multideviceMismatch
}))
})
ws.on('CB:ib,,offline_preview', (node) => {
logger.info('offline preview received', JSON.stringify(node))
sendNode({
tag: 'ib',
attrs: {},
content: [{
tag: 'offline_batch',
attrs: {
count: '100'
}
}]
})
})
ws.on('CB:ib,,edge_routing', (node) => {
const edgeRoutingNode = WABinary_1.getBinaryNodeChild(node, 'edge_routing')
const routingInfo = WABinary_1.getBinaryNodeChild(edgeRoutingNode, 'routing_info')
if (routingInfo?.content) {
authState.creds.routingInfo = Buffer.from(routingInfo?.content)
ev.emit('creds.update', authState.creds)
}
})
let didStartBuffer = false
process.nextTick(() => {
if (creds.me?.id) {
ev.buffer()
didStartBuffer = true
}
ev.emit('connection.update', {
connection: 'connecting',
receivedPendingNotifications: false,
qr: undefined
})
})
ws.on('CB:ib,,offline', (node) => {
const child = WABinary_1.getBinaryNodeChild(node, 'offline')
const offlineNotifs = +(child?.attrs.count || 0)
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
if (didStartBuffer) {
ev.flush()
logger.trace('flushed events for initial buffer')
}
ev.emit('connection.update', {
receivedPendingNotifications: true
})
})
ev.on('creds.update', update => {
const name = update.me?.name
if (creds.me?.name !== name) {
logger.debug({
name
}, 'updated pushName')
sendNode({
tag: 'presence',
attrs: {
name
}
}).catch(err => {
logger.warn({
trace: err.stack
}, 'error in sending presence update on name change')
})
}
Object.assign(creds, update)
})
if (printQRInTerminal) {
Utils_1.printQRIfNecessaryListener(ev, logger)
}
return {
type: 'meta',
ws,
ev,
authState: {
creds,
keys
},
signalRepository,
get user() {
return authState.creds.me
},
generateMessageTag,
query,
waitForMessage,
waitForSocketOpen,
sendRawMessage,
sendNode,
logout,
end,
onUnexpectedError,
uploadPreKeys,
uploadPreKeysToServerIfRequired,
requestPairingCode,
waitForConnectionUpdate: Utils_1.bindWaitForConnectionUpdate(ev),
sendWAMBuffer,
executeUSyncQuery,
onWhatsApp,
logger
}
}
function mapWebSocketError(handler) {
return (error) => {
handler(new boom_1.Boom(`WebSocket Error (${error?.message})`, {
statusCode: Utils_1.getCodeFromWSError(error),
data: error
}))
}
}
module.exports = {
makeSocket
}