fix: revert remaining ?? '' patterns across Utils, WABinary, Signal, and WAUSync files
Completes the comprehensive revert of defensive null coalescing patterns introduced in PRs 124-129. These patterns (e.g. `value ?? ''`) silently produced empty strings instead of failing fast, causing subtle bugs like malformed JIDs and broken Signal session lookups. Files: libsignal.ts, messages-recv.ts, chat-utils.ts, generics.ts, history.ts, messages-media.ts, messages.ts, process-message.ts, validate-connection.ts, jid-utils.ts, UsyncBotProfileProtocol.ts https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB
This commit is contained in:
@@ -708,7 +708,7 @@ function signalStorage(
|
||||
|
||||
if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID) return id
|
||||
|
||||
const pnJid = `${user ?? ''}${device !== '0' ? `:${device}` : ''}@${domainType === WAJIDDomains.HOSTED ? 'hosted' : 's.whatsapp.net'}`
|
||||
const pnJid = `${user!}${device !== '0' ? `:${device}` : ''}@${domainType === WAJIDDomains.HOSTED ? 'hosted' : 's.whatsapp.net'}`
|
||||
|
||||
const lidForPN = await lidMapping.getLIDForPN(pnJid)
|
||||
if (lidForPN) {
|
||||
|
||||
@@ -324,7 +324,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
fromMe: false // TODO: is this really true though
|
||||
},
|
||||
message: messageProto,
|
||||
messageTimestamp: +(child.attrs.t ?? 0)
|
||||
messageTimestamp: +child.attrs.t!
|
||||
}).toJSON() as WAMessage
|
||||
await upsertMessage(fullMessage, 'append')
|
||||
logger.info('Processed plaintext newsletter message')
|
||||
@@ -601,8 +601,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
const actingParticipantLid = fullNode.attrs.participant
|
||||
const actingParticipantPn = fullNode.attrs.participant_pn
|
||||
|
||||
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid || ''
|
||||
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn || ''
|
||||
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid!
|
||||
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn!
|
||||
|
||||
switch (child?.tag) {
|
||||
case 'create':
|
||||
@@ -663,8 +663,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
participants.length === 1 &&
|
||||
// if recv. "remove" message and sender removed themselves
|
||||
// mark as left
|
||||
(areJidsSameUser(participants[0]?.id, actingParticipantLid) ||
|
||||
areJidsSameUser(participants[0]?.id, actingParticipantPn)) &&
|
||||
(areJidsSameUser(participants[0]!.id, actingParticipantLid) ||
|
||||
areJidsSameUser(participants[0]!.id, actingParticipantPn)) &&
|
||||
child.tag === 'remove'
|
||||
) {
|
||||
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE
|
||||
@@ -805,9 +805,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
break
|
||||
case 'account_sync':
|
||||
if (child?.tag === 'disappearing_mode') {
|
||||
const newDuration = +(child.attrs.duration ?? '0')
|
||||
const timestamp = +(child.attrs.t ?? '0')
|
||||
if (child!.tag === 'disappearing_mode') {
|
||||
const newDuration = +child!.attrs.duration!
|
||||
const timestamp = +child!.attrs.t!
|
||||
|
||||
logger.info({ newDuration }, 'updated account disappearing mode')
|
||||
|
||||
@@ -820,7 +820,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (child?.tag === 'blocklist') {
|
||||
} else if (child!.tag === 'blocklist') {
|
||||
const blocklists = getBinaryNodeChildren(child, 'item')
|
||||
|
||||
for (const { attrs } of blocklists) {
|
||||
|
||||
@@ -470,7 +470,7 @@ export const decodeSyncdSnapshot = async (
|
||||
areMutationsRequired
|
||||
? mutation => {
|
||||
const index = mutation.syncAction.index?.toString()
|
||||
mutationMap[index ?? ''] = mutation
|
||||
mutationMap[index!] = mutation
|
||||
}
|
||||
: () => {},
|
||||
validateMacs
|
||||
@@ -558,7 +558,7 @@ export const decodePatches = async (
|
||||
shouldMutate
|
||||
? mutation => {
|
||||
const index = mutation.syncAction.index?.toString()
|
||||
mutationMap[index ?? ''] = mutation
|
||||
mutationMap[index!] = mutation
|
||||
}
|
||||
: () => {},
|
||||
true
|
||||
@@ -689,7 +689,7 @@ export const chatModificationToAppPatch = (mod: ChatModification, jid: string) =
|
||||
messageTimestamp: timestamp
|
||||
}
|
||||
},
|
||||
index: ['deleteMessageForMe', jid, key.id ?? '', key.fromMe ? '1' : '0', '0'],
|
||||
index: ['deleteMessageForMe', jid, key.id!, key.fromMe ? '1' : '0', '0'],
|
||||
type: 'regular_high',
|
||||
apiVersion: 3,
|
||||
operation: OP.SET
|
||||
@@ -896,7 +896,7 @@ export const processSyncAction = (
|
||||
{
|
||||
id,
|
||||
muteEndTime: action.muteAction?.muted ? toNumber(action.muteAction.muteEndTimestamp) : null,
|
||||
conditional: getChatUpdateConditional(id ?? '', undefined)
|
||||
conditional: getChatUpdateConditional(id!, undefined)
|
||||
}
|
||||
])
|
||||
} else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
|
||||
@@ -925,7 +925,7 @@ export const processSyncAction = (
|
||||
{
|
||||
id,
|
||||
archived: isArchived,
|
||||
conditional: getChatUpdateConditional(id ?? '', msgRange)
|
||||
conditional: getChatUpdateConditional(id!, msgRange)
|
||||
}
|
||||
])
|
||||
} else if (action?.markChatAsReadAction) {
|
||||
@@ -939,7 +939,7 @@ export const processSyncAction = (
|
||||
{
|
||||
id,
|
||||
unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
|
||||
conditional: getChatUpdateConditional(id ?? '', markReadAction?.messageRange)
|
||||
conditional: getChatUpdateConditional(id!, markReadAction?.messageRange)
|
||||
}
|
||||
])
|
||||
} else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
|
||||
@@ -965,7 +965,7 @@ export const processSyncAction = (
|
||||
{
|
||||
id,
|
||||
pinned: action.pinAction?.pinned ? toNumber(action.timestamp) : null,
|
||||
conditional: getChatUpdateConditional(id ?? '', undefined)
|
||||
conditional: getChatUpdateConditional(id!, undefined)
|
||||
}
|
||||
])
|
||||
} else if (action?.unarchiveChatsSetting) {
|
||||
@@ -990,7 +990,7 @@ export const processSyncAction = (
|
||||
])
|
||||
} else if (action?.deleteChatAction || type === 'deleteChat') {
|
||||
if (!isInitialSync) {
|
||||
ev.emit('chats.delete', [id ?? ''])
|
||||
ev.emit('chats.delete', [id!])
|
||||
}
|
||||
} else if (action?.labelEditAction) {
|
||||
const { name, color, deleted, predefinedId } = action.labelEditAction
|
||||
|
||||
@@ -343,7 +343,7 @@ const STATUS_MAP: { [_: string]: proto.WebMessageInfo.Status } = {
|
||||
* @param type type from receipt
|
||||
*/
|
||||
export const getStatusFromReceiptType = (type: string | undefined) => {
|
||||
const status = STATUS_MAP[type ?? '']
|
||||
const status = STATUS_MAP[type!]
|
||||
if (typeof type === 'undefined') {
|
||||
return proto.WebMessageInfo.Status.DELIVERY_ACK
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
|
||||
case proto.HistorySync.HistorySyncType.FULL:
|
||||
case proto.HistorySync.HistorySyncType.ON_DEMAND:
|
||||
for (const chat of (item.conversations ?? []) as Chat[]) {
|
||||
const chatId = chat.id ?? ''
|
||||
const chatId = chat.id!
|
||||
|
||||
// Source 2: Extract LID-PN mapping from conversation object
|
||||
// This handles cases where the mapping isn't in phoneNumberToLidMappings
|
||||
@@ -371,7 +371,7 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
|
||||
break
|
||||
case proto.HistorySync.HistorySyncType.PUSH_NAME:
|
||||
for (const c of (item.pushnames ?? [])) {
|
||||
contacts.push({ id: c.id ?? '', notify: c.pushname ?? '' })
|
||||
contacts.push({ id: c.id!, notify: c.pushname! })
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
@@ -317,7 +317,7 @@ export const getStream = async (item: WAMediaUpload, opts?: RequestInit & { maxC
|
||||
const urlStr = item.url.toString()
|
||||
|
||||
if (urlStr.startsWith('data:')) {
|
||||
const buffer = Buffer.from(urlStr.split(',')[1] ?? '', 'base64')
|
||||
const buffer = Buffer.from(urlStr.split(',')[1]!, 'base64')
|
||||
return { stream: toReadable(buffer), type: 'buffer' } as const
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ export const downloadContentFromMessage = async (
|
||||
opts: MediaDownloadOptions = {}
|
||||
) => {
|
||||
const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/')
|
||||
const downloadUrl = isValidMediaUrl ? url : getUrlFromDirectPath(directPath ?? '')
|
||||
const downloadUrl = isValidMediaUrl ? url : getUrlFromDirectPath(directPath!)
|
||||
if (!downloadUrl) {
|
||||
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 })
|
||||
}
|
||||
@@ -656,7 +656,7 @@ export function extensionForMediaMessage(message: WAMessageContent) {
|
||||
extension = '.jpeg'
|
||||
} else {
|
||||
const messageContent = message[type] as WAGenericMediaMessage
|
||||
extension = getExtension(messageContent.mimetype ?? '') ?? ''
|
||||
extension = getExtension(messageContent.mimetype!)!
|
||||
}
|
||||
|
||||
return extension
|
||||
@@ -864,8 +864,8 @@ export const getWAUploadToServer = (
|
||||
|
||||
if (result?.url || result?.direct_path) {
|
||||
urls = {
|
||||
mediaUrl: result.url ?? '',
|
||||
directPath: result.direct_path ?? '',
|
||||
mediaUrl: result.url!,
|
||||
directPath: result.direct_path!,
|
||||
meta_hmac: result.meta_hmac,
|
||||
fbid: result.fbid,
|
||||
ts: result.ts
|
||||
|
||||
@@ -1770,7 +1770,7 @@ export const generateWAMessageFromContent = (
|
||||
const innerContent = innerMessage[key as Exclude<keyof proto.IMessage, 'conversation'>]
|
||||
const contextInfo: proto.IContextInfo =
|
||||
(innerContent && typeof innerContent === 'object' && 'contextInfo' in innerContent && (innerContent as Record<string, unknown>).contextInfo as proto.IContextInfo) || {}
|
||||
contextInfo.participant = jidNormalizedUser(participant ?? '')
|
||||
contextInfo.participant = jidNormalizedUser(participant!)
|
||||
contextInfo.stanzaId = quoted.key.id
|
||||
contextInfo.quotedMessage = quotedMsg
|
||||
|
||||
|
||||
@@ -59,24 +59,22 @@ const REAL_MSG_REQ_ME_STUB_TYPES = new Set([WAMessageStubType.GROUP_PARTICIPANT_
|
||||
/** Cleans a received message to further processing */
|
||||
export const cleanMessage = (message: WAMessage, meId: string, meLid: string) => {
|
||||
// ensure remoteJid and participant doesn't have device or agent in it
|
||||
const remoteJid = message.key.remoteJid ?? ''
|
||||
if (isHostedPnUser(remoteJid) || isHostedLidUser(remoteJid)) {
|
||||
if (isHostedPnUser(message.key.remoteJid!) || isHostedLidUser(message.key.remoteJid!)) {
|
||||
message.key.remoteJid = jidEncode(
|
||||
jidDecode(remoteJid)?.user ?? '',
|
||||
isHostedPnUser(remoteJid) ? 's.whatsapp.net' : 'lid'
|
||||
jidDecode(message.key?.remoteJid!)?.user!,
|
||||
isHostedPnUser(message.key.remoteJid!) ? 's.whatsapp.net' : 'lid'
|
||||
)
|
||||
} else {
|
||||
message.key.remoteJid = jidNormalizedUser(remoteJid)
|
||||
message.key.remoteJid = jidNormalizedUser(message.key.remoteJid!)
|
||||
}
|
||||
|
||||
const participantJid = message.key.participant ?? ''
|
||||
if (isHostedPnUser(participantJid) || isHostedLidUser(participantJid)) {
|
||||
if (isHostedPnUser(message.key.participant!) || isHostedLidUser(message.key.participant!)) {
|
||||
message.key.participant = jidEncode(
|
||||
jidDecode(participantJid)?.user ?? '',
|
||||
isHostedPnUser(participantJid) ? 's.whatsapp.net' : 'lid'
|
||||
jidDecode(message.key.participant!)?.user!,
|
||||
isHostedPnUser(message.key.participant!) ? 's.whatsapp.net' : 'lid'
|
||||
)
|
||||
} else {
|
||||
message.key.participant = jidNormalizedUser(participantJid)
|
||||
message.key.participant = jidNormalizedUser(message.key.participant!)
|
||||
}
|
||||
|
||||
const content = normalizeMessageContent(message.message)
|
||||
@@ -102,8 +100,8 @@ export const cleanMessage = (message: WAMessage, meId: string, meLid: string) =>
|
||||
// if the sender believed the message being reacted to is not from them
|
||||
// we've to correct the key to be from them, or some other participant
|
||||
msgKey.fromMe = !msgKey.fromMe
|
||||
? areJidsSameUser(msgKey.participant || (msgKey.remoteJid ?? ''), meId) ||
|
||||
areJidsSameUser(msgKey.participant || (msgKey.remoteJid ?? ''), meLid)
|
||||
? areJidsSameUser(msgKey.participant || (msgKey.remoteJid!), meId) ||
|
||||
areJidsSameUser(msgKey.participant || (msgKey.remoteJid!), meLid)
|
||||
: // if the message being reacted to, was from them
|
||||
// fromMe automatically becomes false
|
||||
false
|
||||
@@ -177,12 +175,11 @@ export const shouldIncrementChatUnread = (message: WAMessage) => !message.key.fr
|
||||
* Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
|
||||
*/
|
||||
export const getChatId = ({ remoteJid, participant, fromMe }: WAMessageKey) => {
|
||||
const jid = remoteJid ?? ''
|
||||
if (isJidBroadcast(jid) && !isJidStatusBroadcast(jid) && !fromMe) {
|
||||
return participant ?? ''
|
||||
if (isJidBroadcast(remoteJid!) && !isJidStatusBroadcast(remoteJid!) && !fromMe) {
|
||||
return participant!
|
||||
}
|
||||
|
||||
return jid
|
||||
return remoteJid!
|
||||
}
|
||||
|
||||
type PollContext = {
|
||||
@@ -478,7 +475,7 @@ const processMessage = async (
|
||||
ev.emit('messages.upsert', {
|
||||
messages: [webMessageInfo as WAMessage],
|
||||
type: 'notify',
|
||||
requestId: response.stanzaId ?? ''
|
||||
requestId: response.stanzaId!
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -517,10 +514,10 @@ const processMessage = async (
|
||||
const labelAssociationMsg = protocolMsg.memberLabel
|
||||
if (labelAssociationMsg?.label) {
|
||||
ev.emit('group.member-tag.update', {
|
||||
groupId: chat.id ?? '',
|
||||
groupId: chat.id!,
|
||||
label: labelAssociationMsg.label,
|
||||
participant: message.key.participant ?? '',
|
||||
participantAlt: message.key.participantAlt ?? '',
|
||||
participant: message.key.participant!,
|
||||
participantAlt: message.key.participantAlt!,
|
||||
messageTimestamp: Number(message.messageTimestamp)
|
||||
})
|
||||
}
|
||||
@@ -578,7 +575,7 @@ const processMessage = async (
|
||||
const meIdNormalised = jidNormalizedUser(meId)
|
||||
|
||||
// all jids need to be PN
|
||||
const eventCreatorKey = creationMsgKey.participant || (creationMsgKey.remoteJid ?? '')
|
||||
const eventCreatorKey = creationMsgKey.participant || (creationMsgKey.remoteJid!)
|
||||
const eventCreatorPn = isLidUser(eventCreatorKey)
|
||||
? await signalRepository.lidMapping.getPNForLID(eventCreatorKey)
|
||||
: eventCreatorKey
|
||||
@@ -600,7 +597,7 @@ const processMessage = async (
|
||||
const responseMsg = decryptEventResponse(encEventResponse, {
|
||||
eventEncKey,
|
||||
eventCreatorJid,
|
||||
eventMsgId: creationMsgKey.id ?? '',
|
||||
eventMsgId: creationMsgKey.id!,
|
||||
responderJid
|
||||
})
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ export const configureSuccessfulPairing = (
|
||||
content: [
|
||||
{
|
||||
tag: 'device-identity',
|
||||
attrs: { 'key-index': String(deviceIdentity.keyIndex ?? '') },
|
||||
attrs: { 'key-index': deviceIdentity.keyIndex!.toString() },
|
||||
content: accountEnc
|
||||
}
|
||||
]
|
||||
|
||||
@@ -63,7 +63,7 @@ export const jidDecode = (jid: string | undefined): FullJid | undefined => {
|
||||
const userCombined = jid.slice(0, sepIdx)
|
||||
|
||||
const [userAgent, device] = userCombined.split(':')
|
||||
const [user, agent] = (userAgent ?? '').split('_')
|
||||
const [user, agent] = userAgent!.split('_')
|
||||
|
||||
let domainType = WAJIDDomains.WHATSAPP
|
||||
if (server === 'lid') {
|
||||
@@ -78,7 +78,7 @@ export const jidDecode = (jid: string | undefined): FullJid | undefined => {
|
||||
|
||||
return {
|
||||
server: server as JidServer,
|
||||
user: user ?? '',
|
||||
user: user!,
|
||||
domainType,
|
||||
device: device ? +device : undefined
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export const isAnyPnUser = (jid: string | undefined) =>
|
||||
|
||||
const botRegexp = /^1313555\d{4}$|^131655500\d{2}$/
|
||||
|
||||
export const isJidBot = (jid: string | undefined) => jid && botRegexp.test(jid.split('@')[0] ?? '') && jid.endsWith('@c.us')
|
||||
export const isJidBot = (jid: string | undefined) => jid && botRegexp.test(jid.split('@')[0]!) && jid.endsWith('@c.us')
|
||||
|
||||
export const jidNormalizedUser = (jid: string | undefined) => {
|
||||
const result = jidDecode(jid)
|
||||
|
||||
@@ -35,7 +35,7 @@ export class USyncBotProfileProtocol implements USyncQueryProtocol {
|
||||
return {
|
||||
tag: 'bot',
|
||||
attrs: {},
|
||||
content: [{ tag: 'profile', attrs: { persona_id: user.personaId ?? '' } }]
|
||||
content: [{ tag: 'profile', attrs: { persona_id: user.personaId! } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,24 +51,24 @@ export class USyncBotProfileProtocol implements USyncQueryProtocol {
|
||||
|
||||
for (const command of getBinaryNodeChildren(commandsNode, 'command')) {
|
||||
commands.push({
|
||||
name: getBinaryNodeChildString(command, 'name') ?? '',
|
||||
description: getBinaryNodeChildString(command, 'description') ?? ''
|
||||
name: getBinaryNodeChildString(command, 'name')!,
|
||||
description: getBinaryNodeChildString(command, 'description')!
|
||||
})
|
||||
}
|
||||
|
||||
for (const prompt of getBinaryNodeChildren(promptsNode, 'prompt')) {
|
||||
prompts.push(`${getBinaryNodeChildString(prompt, 'emoji') ?? ''} ${getBinaryNodeChildString(prompt, 'text') ?? ''}`)
|
||||
prompts.push(`${getBinaryNodeChildString(prompt, 'emoji')!} ${getBinaryNodeChildString(prompt, 'text')!}`)
|
||||
}
|
||||
|
||||
return {
|
||||
isDefault: !!getBinaryNodeChild(profile, 'default'),
|
||||
jid: node.attrs.jid ?? '',
|
||||
name: getBinaryNodeChildString(profile, 'name') ?? '',
|
||||
attributes: getBinaryNodeChildString(profile, 'attributes') ?? '',
|
||||
description: getBinaryNodeChildString(profile, 'description') ?? '',
|
||||
category: getBinaryNodeChildString(profile, 'category') ?? '',
|
||||
personaId: profile?.attrs['persona_id'] ?? '',
|
||||
commandsDescription: getBinaryNodeChildString(commandsNode, 'description') ?? '',
|
||||
jid: node.attrs.jid!,
|
||||
name: getBinaryNodeChildString(profile, 'name')!,
|
||||
attributes: getBinaryNodeChildString(profile, 'attributes')!,
|
||||
description: getBinaryNodeChildString(profile, 'description')!,
|
||||
category: getBinaryNodeChildString(profile, 'category')!,
|
||||
personaId: profile!.attrs['persona_id']!,
|
||||
commandsDescription: getBinaryNodeChildString(commandsNode, 'description')!,
|
||||
commands,
|
||||
prompts
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user