refactor(retry): improve message encryption and retry logic (#2055)

This commit is contained in:
vini
2025-11-19 10:13:51 -03:00
committed by GitHub
parent 9b012baa5c
commit b4863b624e
4 changed files with 66 additions and 50 deletions
+1 -1
View File
@@ -1200,7 +1200,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
await processingMutex.mutex(async () => {
await decrypt()
// message failed to decrypt
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
return sendMessageAck(node, NACK_REASONS.ParsingError)
}
+53 -47
View File
@@ -572,6 +572,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const isStatus = jid === statusJid
const isLid = server === 'lid'
const isNewsletter = server === 'newsletter'
const isGroupOrStatus = isGroup || isStatus
const finalJid = jid
msgId = msgId || generateMessageIDV2(meId)
@@ -639,7 +640,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
extraAttrs['decrypt-fail'] = 'hide' // todo: expand for reactions and other types
}
if (isGroup || isStatus) {
if (isGroupOrStatus && !isRetryResend) {
const [groupData, senderKeyMap] = await Promise.all([
(async () => {
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
@@ -663,28 +664,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
})()
])
if (!participant) {
const participantsList = []
if (isStatus) {
if (statusJidList?.length) participantsList.push(...statusJidList)
} else {
// default to LID based groups
let groupAddressingMode = 'lid'
if (groupData) {
participantsList.push(...groupData.participants.map(p => p.id))
groupAddressingMode = groupData?.addressingMode || groupAddressingMode
}
// default to lid addressing mode in a group
additionalAttributes = {
...additionalAttributes,
addressing_mode: groupAddressingMode
}
}
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
devices.push(...additionalDevices)
}
const participantsList = groupData ? groupData.participants.map(p => p.id) : []
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
additionalAttributes = {
@@ -693,6 +673,20 @@ export const makeMessagesSocket = (config: SocketConfig) => {
}
}
if (isStatus && statusJidList) {
participantsList.push(...statusJidList)
}
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
devices.push(...additionalDevices)
if (isGroup) {
additionalAttributes = {
...additionalAttributes,
addressing_mode: groupData?.addressingMode || 'lid'
}
}
const patched = await patchMessageBeforeSending(message)
if (Array.isArray(patched)) {
throw new Boom('Per-jid patching is not supported in groups')
@@ -744,30 +738,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
participants.push(...result.nodes)
}
if (isRetryResend) {
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
data: bytes,
jid: participant?.jid!
})
binaryNodeContent.push({
tag: 'enc',
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
content: ciphertext
})
binaryNodeContent.push({
tag: 'enc',
attrs: {
v: '2',
type,
count: participant!.count.toString()
},
content: encryptedContent
})
} else {
binaryNodeContent.push({
tag: 'enc',
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
content: ciphertext
})
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
}
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
} else {
// ADDRESSING CONSISTENCY: Match own identity to conversation context
// TODO: investigate if this is true
@@ -781,7 +758,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const { user: ownUser } = jidDecode(ownId)!
if (!participant) {
if (!isRetryResend) {
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net'
devices.push({
user,
@@ -869,6 +846,35 @@ export const makeMessagesSocket = (config: SocketConfig) => {
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
}
if (isRetryResend) {
const isParticipantLid = isLidUser(participant!.jid)
const isMe = areJidsSameUser(participant!.jid, isParticipantLid ? meLid : meId)
const encodedMessageToSend = isMe
? encodeWAMessage({
deviceSentMessage: {
destinationJid,
message
}
})
: encodeWAMessage(message)
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
data: encodedMessageToSend,
jid: participant!.jid
})
binaryNodeContent.push({
tag: 'enc',
attrs: {
v: '2',
type,
count: participant!.count.toString()
},
content: encryptedContent
})
}
if (participants.length) {
if (additionalAttributes?.['category'] === 'peer') {
const peerNode = participants[0]?.content?.[0] as BinaryNode
+6 -1
View File
@@ -8,7 +8,12 @@ import type { CacheStore } from './Socket'
// export the WAMessage Prototypes
export { proto as WAProto }
export type WAMessage = proto.IWebMessageInfo & { key: WAMessageKey; messageStubParameters?: any }
export type WAMessage = proto.IWebMessageInfo & {
key: WAMessageKey
messageStubParameters?: any
category?: string
retryCount?: number
}
export type WAMessageContent = proto.IMessage
export type WAContactMessage = proto.Message.IContactMessage
export type WAContactsArrayMessage = proto.Message.IContactsArrayMessage
+6 -1
View File
@@ -206,6 +206,7 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
const fullMessage: WAMessage = {
key,
category: stanza.attrs.category,
messageTimestamp: +stanza.attrs.t!,
pushName: pushname,
broadcast: isJidBroadcast(from)
@@ -248,6 +249,10 @@ export const decryptMessageNode = (
fullMessage.key.isViewOnce = true // TODO: remove from here and add a STUB TYPE
}
if (attrs.count && tag === 'enc') {
fullMessage.retryCount = Number(attrs.count)
}
if (tag !== 'enc' && tag !== 'plaintext') {
continue
}
@@ -333,7 +338,7 @@ export const decryptMessageNode = (
}
// if nothing was found to decrypt
if (!decryptables) {
if (!decryptables && !fullMessage.key?.isViewOnce) {
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
}