fix: revert ?? '' and defensive null checks across Socket files

PRs 124-129 replaced TypeScript non-null assertions (!) with optional
chaining (?.) and empty string defaults (?? ''). While defensive
programming is usually good, in Signal protocol and WhatsApp binary
node handling code these changes caused:

- Malformed JIDs with empty user components (e.g. @s.whatsapp.net)
- Silent device enumeration failures (messages skip recipients)
- Wrong own-device detection (message routing errors)
- Broken prekey count handling (prekey uploads silently skipped)
- Wrong retry count tracking in Signal protocol

These empty-string defaults are dangerous because Signal session
lookups fail for malformed JIDs, causing "SessionError: No sessions".

Reverted to original Baileys pattern using non-null assertions (!)
which match the upstream reference (infinitezap/Teste_InfiniteAPI).

Files fixed: messages-send.ts, messages-recv.ts, socket.ts, chats.ts,
groups.ts, communities.ts, business.ts

https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB
This commit is contained in:
Claude
2026-02-09 10:10:56 +00:00
parent e7c31f9270
commit 48b63565b6
7 changed files with 81 additions and 114 deletions
+1 -1
View File
@@ -118,7 +118,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
content: [ content: [
{ {
tag: 'cover_photo', tag: 'cover_photo',
attrs: { id: String(fbid), op: 'update', token: meta_hmac ?? '', ts: String(ts) } attrs: { id: String(fbid), op: 'update', token: meta_hmac!, ts: String(ts) }
} }
] ]
} }
+2 -2
View File
@@ -1107,8 +1107,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
ev.emit('messages.upsert', { messages: [msg], type }) ev.emit('messages.upsert', { messages: [msg], type })
if (!!msg.pushName) { if (!!msg.pushName) {
let jid = msg.key.fromMe ? (authState.creds.me?.id ?? '') : msg.key.participant || msg.key.remoteJid let jid = msg.key.fromMe ? authState.creds.me!.id : msg.key.participant || msg.key.remoteJid
jid = jidNormalizedUser(jid ?? '') jid = jidNormalizedUser(jid!)
if (!msg.key.fromMe) { if (!msg.key.fromMe) {
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName! }]) ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName! }])
+4 -4
View File
@@ -358,13 +358,13 @@ export const makeCommunitiesSocket = (config: SocketConfig) => {
communityAcceptInviteV4: ev.createBufferedFunction( communityAcceptInviteV4: ev.createBufferedFunction(
async (key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => { async (key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => {
key = typeof key === 'string' ? { remoteJid: key } : key key = typeof key === 'string' ? { remoteJid: key } : key
const results = await communityQuery(inviteMessage.groupJid ?? '', 'set', [ const results = await communityQuery(inviteMessage.groupJid!, 'set', [
{ {
tag: 'accept', tag: 'accept',
attrs: { attrs: {
code: inviteMessage.inviteCode ?? '', code: inviteMessage.inviteCode!,
expiration: (inviteMessage.inviteExpiration ?? 0).toString(), expiration: (inviteMessage.inviteExpiration ?? 0).toString(),
admin: key.remoteJid ?? '' admin: key.remoteJid!
} }
} }
]) ])
@@ -475,7 +475,7 @@ export const extractCommunityMetadata = (result: BinaryNode) => {
participants: getBinaryNodeChildren(community, 'participant').map(({ attrs }) => { participants: getBinaryNodeChildren(community, 'participant').map(({ attrs }) => {
return { return {
// TODO: IMPLEMENT THE PN/LID FIELDS HERE!! // TODO: IMPLEMENT THE PN/LID FIELDS HERE!!
id: attrs.jid ?? '', id: attrs.jid!,
admin: (attrs.type || null) as GroupParticipant['admin'] admin: (attrs.type || null) as GroupParticipant['admin']
} }
}), }),
+6 -6
View File
@@ -225,12 +225,12 @@ export const makeGroupsSocket = (config: SocketConfig) => {
groupAcceptInviteV4: ev.createBufferedFunction( groupAcceptInviteV4: ev.createBufferedFunction(
async (key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => { async (key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => {
key = typeof key === 'string' ? { remoteJid: key } : key key = typeof key === 'string' ? { remoteJid: key } : key
const results = await groupQuery(inviteMessage.groupJid ?? '', 'set', [ const results = await groupQuery(inviteMessage.groupJid!, 'set', [
{ {
tag: 'accept', tag: 'accept',
attrs: { attrs: {
code: inviteMessage.inviteCode ?? '', code: inviteMessage.inviteCode!,
expiration: String(inviteMessage.inviteExpiration ?? ''), expiration: inviteMessage.inviteExpiration!.toString(),
admin: key.remoteJid! admin: key.remoteJid!
} }
} }
@@ -316,14 +316,14 @@ export const extractGroupMetadata = (result: BinaryNode) => {
descId = descChild.attrs.id descId = descChild.attrs.id
} }
const groupId = (group.attrs.id ?? '').includes('@') ? (group.attrs.id ?? '') : jidEncode(group.attrs.id ?? '', 'g.us') const groupId = group.attrs.id!.includes('@') ? group.attrs.id! : jidEncode(group.attrs.id!, 'g.us')
const eph = getBinaryNodeChild(group, 'ephemeral')?.attrs.expiration const eph = getBinaryNodeChild(group, 'ephemeral')?.attrs.expiration
const memberAddMode = getBinaryNodeChildString(group, 'member_add_mode') === 'all_member_add' const memberAddMode = getBinaryNodeChildString(group, 'member_add_mode') === 'all_member_add'
const metadata: GroupMetadata = { const metadata: GroupMetadata = {
id: groupId, id: groupId,
notify: group.attrs.notify, notify: group.attrs.notify,
addressingMode: group.attrs.addressing_mode === 'lid' ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN, addressingMode: group.attrs.addressing_mode === 'lid' ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN,
subject: group.attrs.subject ?? '', subject: group.attrs.subject!,
subjectOwner: group.attrs.s_o, subjectOwner: group.attrs.s_o,
subjectOwnerPn: group.attrs.s_o_pn, subjectOwnerPn: group.attrs.s_o_pn,
subjectTime: +(group.attrs.s_t ?? '0'), subjectTime: +(group.attrs.s_t ?? '0'),
@@ -347,7 +347,7 @@ export const extractGroupMetadata = (result: BinaryNode) => {
participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => { participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => {
// TODO: Store LID MAPPINGS // TODO: Store LID MAPPINGS
return { return {
id: attrs.jid ?? '', id: attrs.jid!,
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined, phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined, lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
admin: (attrs.type || null) as GroupParticipant['admin'] admin: (attrs.type || null) as GroupParticipant['admin']
+41 -57
View File
@@ -156,17 +156,16 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
throw new Boom('Not authenticated') throw new Boom('Not authenticated')
} }
const msgId = messageKey?.id ?? '' if (await placeholderResendCache.get(messageKey?.id!)) {
if (await placeholderResendCache.get(msgId)) {
logger.debug({ messageKey }, 'already requested resend') logger.debug({ messageKey }, 'already requested resend')
return return
} else { } else {
await placeholderResendCache.set(msgId, true) await placeholderResendCache.set(messageKey?.id!, true)
} }
await delay(2000) await delay(2000)
if (!(await placeholderResendCache.get(msgId))) { if (!(await placeholderResendCache.get(messageKey?.id!))) {
logger.debug({ messageKey }, 'message received while resend requested') logger.debug({ messageKey }, 'message received while resend requested')
return 'RESOLVED' return 'RESOLVED'
} }
@@ -181,9 +180,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
setTimeout(async () => { setTimeout(async () => {
if (await placeholderResendCache.get(msgId)) { if (await placeholderResendCache.get(messageKey?.id!)) {
logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline') logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline')
await placeholderResendCache.del(msgId) await placeholderResendCache.del(messageKey?.id!)
} }
}, 8_000) }, 8_000)
@@ -234,7 +233,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
if (update.jid && update.user) { if (update.jid && update.user) {
ev.emit('newsletter-participants.update', { ev.emit('newsletter-participants.update', {
id: update.jid, id: update.jid,
author: node.attrs.from ?? '', author: node.attrs.from!,
user: update.user, user: update.user,
new_role: 'ADMIN', new_role: 'ADMIN',
action: 'promote' action: 'promote'
@@ -252,10 +251,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
// Handles newsletter notifications // Handles newsletter notifications
const handleNewsletterNotification = async (node: BinaryNode) => { const handleNewsletterNotification = async (node: BinaryNode) => {
const from = node.attrs.from ?? '' const from = node.attrs.from!
const child = getAllBinaryNodeChildren(node)[0] const child = getAllBinaryNodeChildren(node)[0]!
if (!child) return const author = node.attrs.participant!
const author = node.attrs.participant ?? ''
logger.info({ from, child }, 'got newsletter notification') logger.info({ from, child }, 'got newsletter notification')
@@ -263,7 +261,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
case 'reaction': case 'reaction':
const reactionUpdate = { const reactionUpdate = {
id: from, id: from,
server_id: child.attrs.message_id ?? '', server_id: child.attrs.message_id!,
reaction: { reaction: {
code: getBinaryNodeChildString(child, 'reaction'), code: getBinaryNodeChildString(child, 'reaction'),
count: 1 count: 1
@@ -275,7 +273,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
case 'view': case 'view':
const viewUpdate = { const viewUpdate = {
id: from, id: from,
server_id: child.attrs.message_id ?? '', server_id: child.attrs.message_id!,
count: parseInt(child.content?.toString() || '0', 10) count: parseInt(child.content?.toString() || '0', 10)
} }
ev.emit('newsletter.view', viewUpdate) ev.emit('newsletter.view', viewUpdate)
@@ -285,9 +283,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const participantUpdate = { const participantUpdate = {
id: from, id: from,
author, author,
user: child.attrs.jid ?? '', user: child.attrs.jid!,
action: child.attrs.action ?? '', action: child.attrs.action!,
new_role: child.attrs.role ?? '' new_role: child.attrs.role!
} }
ev.emit('newsletter-participants.update', participantUpdate) ev.emit('newsletter-participants.update', participantUpdate)
break break
@@ -347,8 +345,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const stanza: BinaryNode = { const stanza: BinaryNode = {
tag: 'ack', tag: 'ack',
attrs: { attrs: {
id: attrs.id ?? '', id: attrs.id!,
to: attrs.from ?? '', to: attrs.from!,
class: tag class: tag
} }
} }
@@ -407,16 +405,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
const sendRetryRequest = async (node: BinaryNode, forceIncludeKeys = false) => { const sendRetryRequest = async (node: BinaryNode, forceIncludeKeys = false) => {
const meId = authState.creds.me?.id const { fullMessage } = decodeMessageNode(node, authState.creds.me!.id, authState.creds.me!.lid || '')
if (!meId) throw new Boom('Not authenticated', { statusCode: 401 })
const meLid = authState.creds.me?.lid || ''
const { fullMessage } = decodeMessageNode(node, meId, meLid)
const { key: msgKey } = fullMessage const { key: msgKey } = fullMessage
const msgId = msgKey.id const msgId = msgKey.id!
if (!msgId) {
logger.debug({ node }, 'missing message id in retry request')
return
}
if (messageRetryManager) { if (messageRetryManager) {
// Check if we've exceeded max retries using the new system // Check if we've exceeded max retries using the new system
@@ -454,7 +445,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const retryCount = (await msgRetryCache.get<number>(key)) || 1 const retryCount = (await msgRetryCache.get<number>(key)) || 1
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds
const fromJid = node.attrs.from ?? '' const fromJid = node.attrs.from!
// Check if we should recreate the session // Check if we should recreate the session
let shouldRecreateSession = false let shouldRecreateSession = false
@@ -519,15 +510,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
attrs: { attrs: {
id: msgId, id: msgId,
type: 'retry', type: 'retry',
to: node.attrs.from ?? '' to: node.attrs.from!
}, },
content: [ content: [
{ {
tag: 'retry', tag: 'retry',
attrs: { attrs: {
count: retryCount.toString(), count: retryCount.toString(),
id: node.attrs.id ?? '', id: node.attrs.id!,
t: node.attrs.t ?? '', t: node.attrs.t!,
v: '1', v: '1',
// ADD ERROR FIELD // ADD ERROR FIELD
error: '0' error: '0'
@@ -553,19 +544,16 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const { update, preKeys } = await getNextPreKeys(authState, 1) const { update, preKeys } = await getNextPreKeys(authState, 1)
const [keyId] = Object.keys(preKeys) const [keyId] = Object.keys(preKeys)
if (!keyId) throw new Boom('No pre-keys available') const key = preKeys[+keyId!]
const key = preKeys[+keyId]
if (!key) throw new Boom('Pre-key not found')
const content = receipt.content as BinaryNode[] const content = receipt.content! as BinaryNode[]
if (!content) throw new Boom('Receipt content missing')
content.push({ content.push({
tag: 'keys', tag: 'keys',
attrs: {}, attrs: {},
content: [ content: [
{ tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) }, { tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) },
{ tag: 'identity', attrs: {}, content: identityKey.public }, { tag: 'identity', attrs: {}, content: identityKey.public },
xmppPreKey(key, +keyId), xmppPreKey(key!, +keyId!),
xmppSignedPreKey(signedPreKey), xmppSignedPreKey(signedPreKey),
{ tag: 'device-identity', attrs: {}, content: deviceIdentity } { tag: 'device-identity', attrs: {}, content: deviceIdentity }
] ]
@@ -584,9 +572,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const from = node.attrs.from const from = node.attrs.from
if (from === S_WHATSAPP_NET) { if (from === S_WHATSAPP_NET) {
const countChild = getBinaryNodeChild(node, 'count') const countChild = getBinaryNodeChild(node, 'count')
const countVal = countChild?.attrs?.value const count = +countChild!.attrs.value!
if (!countVal) return
const count = +countVal
const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT
logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count') logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count')
@@ -651,7 +637,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
break break
case 'modify': case 'modify':
const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid ?? '').filter(Boolean) const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid!)
msg.messageStubParameters = oldNumber || [] msg.messageStubParameters = oldNumber || []
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER
break break
@@ -666,7 +652,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => { const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
// TODO: Store LID MAPPINGS // TODO: Store LID MAPPINGS
return { return {
id: attrs.jid ?? '', id: attrs.jid!,
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined, phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined, lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
admin: (attrs.type || null) as GroupParticipant['admin'] admin: (attrs.type || null) as GroupParticipant['admin']
@@ -688,7 +674,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
break break
case 'subject': case 'subject':
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT
msg.messageStubParameters = [child.attrs.subject ?? ''] msg.messageStubParameters = [child.attrs.subject!]
break break
case 'description': case 'description':
const description = getBinaryNodeChild(child, 'body')?.content?.toString() const description = getBinaryNodeChild(child, 'body')?.content?.toString()
@@ -707,7 +693,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
break break
case 'invite': case 'invite':
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK
msg.messageStubParameters = [child.attrs.code ?? ''] msg.messageStubParameters = [child.attrs.code!]
break break
case 'member_add_mode': case 'member_add_mode':
const addMode = child.content const addMode = child.content
@@ -721,7 +707,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const approvalMode = getBinaryNodeChild(child, 'group_join') const approvalMode = getBinaryNodeChild(child, 'group_join')
if (approvalMode) { if (approvalMode) {
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE
msg.messageStubParameters = [approvalMode.attrs.state ?? ''] msg.messageStubParameters = [approvalMode.attrs.state!]
} }
break break
@@ -730,7 +716,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
msg.messageStubParameters = [ msg.messageStubParameters = [
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }), JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
'created', 'created',
child.attrs.request_method ?? '' child.attrs.request_method!
] ]
break break
case 'revoked_membership_requests': case 'revoked_membership_requests':
@@ -760,8 +746,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
break break
case 'w:gp2': case 'w:gp2':
// TODO: HANDLE PARTICIPANT_PN // TODO: HANDLE PARTICIPANT_PN
if (!child) break handleGroupNotification(node, child!, result)
handleGroupNotification(node, child, result)
break break
case 'mediaretry': case 'mediaretry':
const event = decodeMediaRetryNode(node) const event = decodeMediaRetryNode(node)
@@ -771,11 +756,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
await handleEncryptNotification(node) await handleEncryptNotification(node)
break break
case 'devices': case 'devices':
if (!child) break
const devices = getBinaryNodeChildren(child, 'device') const devices = getBinaryNodeChildren(child, 'device')
if ( if (
areJidsSameUser(child.attrs.jid, authState.creds.me?.id) || areJidsSameUser(child!.attrs.jid, authState.creds.me!.id) ||
areJidsSameUser(child.attrs.lid, authState.creds.me?.lid) areJidsSameUser(child!.attrs.lid, authState.creds.me!.lid)
) { ) {
const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid })) const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }))
logger.info({ deviceData }, 'my own devices changed') logger.info({ deviceData }, 'my own devices changed')
@@ -840,7 +824,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const blocklists = getBinaryNodeChildren(child, 'item') const blocklists = getBinaryNodeChildren(child, 'item')
for (const { attrs } of blocklists) { for (const { attrs } of blocklists) {
const blocklist = [attrs.jid ?? ''] const blocklist = [attrs.jid!]
const type = attrs.action === 'block' ? 'add' : 'remove' const type = attrs.action === 'block' ? 'add' : 'remove'
ev.emit('blocklist.update', { blocklist, type }) ev.emit('blocklist.update', { blocklist, type })
} }
@@ -890,7 +874,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
{ {
tag: 'link_code_companion_reg', tag: 'link_code_companion_reg',
attrs: { attrs: {
jid: authState.creds.me?.id ?? '', jid: authState.creds.me!.id,
stage: 'companion_finish' stage: 'companion_finish'
}, },
content: [ content: [
@@ -1092,7 +1076,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const handleReceipt = async (node: BinaryNode) => { const handleReceipt = async (node: BinaryNode) => {
const { attrs, content } = node const { attrs, content } = node
const isLid = (attrs.from ?? '').includes('lid') const isLid = attrs.from!.includes('lid')
const isNodeFromMe = areJidsSameUser( const isNodeFromMe = areJidsSameUser(
attrs.participant || attrs.from, attrs.participant || attrs.from,
isLid ? authState.creds.me?.lid : authState.creds.me?.id isLid ? authState.creds.me?.lid : authState.creds.me?.id
@@ -1198,7 +1182,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
notificationMutex.mutex(async () => { notificationMutex.mutex(async () => {
const msg = await processNotification(node) const msg = await processNotification(node)
if (msg) { if (msg) {
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me?.id ?? '') const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me!.id)
const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node) const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node)
msg.key = { msg.key = {
remoteJid, remoteJid,
@@ -1242,7 +1226,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
category, category,
author, author,
decrypt 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 const alt = msg.key.participantAlt || msg.key.remoteJidAlt
// Handle LID/PN mappings with hybrid approach: // Handle LID/PN mappings with hybrid approach:
@@ -1527,7 +1511,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
// JID normalization moved BEFORE mutex acquisition (line 1273) to prevent race conditions // JID normalization moved BEFORE mutex acquisition (line 1273) to prevent race conditions
// cleanMessage still runs inside mutex to ensure atomic message processing // cleanMessage still runs inside mutex to ensure atomic message processing
cleanMessage(msg, authState.creds.me?.id ?? '', authState.creds.me?.lid ?? '') cleanMessage(msg, authState.creds.me!.id, authState.creds.me!.lid!)
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify') await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
+20 -21
View File
@@ -131,11 +131,11 @@ export const makeMessagesSocket = (config: SocketConfig) => {
// TODO: explore full length of data that whatsapp provides // TODO: explore full length of data that whatsapp provides
const node: MediaConnInfo = { const node: MediaConnInfo = {
hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({ hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
hostname: attrs.hostname ?? '', hostname: attrs.hostname!,
maxContentLengthBytes: +(attrs.maxContentLengthBytes ?? 0) maxContentLengthBytes: +attrs.maxContentLengthBytes!
})), })),
auth: mediaConnNode.attrs.auth ?? '', auth: mediaConnNode.attrs.auth!,
ttl: +(mediaConnNode.attrs.ttl ?? 0), ttl: +mediaConnNode.attrs.ttl!,
fetchDate: new Date() fetchDate: new Date()
} }
logger.debug('fetched media conn') logger.debug('fetched media conn')
@@ -163,7 +163,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const node: BinaryNode = { const node: BinaryNode = {
tag: 'receipt', tag: 'receipt',
attrs: { attrs: {
id: messageIds[0] ?? '' id: messageIds[0]!
} }
} }
const isReadReceipt = type === 'read' || type === 'read-self' const isReadReceipt = type === 'read' || type === 'read-self'
@@ -268,11 +268,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
} }
for (const { jid, user } of jidsWithUser) { for (const { jid, user } of jidsWithUser) {
if (!user) continue
if (useCache) { if (useCache) {
const devices = const devices =
mgetDevices?.[user] || mgetDevices?.[user!] ||
(userDevicesCache.mget ? undefined : ((await userDevicesCache.get(user)) as FullJid[])) (userDevicesCache.mget ? undefined : ((await userDevicesCache.get(user!)) as FullJid[]))
if (devices) { if (devices) {
const devicesWithJid = devices.map(d => ({ const devicesWithJid = devices.map(d => ({
...d, ...d,
@@ -567,8 +566,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
let msgToEncrypt = patchedMessage let msgToEncrypt = patchedMessage
if (dsmMessage) { if (dsmMessage) {
const targetUser = jidDecode(jid)?.user ?? '' const { user: targetUser } = jidDecode(jid)!
const ownPnUser = jidDecode(meId)?.user ?? '' const { user: ownPnUser } = jidDecode(meId)!
const ownLidUser = meLidUser const ownLidUser = meLidUser
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser) const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser)
@@ -1081,7 +1080,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation') logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
} }
const ownUser = jidDecode(ownId)?.user ?? '' const { user: ownUser } = jidDecode(ownId)!
if (!participant) { if (!participant) {
const patchedForReporting = await patchMessageBeforeSending(message, [jid]) const patchedForReporting = await patchMessageBeforeSending(message, [jid])
reportingMessage = Array.isArray(patchedForReporting) reportingMessage = Array.isArray(patchedForReporting)
@@ -1099,7 +1098,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
if (user !== ownUser) { if (user !== ownUser) {
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net' const ownUserServer = isLid ? 'lid' : 's.whatsapp.net'
const ownUserForAddressing = isLid && meLid ? (jidDecode(meLid)?.user ?? '') : (jidDecode(meId)?.user ?? '') const ownUserForAddressing = isLid && meLid ? jidDecode(meLid)!.user : jidDecode(meId)!.user
devices.push({ devices.push({
user: ownUserForAddressing, user: ownUserForAddressing,
@@ -1115,8 +1114,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
// Use conversation-appropriate sender identity // Use conversation-appropriate sender identity
const senderIdentity = const senderIdentity =
isLid && meLid isLid && meLid
? jidEncode(jidDecode(meLid)?.user ?? '', 'lid', undefined) ? jidEncode(jidDecode(meLid)?.user!, 'lid', undefined)
: jidEncode(jidDecode(meId)?.user ?? '', 's.whatsapp.net', undefined) : jidEncode(jidDecode(meId)?.user!, 's.whatsapp.net', undefined)
// Enumerate devices for sender and target with consistent addressing // Enumerate devices for sender and target with consistent addressing
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false) const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false)
@@ -1135,8 +1134,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const allRecipients: string[] = [] const allRecipients: string[] = []
const meRecipients: string[] = [] const meRecipients: string[] = []
const otherRecipients: string[] = [] const otherRecipients: string[] = []
const mePnUser = jidDecode(meId)?.user ?? '' const { user: mePnUser } = jidDecode(meId)!
const meLidUser = meLid ? (jidDecode(meLid)?.user ?? null) : null const { user: meLidUser } = meLid ? jidDecode(meLid)! : { user: null }
// Carousel in DSM wrapper causes error 479 on own devices // Carousel in DSM wrapper causes error 479 on own devices
const isCarouselMsg = isCarouselMessage(message) const isCarouselMsg = isCarouselMessage(message)
@@ -1212,7 +1211,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
attrs: { attrs: {
v: '2', v: '2',
type, type,
count: (participant?.count ?? 0).toString() count: participant!.count.toString()
}, },
content: encryptedContent content: encryptedContent
}) })
@@ -1636,7 +1635,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
throw new Boom('Missing media key for update', { statusCode: 400 }) throw new Boom('Missing media key for update', { statusCode: 400 })
} }
const meId = authState.creds.me?.id ?? '' const meId = authState.creds.me!.id
const node = encryptMediaRetryRequest(message.key, mediaKey, meId) const node = encryptMediaRetryRequest(message.key, mediaKey, meId)
let error: Error | undefined = undefined let error: Error | undefined = undefined
@@ -1713,7 +1712,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
options: MiscMessageGenerationOptions = {} options: MiscMessageGenerationOptions = {}
): Promise<AlbumSendResult> => { ): Promise<AlbumSendResult> => {
const startTime = Date.now() const startTime = Date.now()
const userJid = authState.creds.me?.id ?? '' const userJid = authState.creds.me!.id
const { const {
medias, medias,
@@ -1868,7 +1867,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
// Relay the message // Relay the message
await relayMessage(jid, mediaMsg.message, { await relayMessage(jid, mediaMsg.message, {
messageId: mediaMsg.key.id ?? '', messageId: mediaMsg.key.id!,
useCachedGroupMetadata: options.useCachedGroupMetadata useCachedGroupMetadata: options.useCachedGroupMetadata
}) })
@@ -1997,7 +1996,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
) )
} }
const userJid = authState.creds.me?.id ?? '' const userJid = authState.creds.me!.id
// Special path for carousel: call relayMessage directly with plain JS object // Special path for carousel: call relayMessage directly with plain JS object
// This matches Pastorini's working approach: // This matches Pastorini's working approach:
+7 -23
View File
@@ -609,12 +609,8 @@ export const makeSocket = (config: SocketConfig) => {
}, },
content: [{ tag: 'count', attrs: {} }] content: [{ tag: 'count', attrs: {} }]
}) })
const countChild = getBinaryNodeChild(result, 'count') const countChild = getBinaryNodeChild(result, 'count')!
if (!countChild) { return +countChild.attrs.value!
return 0
}
return +(countChild.attrs.value ?? 0)
} }
// Pre-key upload state management // Pre-key upload state management
@@ -1364,7 +1360,7 @@ export const makeSocket = (config: SocketConfig) => {
attrs: { attrs: {
to: S_WHATSAPP_NET, to: S_WHATSAPP_NET,
type: 'result', type: 'result',
id: stanza.attrs.id ?? '' id: stanza.attrs.id!
} }
} }
await sendNode(iq) await sendNode(iq)
@@ -1443,9 +1439,7 @@ export const makeSocket = (config: SocketConfig) => {
logger.info('opened connection to WA') logger.info('opened connection to WA')
clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
if (authState.creds.me) { ev.emit('creds.update', { me: { ...authState.creds.me!, lid: node.attrs.lid } })
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
}
ev.emit('connection.update', { connection: 'open' }) ev.emit('connection.update', { connection: 'open' })
@@ -1468,23 +1462,13 @@ export const makeSocket = (config: SocketConfig) => {
const myLID = node.attrs.lid const myLID = node.attrs.lid
process.nextTick(async () => { process.nextTick(async () => {
try { try {
const me = authState.creds.me const myPN = authState.creds.me!.id
if (!me) {
return
}
const myPN = me.id
// Store our own LID-PN mapping // Store our own LID-PN mapping
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }]) await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }])
// Create device list for our own user (needed for bulk migration) // Create device list for our own user (needed for bulk migration)
const decoded = jidDecode(myPN) const { user, device } = jidDecode(myPN)!
if (!decoded) {
return
}
const { user, device } = decoded
await authState.keys.set({ await authState.keys.set({
'device-list': { 'device-list': {
[user]: [device?.toString() || '0'] [user]: [device?.toString() || '0']
@@ -1572,7 +1556,7 @@ export const makeSocket = (config: SocketConfig) => {
logger.debug({ name }, 'updated pushName') logger.debug({ name }, 'updated pushName')
sendNode({ sendNode({
tag: 'presence', tag: 'presence',
attrs: { name: name ?? '' } attrs: { name: name! }
}).catch(err => { }).catch(err => {
logger.warn({ trace: err.stack }, 'error in sending presence update on name change') logger.warn({ trace: err.stack }, 'error in sending presence update on name change')
}) })