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:
+37
-36
@@ -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'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user