feat: implement message retry handling

so if a message fails to decrypt on the other user's end -- it can be retried
This commit is contained in:
Adhiraj Singh
2021-12-04 13:47:24 +05:30
parent 903871d180
commit 78fd72c8e5
6 changed files with 91 additions and 33 deletions

View File

@@ -6,6 +6,7 @@ import { proto } from "../../WAProto"
import { KEY_BUNDLE_TYPE } from "../Defaults"
import { makeChatsSocket } from "./chats"
import { extractGroupMetadata } from "./groups"
import { Boom } from "@hapi/boom"
const getStatusFromReceiptType = (type: string | undefined) => {
if(type === 'read' || type === 'read-self') {
@@ -24,6 +25,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
ev,
authState,
ws,
assertSession,
assertingPreKeys,
sendNode,
relayMessage,
@@ -464,30 +466,64 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
}
})
const sendMessagesAgain = async(key: proto.IMessageKey, ids: string[]) => {
const participant = key.participant || key.remoteJid
await assertSession(participant, true)
logger.debug({ key, ids }, 'recv retry request, forced new session')
const msgs = await Promise.all(
ids.map(id => (
config.getMessage({ ...key, id })
))
)
const missingMsgIdx = msgs.findIndex(m => !m)
if(missingMsgIdx >= 0) {
throw new Boom(
`recv request to retry message, but message "${ids[missingMsgIdx]}" not available`,
{ statusCode: 404, data: { key } }
)
}
for(let i = 0; i < msgs.length;i++) {
await relayMessage(key.remoteJid, msgs[i], {
messageId: ids[i],
participant
})
}
}
const handleReceipt = async(node: BinaryNode) => {
const { attrs, content } = node
const status = getStatusFromReceiptType(attrs.type)
const remoteJid = attrs.recipient || attrs.from
const fromMe = attrs.recipient ? false : true
const ids = [attrs.id]
if(Array.isArray(content)) {
const items = getBinaryNodeChildren(content[0], 'item')
ids.push(...items.map(i => i.attrs.id))
}
const key: proto.IMessageKey = {
remoteJid,
id: '',
fromMe,
participant: attrs.participant
}
const status = getStatusFromReceiptType(attrs.type)
if(typeof status !== 'undefined' && !areJidsSameUser(attrs.from, authState.creds.me?.id)) {
const ids = [attrs.id]
if(Array.isArray(content)) {
const items = getBinaryNodeChildren(content[0], 'item')
ids.push(...items.map(i => i.attrs.id))
}
const remoteJid = attrs.recipient || attrs.from
const fromMe = attrs.recipient ? false : true
ev.emit('messages.update', ids.map(id => ({
key: {
remoteJid,
id: id,
fromMe,
participant: attrs.participant
},
key: { ...key, id },
update: { status }
})))
}
if(attrs.type === 'retry') {
await sendMessagesAgain(key, ids)
}
await sendMessageAck(node, { class: 'receipt', type: attrs.type })
}

View File

@@ -238,8 +238,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const relayMessage = async(
jid: string,
message: proto.IMessage,
{ messageId: msgId, additionalAttributes, cachedGroupMetadata }: MessageRelayOptions
{ messageId: msgId, participant, additionalAttributes, cachedGroupMetadata }: MessageRelayOptions
) => {
const meId = authState.creds.me!.id
const { user, server } = jidDecode(jid)
const isGroup = server === 'g.us'
msgId = msgId || generateMessageID()
@@ -250,16 +252,23 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const destinationJid = jidEncode(user, isGroup ? 'g.us' : 's.whatsapp.net')
const devices: JidWithDevice[] = []
if(participant) {
const { user, device } = jidDecode(participant)
devices.push({ user, device })
}
if(isGroup) {
const { ciphertext, senderKeyDistributionMessageKey } = await encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, authState.creds.me!.id, authState)
const { ciphertext, senderKeyDistributionMessageKey } = await encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, meId, authState)
let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
if(!groupData) groupData = await groupMetadata(jid)
const participantsList = groupData.participants.map(p => p.id)
const devices = await getUSyncDevices(participantsList, false)
logger.debug(`got ${devices.length} additional devices`)
if(!participant) {
const participantsList = groupData.participants.map(p => p.id)
const devices = await getUSyncDevices(participantsList, false)
devices.push(...devices)
}
const encSenderKeyMsg = encodeWAMessage({
senderKeyDistributionMessage: {
@@ -304,7 +313,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
content: binaryNodeContent
}
} else {
const { user: meUser } = jidDecode(authState.creds.me!.id!)
const { user: meUser } = jidDecode(meId)
const messageToMyself: proto.IMessage = {
deviceSentMessage: {
@@ -314,15 +323,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
}
const encodedMeMsg = encodeWAMessage(messageToMyself)
participants.push(
await createParticipantNode(jidEncode(user, 's.whatsapp.net'), encodedMsg)
)
participants.push(
await createParticipantNode(jidEncode(meUser, 's.whatsapp.net'), encodedMeMsg)
)
const devices = await getUSyncDevices([ authState.creds.me!.id!, jid ], true)
logger.debug(`got ${devices.length} additional devices`)
if(!participant) {
devices.push({ user })
devices.push({ user: meUser })
const additionalDevices = await getUSyncDevices([ meId, jid ], true)
devices.push(...additionalDevices)
}
for(const { user, device } of devices) {
const isMe = user === meUser
@@ -363,7 +370,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
content: proto.ADVSignedDeviceIdentity.encode(authState.creds.account).finish()
})
}
logger.debug({ msgId }, 'sending message')
logger.debug({ msgId }, `sending message to ${devices.length} devices`)
await sendNode(stanza)