From 37c41931cf5043fc0217095abfa1ef43c28040f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Mar 2026 00:55:00 +0000 Subject: [PATCH] fix: resolve all lint errors in PR #297 (prettier formatting + max-depth) - Apply prettier auto-fix across all modified files (indentation, trailing commas, import order) - Fix simple-import-sort violations in messages-send.ts, messages.ts, process-message.ts, sticker-pack.ts - Add eslint-disable-next-line max-depth for 3 deeply nested if blocks in messages.ts - Remove unused eslint-disable directives flagged as warnings in messages-send.ts https://claude.ai/code/session_011Z7EFNQqHqn3Eu6xtQ9YZK --- src/Signal/libsignal.ts | 2 +- src/Socket/groups.ts | 40 +-- src/Socket/messages-recv.ts | 470 +++++++++++++++++---------------- src/Socket/messages-send.ts | 6 +- src/Socket/socket.ts | 21 +- src/Utils/circuit-breaker.ts | 2 +- src/Utils/decode-wa-message.ts | 4 +- src/Utils/generics.ts | 1 + src/Utils/messages.ts | 5 +- src/Utils/process-message.ts | 5 +- src/Utils/sticker-pack.ts | 9 +- 11 files changed, 289 insertions(+), 276 deletions(-) diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index 8ef48a8f..6ee7931e 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -306,7 +306,7 @@ export function makeLibSignalRepository( // This prevents PN/LID race conditions where concurrent operations for the // same logical contact acquire different mutex locks because one uses PN // and the other uses LID. (Aligned with WABA behavior — all operations use LID internally.) - const resolveCanonicalJid = async(jid: string): Promise => { + const resolveCanonicalJid = async (jid: string): Promise => { if (isAnyLidUser(jid)) { return jid } diff --git a/src/Socket/groups.ts b/src/Socket/groups.ts index 0c4716da..50032b44 100644 --- a/src/Socket/groups.ts +++ b/src/Socket/groups.ts @@ -25,28 +25,30 @@ export const makeGroupsSocket = (config: SocketConfig) => { const lidMapping = signalRepository.lidMapping // Resolve all participant LIDs in parallel for better performance on large groups - await Promise.all(metadata.participants.map(async (p) => { - if (isLidUser(p.id)) { - if (p.phoneNumber) { - p.lid = p.id - p.id = p.phoneNumber - } else { - const resolved = await resolveLidToPn(p.id, lidMapping, logger) - if (resolved && resolved !== p.id) { + await Promise.all( + metadata.participants.map(async p => { + if (isLidUser(p.id)) { + if (p.phoneNumber) { p.lid = p.id - p.id = resolved + p.id = p.phoneNumber + } else { + const resolved = await resolveLidToPn(p.id, lidMapping, logger) + if (resolved && resolved !== p.id) { + p.lid = p.id + p.id = resolved + } } } - } - })) + }) + ) // Normalize owner/subjectOwner if LID (parallel) const [resolvedOwner, resolvedSubjectOwner] = await Promise.all([ metadata.owner && isLidUser(metadata.owner) - ? (metadata.ownerPn || resolveLidToPn(metadata.owner, lidMapping, logger)) + ? metadata.ownerPn || resolveLidToPn(metadata.owner, lidMapping, logger) : null, metadata.subjectOwner && isLidUser(metadata.subjectOwner) - ? (metadata.subjectOwnerPn || resolveLidToPn(metadata.subjectOwner, lidMapping, logger)) + ? metadata.subjectOwnerPn || resolveLidToPn(metadata.subjectOwner, lidMapping, logger) : null ]) if (resolvedOwner) metadata.owner = resolvedOwner @@ -95,11 +97,13 @@ export const makeGroupsSocket = (config: SocketConfig) => { if (groupsChild) { const groups = getBinaryNodeChildren(groupsChild, 'group') for (const groupNode of groups) { - const meta = await normalizeGroupMetadata(extractGroupMetadata({ - tag: 'result', - attrs: {}, - content: [groupNode] - })) + const meta = await normalizeGroupMetadata( + extractGroupMetadata({ + tag: 'result', + attrs: {}, + content: [groupNode] + }) + ) data[meta.id] = meta } } diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index 1a08ea88..92c8a554 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -376,7 +376,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { for (const update of updates) { if (update.jid && update.user) { const [resolvedAuthor, resolvedUser] = await Promise.all([ - resolveLidToPn(node.attrs.from!, signalRepository.lidMapping, logger), + resolveLidToPn(node.attrs.from, signalRepository.lidMapping, logger), resolveLidToPn(update.user, signalRepository.lidMapping, logger) ]) ev.emit('newsletter-participants.update', { @@ -403,7 +403,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const child = getAllBinaryNodeChildren(node)[0]! const rawAuthor = node.attrs.participant! // Resolve author LID→PN (participant is a user JID that could be LID) - const author = await resolveLidToPn(rawAuthor, signalRepository.lidMapping, logger) || rawAuthor + const author = (await resolveLidToPn(rawAuthor, signalRepository.lidMapping, logger)) || rawAuthor logger.info({ from, child }, 'got newsletter notification') @@ -430,7 +430,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { break case 'participant': - const resolvedParticipantUser = await resolveLidToPn(child.attrs.jid!, signalRepository.lidMapping, logger) || child.attrs.jid! + const resolvedParticipantUser = + (await resolveLidToPn(child.attrs.jid, signalRepository.lidMapping, logger)) || child.attrs.jid! const participantUpdate = { id: from, author, @@ -573,7 +574,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const offerContent: BinaryNode[] = [ { tag: 'privacy', attrs: {}, content: undefined }, { tag: 'audio', attrs: { rate: '8000', enc: 'opus' }, content: undefined }, - { tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }, + { tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined } ] if (isVideo) { @@ -594,31 +595,31 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { { tag: 'net', attrs: { medium: '3' }, content: undefined }, { tag: 'capability', attrs: { ver: '1' }, content: undefined }, { tag: 'enc', attrs: { v: '2', type: isVideo ? 'msg' : 'pkmsg' }, content: undefined }, - { tag: 'encopt', attrs: { keygen: '2' }, content: undefined }, + { tag: 'encopt', attrs: { keygen: '2' }, content: undefined } ) // Voice calls include device-identity (verified via Frida capture) if (!isVideo) { - offerContent.push( - { tag: 'device-identity', attrs: {}, content: undefined }, - ) + offerContent.push({ tag: 'device-identity', attrs: {}, content: undefined }) } const stanza: BinaryNode = { tag: 'call', attrs: { to: jid, - id: stanzaId, + id: stanzaId }, - content: [{ - tag: 'offer', - attrs: { - 'call-creator': meId, - 'call-id': callId, - 'device_class': '2013', - }, - content: offerContent - }] + content: [ + { + tag: 'offer', + attrs: { + 'call-creator': meId, + 'call-id': callId, + device_class: '2013' + }, + content: offerContent + } + ] } await query(stanza) @@ -647,7 +648,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const terminateAttrs: Record = { 'call-id': callId, - 'call-creator': callCreator || meId, + 'call-creator': callCreator || meId } if (reason) { @@ -663,13 +664,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { tag: 'call', attrs: { to: callTo, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'terminate', - attrs: terminateAttrs, - content: undefined - }] + content: [ + { + tag: 'terminate', + attrs: terminateAttrs, + content: undefined + } + ] } await query(stanza) @@ -683,24 +686,18 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { * @param callFrom - JID of the caller (call-creator) * @param isVideo - true for video call */ - const acceptCall = async ( - callId: string, - callFrom: string, - isVideo?: boolean, - ) => { + const acceptCall = async (callId: string, callFrom: string, isVideo?: boolean) => { const meId = authState.creds.me?.id if (!meId) throw new Boom('Not authenticated', { statusCode: 401 }) - const acceptContent: BinaryNode[] = [ - { tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }, - ] + const acceptContent: BinaryNode[] = [{ tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }] if (isVideo) { acceptContent.push({ tag: 'video', attrs: { dec: 'H264,AV1', - device_orientation: '1', + device_orientation: '1' }, content: undefined }) @@ -708,7 +705,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { acceptContent.push( { tag: 'net', attrs: { medium: '2' }, content: undefined }, - { tag: 'encopt', attrs: { keygen: '2' }, content: undefined }, + { tag: 'encopt', attrs: { keygen: '2' }, content: undefined } ) const stanza: BinaryNode = { @@ -716,16 +713,18 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { attrs: { from: meId, to: callFrom, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'accept', - attrs: { - 'call-id': callId, - 'call-creator': callFrom, - }, - content: acceptContent - }] + content: [ + { + tag: 'accept', + attrs: { + 'call-id': callId, + 'call-creator': callFrom + }, + content: acceptContent + } + ] } await query(stanza) @@ -740,14 +739,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { * @param callCreator - JID of the caller * @param isVideo - true for video call */ - const preacceptCall = async ( - callId: string, - callCreator: string, - isVideo?: boolean, - ) => { - const preacceptContent: BinaryNode[] = [ - { tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }, - ] + const preacceptCall = async (callId: string, callCreator: string, isVideo?: boolean) => { + const preacceptContent: BinaryNode[] = [{ tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }] if (isVideo) { preacceptContent.push({ @@ -756,7 +749,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { screen_width: '1080', screen_height: '2400', dec: 'H264,H265,AV1', - device_orientation: '0', + device_orientation: '0' }, content: undefined }) @@ -764,23 +757,25 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { preacceptContent.push( { tag: 'encopt', attrs: { keygen: '2' }, content: undefined }, - { tag: 'capability', attrs: { ver: '1' }, content: undefined }, + { tag: 'capability', attrs: { ver: '1' }, content: undefined } ) const stanza: BinaryNode = { tag: 'call', attrs: { to: callCreator, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'preaccept', - attrs: { - 'call-id': callId, - 'call-creator': callCreator, - }, - content: preacceptContent - }] + content: [ + { + tag: 'preaccept', + attrs: { + 'call-id': callId, + 'call-creator': callCreator + }, + content: preacceptContent + } + ] } await query(stanza) @@ -806,11 +801,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { dlBw?: number ulBw?: number }>, - transactionId?: string, + transactionId?: string ) => { const relayLatencyAttrs: Record = { 'call-id': callId, - 'call-creator': callCreator, + 'call-creator': callCreator } if (transactionId) { @@ -843,13 +838,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { tag: 'call', attrs: { to: callCreator, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'relaylatency', - attrs: relayLatencyAttrs, - content: teChildren - }] + content: [ + { + tag: 'relaylatency', + attrs: relayLatencyAttrs, + content: teChildren + } + ] } await sendNode(stanza) @@ -870,12 +867,12 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { callCreator: string, to: string, candidates: Array<{ priority: string; data?: Uint8Array }>, - round?: number, + round?: number ) => { const transportAttrs: Record = { 'call-id': callId, 'call-creator': callCreator, - 'transport-message-type': '1', + 'transport-message-type': '1' } if (round !== undefined) { @@ -885,20 +882,22 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const teChildren: BinaryNode[] = candidates.map(c => ({ tag: 'te', attrs: { priority: c.priority }, - content: c.data, + content: c.data })) const stanza: BinaryNode = { tag: 'call', attrs: { to, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'transport', - attrs: transportAttrs, - content: teChildren - }] + content: [ + { + tag: 'transport', + attrs: transportAttrs, + content: teChildren + } + ] } await sendNode(stanza) @@ -919,25 +918,27 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { callCreator: string, peer: string, audioDuration: number, - callType: string = '1x1', + callType = '1x1' ) => { const stanza: BinaryNode = { tag: 'call', attrs: { to: 'call', - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'duration', - attrs: { - 'call-id': callId, - 'call-creator': callCreator, - peer, - audio_duration: String(audioDuration), - type: callType, - }, - content: undefined - }] + content: [ + { + tag: 'duration', + attrs: { + 'call-id': callId, + 'call-creator': callCreator, + peer, + audio_duration: String(audioDuration), + type: callType + }, + content: undefined + } + ] } await sendNode(stanza) @@ -952,27 +953,24 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { * @param to - destination JID * @param muted - true to mute, false to unmute */ - const muteCall = async ( - callId: string, - callCreator: string, - to: string, - muted: boolean, - ) => { + const muteCall = async (callId: string, callCreator: string, to: string, muted: boolean) => { const stanza: BinaryNode = { tag: 'call', attrs: { to, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'mute_v2', - attrs: { - 'mute-state': muted ? '1' : '0', - 'call-id': callId, - 'call-creator': callCreator, - }, - content: undefined - }] + content: [ + { + tag: 'mute_v2', + attrs: { + 'mute-state': muted ? '1' : '0', + 'call-id': callId, + 'call-creator': callCreator + }, + content: undefined + } + ] } await sendNode(stanza) @@ -985,24 +983,23 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { * @param callId - the call-id (also used as JID with @call) * @param callCreator - JID of the call creator */ - const sendHeartbeat = async ( - callId: string, - callCreator: string, - ) => { + const sendHeartbeat = async (callId: string, callCreator: string) => { const stanza: BinaryNode = { tag: 'call', attrs: { to: `${callId}@call`, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'heartbeat', - attrs: { - 'call-id': callId, - 'call-creator': callCreator, - }, - content: undefined - }] + content: [ + { + tag: 'heartbeat', + attrs: { + 'call-id': callId, + 'call-creator': callCreator + }, + content: undefined + } + ] } await sendNode(stanza) @@ -1017,30 +1014,27 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { * @param to - destination JID * @param transactionId - transaction ID for the rekey */ - const sendEncRekey = async ( - callId: string, - callCreator: string, - to: string, - transactionId: string, - ) => { + const sendEncRekey = async (callId: string, callCreator: string, to: string, transactionId: string) => { const stanza: BinaryNode = { tag: 'call', attrs: { to, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'enc_rekey', - attrs: { - 'transaction-id': transactionId, - 'call-id': callId, - 'call-creator': callCreator, - }, - content: [ - { tag: 'encopt', attrs: { keygen: '2' }, content: undefined }, - { tag: 'enc', attrs: { v: '2', type: 'msg' }, content: undefined }, - ] - }] + content: [ + { + tag: 'enc_rekey', + attrs: { + 'transaction-id': transactionId, + 'call-id': callId, + 'call-creator': callCreator + }, + content: [ + { tag: 'encopt', attrs: { keygen: '2' }, content: undefined }, + { tag: 'enc', attrs: { v: '2', type: 'msg' }, content: undefined } + ] + } + ] } await sendNode(stanza) @@ -1061,24 +1055,26 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { callCreator: string, to: string, enabled: boolean, - orientation: string = '1', + orientation = '1' ) => { const stanza: BinaryNode = { tag: 'call', attrs: { to, - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'video', - attrs: { - 'call-id': callId, - 'call-creator': callCreator, - state: enabled ? '1' : '0', - device_orientation: orientation, - }, - content: undefined - }] + content: [ + { + tag: 'video', + attrs: { + 'call-id': callId, + 'call-creator': callCreator, + state: enabled ? '1' : '0', + device_orientation: orientation + }, + content: undefined + } + ] } await sendNode(stanza) @@ -1100,15 +1096,17 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { tag: 'call', attrs: { to: 'call', - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'link_create', - attrs: { media }, - content: event - ? [{ tag: 'event', attrs: { start_time: String(event.startTime) }, content: undefined }] - : undefined - }] + content: [ + { + tag: 'link_create', + attrs: { media }, + content: event + ? [{ tag: 'event', attrs: { start_time: String(event.startTime) }, content: undefined }] + : undefined + } + ] } const response = await query(stanza, timeoutMs) @@ -1136,9 +1134,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { } // URL format verified via Frida capture: https://call.whatsapp.com/ - const url = token - ? `https://call.whatsapp.com/${token}` - : undefined + const url = token ? `https://call.whatsapp.com/${token}` : undefined return { token, url, response } } @@ -1156,13 +1152,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { tag: 'call', attrs: { to: 'call', - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'link_query', - attrs: { media, token }, - content: undefined - }] + content: [ + { + tag: 'link_query', + attrs: { media, token }, + content: undefined + } + ] } return await query(stanza) @@ -1180,7 +1178,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const joinContent: BinaryNode[] = [ { tag: 'audio', attrs: { rate: '16000', enc: 'opus' }, content: undefined }, { tag: 'net', attrs: { medium: '2' }, content: undefined }, - { tag: 'capability', attrs: { ver: '1' }, content: undefined }, + { tag: 'capability', attrs: { ver: '1' }, content: undefined } ] if (media === 'video') { @@ -1190,7 +1188,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { screen_width: '1080', screen_height: '2400', dec: 'H264,H265,AV1', - device_orientation: '0', + device_orientation: '0' }, content: undefined }) @@ -1200,13 +1198,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { tag: 'call', attrs: { to: 'call', - id: randomBytes(16).toString('hex').toUpperCase(), + id: randomBytes(16).toString('hex').toUpperCase() }, - content: [{ - tag: 'link_join', - attrs: { media, token }, - content: joinContent - }] + content: [ + { + tag: 'link_join', + attrs: { media, token }, + content: joinContent + } + ] } return await query(stanza) @@ -1223,7 +1223,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // For group messages, scope by participant (each participant has its own Signal session). const retryDedupeJid = msgKey.participant ? jidNormalizedUser(msgKey.participant) - : jidNormalizedUser(node.attrs.from!) + : jidNormalizedUser(node.attrs.from) if (retryRequestActiveJids.has(retryDedupeJid)) { logger.debug( { fromJid: retryDedupeJid, msgId }, @@ -1246,7 +1246,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // this cleanup only runs as a last resort. // For group messages, use participant JID (Signal sessions are per-participant, not per-group). if (autoCleanCorrupted) { - const senderJid = msgKey.participant ? jidNormalizedUser(msgKey.participant) : jidNormalizedUser(node.attrs.from!) + const senderJid = msgKey.participant + ? jidNormalizedUser(msgKey.participant) + : jidNormalizedUser(node.attrs.from) try { const decryptionJid = await getDecryptionJid(senderJid, signalRepository) const deletedCount = await cleanupCorruptedSession(decryptionJid, signalRepository, logger) @@ -1282,7 +1284,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // Safety net cleanup (same as new system above) if (autoCleanCorrupted) { - const senderJid = msgKey.participant ? jidNormalizedUser(msgKey.participant) : jidNormalizedUser(node.attrs.from!) + const senderJid = msgKey.participant + ? jidNormalizedUser(msgKey.participant) + : jidNormalizedUser(node.attrs.from) try { const decryptionJid = await getDecryptionJid(senderJid, signalRepository) await cleanupCorruptedSession(decryptionJid, signalRepository, logger) @@ -1464,13 +1468,13 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const senderTs = unixTimestampSeconds() logTcToken('reissue', { jid: normalizedJid, reason: 'session_refreshed' }) getPrivacyTokens([normalizedJid], senderTs) - .then(async (iqResult) => { + .then(async iqResult => { await storeTcTokensFromIqResult({ result: iqResult, fallbackJid: normalizedJid, keys: authState.keys, getLIDForPN, - onNewJidStored: (storedJid) => { + onNewJidStored: storedJid => { tcTokenKnownJids.add(storedJid) scheduleTcTokenIndexSave() } @@ -1516,16 +1520,20 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn! // Resolve acting participant to PN — prefer inline PN, fall back to LID→PN resolution - const actingParticipant = actingParticipantPn - || await resolveLidToPn(actingParticipantLid, lidMapping, logger) + const actingParticipant = actingParticipantPn || (await resolveLidToPn(actingParticipantLid, lidMapping, logger)) // Resolve affected participant to PN - const affectedParticipant = affectedParticipantPn - || await resolveLidToPn(affectedParticipantLid, lidMapping, logger) + const affectedParticipant = + affectedParticipantPn || (await resolveLidToPn(affectedParticipantLid, lidMapping, logger)) // Store LID↔PN mappings from notification attributes const mappingsToStore: Array<{ lid: string; pn: string }> = [] - if (actingParticipantLid && actingParticipantPn && isLidUser(actingParticipantLid) && isPnUser(actingParticipantPn)) { + if ( + actingParticipantLid && + actingParticipantPn && + isLidUser(actingParticipantLid) && + isPnUser(actingParticipantPn) + ) { mappingsToStore.push({ lid: actingParticipantLid, pn: actingParticipantPn }) } @@ -1554,7 +1562,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // Resolve metadata owner to PN if (metadata.owner && isLidUser(metadata.owner)) { - const resolvedOwner = metadata.ownerPn || await resolveLidToPn(metadata.owner, lidMapping, logger) + const resolvedOwner = metadata.ownerPn || (await resolveLidToPn(metadata.owner, lidMapping, logger)) if (resolvedOwner) metadata.owner = resolvedOwner } @@ -1588,7 +1596,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { case 'modify': const oldNumbers = await Promise.all( getBinaryNodeChildren(child, 'participant').map(async p => { - const resolved = await resolveLidToPn(p.attrs.jid!, lidMapping, logger) + const resolved = await resolveLidToPn(p.attrs.jid, lidMapping, logger) return resolved || p.attrs.jid! }) ) @@ -1770,7 +1778,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { { const rawPictureJid = jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '' - const pictureJid = await resolveLidToPn(rawPictureJid, signalRepository.lidMapping, logger) || rawPictureJid + const pictureJid = (await resolveLidToPn(rawPictureJid, signalRepository.lidMapping, logger)) || rawPictureJid ev.emit('contacts.update', [ { id: pictureJid, @@ -1815,7 +1823,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const blocklists = getBinaryNodeChildren(child, 'item') for (const { attrs } of blocklists) { - const resolvedBlockJid = await resolveLidToPn(attrs.jid!, signalRepository.lidMapping, logger) || attrs.jid! + const resolvedBlockJid = + (await resolveLidToPn(attrs.jid, signalRepository.lidMapping, logger)) || attrs.jid! const blocklist = [resolvedBlockJid] const type = attrs.action === 'block' ? 'add' : 'remove' ev.emit('blocklist.update', { blocklist, type }) @@ -2146,7 +2155,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { if (attrs.participant) { const updateKey: keyof MessageUserReceipt = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp' - const resolvedReceiptUserJid = await resolveLidToPn(attrs.participant, lidMapping, logger) || jidNormalizedUser(attrs.participant) + const resolvedReceiptUserJid = + (await resolveLidToPn(attrs.participant, lidMapping, logger)) || jidNormalizedUser(attrs.participant) ev.emit( 'message-receipt.update', ids.map(id => ({ @@ -2259,13 +2269,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { category, author, decrypt - } = decryptMessageNode( - node, - authState.creds.me!.id, - authState.creds.me!.lid || '', - signalRepository, - logger, - ) + } = decryptMessageNode(node, authState.creds.me!.id, authState.creds.me!.lid || '', signalRepository, logger) const alt = msg.key.participantAlt || msg.key.remoteJidAlt // Handle LID/PN mappings with hybrid approach: @@ -2680,7 +2684,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { jid: u.attrs.jid, state: u.attrs.state, userPn: u.attrs.user_pn, - type: u.attrs.type, + type: u.attrs.type })) } @@ -2768,9 +2772,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const callSummary = getBinaryNodeChild(infoChild, 'call_summary') if (callSummary) { call.media = callSummary.attrs.media - call.duration = callSummary.attrs.call_duration - ? Number(callSummary.attrs.call_duration) - : undefined + call.duration = callSummary.attrs.call_duration ? Number(callSummary.attrs.call_duration) : undefined call.participants = extractParticipants(callSummary) } } @@ -2814,12 +2816,14 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { if (resolvedLinkCreator) call.linkCreator = resolvedLinkCreator // Resolve participant JIDs in parallel if (call.participants) { - await Promise.all(call.participants.map(async (p) => { - if (p.jid) { - const resolved = p.userPn || await resolveLidToPn(p.jid, callLidMapping, logger) - if (resolved) p.jid = resolved - } - })) + await Promise.all( + call.participants.map(async p => { + if (p.jid) { + const resolved = p.userPn || (await resolveLidToPn(p.jid, callLidMapping, logger)) + if (resolved) p.jid = resolved + } + }) + ) } ev.emit('call', [call]) @@ -2857,20 +2861,22 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // WABA Android: error 463 triggers getPrivacyTokens() fire-and-forget // to ensure token is available for the retry below getPrivacyTokens([jid]) - .then(async (result) => { + .then(async result => { await storeTcTokensFromIqResult({ result, fallbackJid: jid, keys: authState.keys, getLIDForPN, - onNewJidStored: (storedJid) => { + onNewJidStored: storedJid => { tcTokenKnownJids.add(storedJid) scheduleTcTokenIndexSave() } }) logTcToken('fetched', { jid, reason: 'error_463' }) }) - .catch(() => { /* fire-and-forget */ }) + .catch(() => { + /* fire-and-forget */ + }) // Single-retry: wait 1.5s for the server's tctoken notification to arrive, // then resend. A Set prevents infinite retry loops. @@ -2911,20 +2917,22 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { logTcToken('error_479', { jid: jid479, msgId: attrs.id }) // WABA Android: error 479 (SmaxInvalid) also triggers token re-fetch getPrivacyTokens([jid479]) - .then(async (result) => { + .then(async result => { await storeTcTokensFromIqResult({ result, fallbackJid: jid479, keys: authState.keys, getLIDForPN, - onNewJidStored: (storedJid) => { + onNewJidStored: storedJid => { tcTokenKnownJids.add(storedJid) scheduleTcTokenIndexSave() } }) logTcToken('fetched', { jid: jid479, reason: 'error_479' }) }) - .catch(() => { /* fire-and-forget */ }) + .catch(() => { + /* fire-and-forget */ + }) } else { logger.warn({ attrs }, 'received error in ack') } @@ -3058,24 +3066,20 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // (call link relays may arrive without these attrs — just log them) if (callId && rawCallCreator) { // Resolve LID→PN for call creator - const callCreator = await resolveLidToPn(rawCallCreator, signalRepository.lidMapping, logger) || rawCallCreator - logger.debug( - { callId, callCreator, uuid: node.attrs.uuid }, - 'received relay info' - ) - ev.emit('call', [{ - chatId: callCreator, - from: callCreator, - id: callId, - date: new Date(), - offline: false, - status: 'relay' as WACallUpdateType, - }]) + const callCreator = (await resolveLidToPn(rawCallCreator, signalRepository.lidMapping, logger)) || rawCallCreator + logger.debug({ callId, callCreator, uuid: node.attrs.uuid }, 'received relay info') + ev.emit('call', [ + { + chatId: callCreator, + from: callCreator, + id: callId, + date: new Date(), + offline: false, + status: 'relay' as WACallUpdateType + } + ]) } else { - logger.debug( - { attrs: node.attrs }, - 'received relay stanza without call-id/call-creator' - ) + logger.debug({ attrs: node.attrs }, 'received relay stanza without call-id/call-creator') } }) diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index a53bd937..e3b93c8a 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -1,6 +1,6 @@ -import { randomBytes } from 'crypto' import NodeCache from '@cacheable/node-cache' import { Boom } from '@hapi/boom' +import { randomBytes } from 'crypto' import { proto } from '../../WAProto/index.js' import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults' import type { @@ -74,8 +74,6 @@ import { import { USyncQuery, USyncUser } from '../WAUSync' import { makeNewsletterSocket } from './newsletter' -/* eslint-disable max-depth */ - export const makeMessagesSocket = (config: SocketConfig) => { const { logger, @@ -601,7 +599,6 @@ export const makeMessagesSocket = (config: SocketConfig) => { return patchedMessage } - /* eslint-disable max-depth */ const createParticipantNodes = async ( recipientJids: string[], message: proto.IMessage, @@ -669,7 +666,6 @@ export const makeMessagesSocket = (config: SocketConfig) => { return { nodes, shouldIncludeDeviceIdentity } } - /* eslint-enable max-depth */ // Interactive message detection and binary node injection diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index 0fdc80d6..4f18408c 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -1361,13 +1361,16 @@ export const makeSocket = (config: SocketConfig) => { const pairPlatformId = isAndroid ? getPlatformId('Chrome') : getPlatformId(browser[1]) const pairPlatformDisplay = isAndroid ? 'Chrome (Mac OS)' : `${browser[1]} (${browser[0]})` - logger.info({ - pairCode: pairingCode, - jid: authState.creds.me.id, - companionPlatformId: pairPlatformId, - companionPlatformDisplay: pairPlatformDisplay, - isAndroid, - }, `pair code requested | companion: ${pairPlatformDisplay} | ${isAndroid ? 'android override -> Chrome' : 'native platform'}`) + logger.info( + { + pairCode: pairingCode, + jid: authState.creds.me.id, + companionPlatformId: pairPlatformId, + companionPlatformDisplay: pairPlatformDisplay, + isAndroid + }, + `pair code requested | companion: ${pairPlatformDisplay} | ${isAndroid ? 'android override -> Chrome' : 'native platform'}` + ) ev.emit('creds.update', authState.creds) await sendNode({ @@ -1538,7 +1541,9 @@ export const makeSocket = (config: SocketConfig) => { ws.on('CB:success', async (node: BinaryNode) => { const isAndroid = isAndroidBrowser(browser) const phoneId = authState.creds.me?.id?.split(':')[0]?.split('@')[0] || 'new session' - logger.info(`${isAndroid ? '\uD83D\uDCF1' : '\uD83D\uDDA5\uFE0F'} Connected to WA | ${phoneId} | platform: ${isAndroid ? 'SMB_ANDROID' : 'MACOS'} | device: ${isAndroid ? 'Android' : 'Desktop'} | platformType: ${isAndroid ? 'ANDROID_PHONE' : 'CHROME'}`) + logger.info( + `${isAndroid ? '\uD83D\uDCF1' : '\uD83D\uDDA5\uFE0F'} Connected to WA | ${phoneId} | platform: ${isAndroid ? 'SMB_ANDROID' : 'MACOS'} | device: ${isAndroid ? 'Android' : 'Desktop'} | platformType: ${isAndroid ? 'ANDROID_PHONE' : 'CHROME'}` + ) clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try ev.emit('creds.update', { me: { ...authState.creds.me!, lid: node.attrs.lid } }) diff --git a/src/Utils/circuit-breaker.ts b/src/Utils/circuit-breaker.ts index 00be9cc6..155289f3 100644 --- a/src/Utils/circuit-breaker.ts +++ b/src/Utils/circuit-breaker.ts @@ -716,7 +716,7 @@ export function createConnectionCircuitBreaker(customOptions?: Partial { const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid) return { diff --git a/src/Utils/generics.ts b/src/Utils/generics.ts index 24b0bc28..a7ee803a 100644 --- a/src/Utils/generics.ts +++ b/src/Utils/generics.ts @@ -246,6 +246,7 @@ export const fetchLatestBaileysVersion = async (options: RequestInit & { timeout } finally { clearTimeout(timeout) } + if (!response.ok) { throw new Boom(`Failed to fetch latest Baileys version: ${response.statusText}`, { statusCode: response.status }) } diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index d3836990..d4fe549e 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -45,8 +45,8 @@ import { generateThumbnail, getAudioDuration, getAudioWaveform, - getStream, getRawMediaUploadData, + getStream, type MediaDownloadOptions } from './messages-media' import { shouldIncludeReportingToken } from './reporting-utils' @@ -658,14 +658,17 @@ export const generateCarouselMessage = async ( const { stream } = await getStream(card.image, mediaOptions.options) const { buffer, original } = await extractImageThumb(stream) + // eslint-disable-next-line max-depth if (!imageMessage.jpegThumbnail) { imageMessage.jpegThumbnail = buffer.toString('base64') } + // eslint-disable-next-line max-depth if (!imageMessage.width && original.width) { imageMessage.width = original.width } + // eslint-disable-next-line max-depth if (!imageMessage.height && original.height) { imageMessage.height = original.height } diff --git a/src/Utils/process-message.ts b/src/Utils/process-message.ts index 19b95a2b..8314bf6f 100644 --- a/src/Utils/process-message.ts +++ b/src/Utils/process-message.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import Long from 'long' import { proto } from '../../WAProto/index.js' +import type { LIDMappingStore } from '../Signal/lid-mapping' import type { AuthenticationCreds, BaileysEventEmitter, @@ -19,7 +20,6 @@ import type { WAMessage, WAMessageKey } from '../Types' -import type { LIDMappingStore } from '../Signal/lid-mapping' import { WAMessageStubType } from '../Types' import { getContentType, normalizeMessageContent } from '../Utils/messages' import { @@ -784,7 +784,8 @@ const processMessage = async ( }) } - const participantsIncludesMe = () => participants.find(p => areJidsSameUser(meId, p.id) || areJidsSameUser(meId, p.phoneNumber)) + const participantsIncludesMe = () => + participants.find(p => areJidsSameUser(meId, p.id) || areJidsSameUser(meId, p.phoneNumber)) switch (message.messageStubType) { case WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER: diff --git a/src/Utils/sticker-pack.ts b/src/Utils/sticker-pack.ts index b1357721..d1f2bdf5 100644 --- a/src/Utils/sticker-pack.ts +++ b/src/Utils/sticker-pack.ts @@ -2,7 +2,7 @@ import { Boom } from '@hapi/boom' import { createHash } from 'crypto' import { zipSync } from 'fflate' import { promises as fs } from 'fs' -import { gzipSync, gunzipSync } from 'zlib' +import { gunzipSync, gzipSync } from 'zlib' import { proto } from '../../WAProto/index.js' import type { MediaType } from '../Defaults/index.js' import type { StickerPack, WAMediaUpload, WAMediaUploadFunction } from '../Types/Message.js' @@ -728,10 +728,9 @@ export const prepareStickerPackMessage = async ( // Tray icon uses PNG format, 96x96 pixels (official client standard) const lib = await getImageProcessingLibrary() if (!lib?.sharp) { - throw new Boom( - 'Sharp library is required for cover/tray icon processing. Install with: yarn add sharp', - { statusCode: 400 } - ) + throw new Boom('Sharp library is required for cover/tray icon processing. Install with: yarn add sharp', { + statusCode: 400 + }) } coverWebP = await lib.sharp