fix: address PR review — stale comments, createCallLink compat, relay guard

- Fix call link URL: remove media prefix (Frida shows https://call.whatsapp.com/<token>)
- Make createCallLink() backward compatible with chats.ts signature (event, timeoutMs)
- Guard CB:relay handler — require both callId AND callCreator before emitting
- Extract extractParticipants() helper to eliminate 3x code duplication

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Renato Alcara
2026-03-01 12:17:17 -03:00
parent 3535a16b32
commit cdab2e3c63
+37 -36
View File
@@ -20,6 +20,7 @@ import type {
PlaceholderMessageData, PlaceholderMessageData,
SocketConfig, SocketConfig,
WACallEvent, WACallEvent,
WACallParticipant,
WACallUpdateType, WACallUpdateType,
WAMessage, WAMessage,
WAMessageKey, WAMessageKey,
@@ -1073,7 +1074,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
* @param media - 'video' or 'audio' * @param media - 'video' or 'audio'
* @returns object with token, full URL, and raw server response * @returns object with token, full URL, and raw server response
*/ */
const createCallLink = async (media: 'video' | 'audio' = 'video') => { const createCallLink = async (
media: 'video' | 'audio' = 'video',
event?: { startTime: number },
timeoutMs?: number
) => {
const stanza: BinaryNode = { const stanza: BinaryNode = {
tag: 'call', tag: 'call',
attrs: { attrs: {
@@ -1083,14 +1088,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
content: [{ content: [{
tag: 'link_create', tag: 'link_create',
attrs: { media }, attrs: { media },
content: undefined content: event
? [{ tag: 'event', attrs: { start_time: String(event.startTime) }, content: undefined }]
: undefined
}] }]
} }
const response = await query(stanza) const response = await query(stanza, timeoutMs)
// Extract token from server response // Extract token from server response
// Response contains link_create child with token attribute
let token: string | undefined let token: string | undefined
const linkCreateResp = getBinaryNodeChild(response, 'link_create') const linkCreateResp = getBinaryNodeChild(response, 'link_create')
if (linkCreateResp) { if (linkCreateResp) {
@@ -1112,8 +1118,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
} }
// URL format verified via Frida capture: https://call.whatsapp.com/<token>
const url = token const url = token
? `https://call.whatsapp.com/${media}/${token}` ? `https://call.whatsapp.com/${token}`
: undefined : undefined
return { token, url, response } return { token, url, response }
@@ -2470,6 +2477,18 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
return pn return pn
} }
/** Extract participants from a node containing <user> children */
const extractParticipants = (parentNode: BinaryNode): WACallParticipant[] | undefined => {
const userNodes = getBinaryNodeChildren(parentNode, 'user')
if (!userNodes.length) return undefined
return userNodes.map(u => ({
jid: u.attrs.jid,
state: u.attrs.state,
userPn: u.attrs.user_pn,
type: u.attrs.type,
}))
}
const handleCall = async (node: BinaryNode) => { const handleCall = async (node: BinaryNode) => {
const { attrs } = node const { attrs } = node
const children = getAllBinaryNodeChildren(node) const children = getAllBinaryNodeChildren(node)
@@ -2511,16 +2530,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
call.connectedLimit = groupInfo.attrs['connected-limit'] call.connectedLimit = groupInfo.attrs['connected-limit']
? Number(groupInfo.attrs['connected-limit']) ? Number(groupInfo.attrs['connected-limit'])
: undefined : undefined
// Extract participants from group_info call.participants = extractParticipants(groupInfo)
const userNodes = getBinaryNodeChildren(groupInfo, 'user')
if (userNodes.length) {
call.participants = userNodes.map(u => ({
jid: u.attrs.jid,
state: u.attrs.state,
userPn: u.attrs.user_pn,
type: u.attrs.type,
}))
}
} }
// Extract link_info (who created the link) // Extract link_info (who created the link)
@@ -2543,15 +2553,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
call.connectedLimit = groupInfo.attrs['connected-limit'] call.connectedLimit = groupInfo.attrs['connected-limit']
? Number(groupInfo.attrs['connected-limit']) ? Number(groupInfo.attrs['connected-limit'])
: undefined : undefined
const userNodes = getBinaryNodeChildren(groupInfo, 'user') call.participants = extractParticipants(groupInfo)
if (userNodes.length) {
call.participants = userNodes.map(u => ({
jid: u.attrs.jid,
state: u.attrs.state,
userPn: u.attrs.user_pn,
type: u.attrs.type,
}))
}
} }
} }
@@ -2574,15 +2576,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
call.duration = callSummary.attrs.call_duration call.duration = callSummary.attrs.call_duration
? Number(callSummary.attrs.call_duration) ? Number(callSummary.attrs.call_duration)
: undefined : undefined
const userNodes = getBinaryNodeChildren(callSummary, 'user') call.participants = extractParticipants(callSummary)
if (userNodes.length) {
call.participants = userNodes.map(u => ({
jid: u.attrs.jid,
state: u.attrs.state,
userPn: u.attrs.user_pn,
type: u.attrs.type,
}))
}
} }
} }
@@ -2809,19 +2803,26 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
ws.on('CB:relay', async (node: BinaryNode) => { ws.on('CB:relay', async (node: BinaryNode) => {
const callId = node.attrs['call-id'] const callId = node.attrs['call-id']
const callCreator = node.attrs['call-creator'] const callCreator = node.attrs['call-creator']
if (callId) { // Both callId and callCreator must be present to emit a valid event
// (call link relays may arrive without these attrs — just log them)
if (callId && callCreator) {
logger.debug( logger.debug(
{ callId, callCreator, uuid: node.attrs.uuid }, { callId, callCreator, uuid: node.attrs.uuid },
'received relay info' 'received relay info'
) )
ev.emit('call', [{ ev.emit('call', [{
chatId: callCreator || '', chatId: callCreator,
from: callCreator || '', from: callCreator,
id: callId, id: callId,
date: new Date(), date: new Date(),
offline: false, offline: false,
status: 'relay' as WACallUpdateType, status: 'relay' as WACallUpdateType,
}]) }])
} else {
logger.debug(
{ attrs: node.attrs },
'received relay stanza without call-id/call-creator'
)
} }
}) })