fix: cannot send message on new Whatsapp Business limited accounts (#2080)

* fix: cannot send message on new Whatsapp Business limited accounts

* chore: add woraround to resend the message on 475 errors

* fix: lint

* fix: add handler

* fix: lint

* Update src/Socket/messages-send.ts

---------

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
This commit is contained in:
Cassio Santos
2025-11-19 10:18:19 -03:00
committed by GitHub
parent 0145e4c710
commit 5c456e514e
7 changed files with 67 additions and 3 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ export const PROCESSABLE_HISTORY_TYPES = [
proto.HistorySync.HistorySyncType.FULL,
proto.HistorySync.HistorySyncType.ON_DEMAND,
proto.HistorySync.HistorySyncType.NON_BLOCKING_DATA,
proto.HistorySync.HistorySyncType.INITIAL_STATUS_V3,
proto.HistorySync.HistorySyncType.INITIAL_STATUS_V3
]
export const DEFAULT_CONNECTION_CONFIG: SocketConfig = {
+2 -1
View File
@@ -1037,7 +1037,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
const historyMsg = getHistoryMsg(msg.message!)
const shouldProcessHistoryMsg = historyMsg
? shouldSyncHistoryMessage(historyMsg) && PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType! as proto.HistorySync.HistorySyncType)
? shouldSyncHistoryMessage(historyMsg) &&
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType! as proto.HistorySync.HistorySyncType)
: false
// State machine: decide on sync and flush
+48
View File
@@ -872,6 +872,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
})
authState.creds.registered = true
ev.emit('creds.update', authState.creds)
break
case 'privacy_token':
await handlePrivacyTokenNotification(node)
break
}
if (Object.keys(result).length) {
@@ -879,6 +883,36 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
}
}
const handlePrivacyTokenNotification = async (node: BinaryNode) => {
const tokensNode = getBinaryNodeChild(node, 'tokens')
const from = jidNormalizedUser(node.attrs.from)
if (!tokensNode) return
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token')
for (const tokenNode of tokenNodes) {
const { attrs, content } = tokenNode
const type = attrs.type
const timestamp = attrs.t
if (type === 'trusted_contact' && content instanceof Buffer) {
logger.debug(
{
from,
timestamp,
tcToken: content
},
'received trusted contact token'
)
await authState.keys.set({
'contacts-tc-token': { [from]: { token: content, timestamp } }
})
}
}
}
async function decipherLinkPublicKey(data: Uint8Array | Buffer) {
const buffer = toRequiredBuffer(data)
const salt = buffer.slice(0, 32)
@@ -1369,6 +1403,20 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
}
}
])
// resend the message with device_fanout=false, use at your own risk
// if (attrs.error === '475') {
// const msg = await getMessage(key)
// if (msg) {
// await relayMessage(key.remoteJid!, msg, {
// messageId: key.id!,
// useUserDevicesCache: false,
// additionalAttributes: {
// device_fanout: 'false'
// }
// })
// }
// }
}
}
+13
View File
@@ -929,6 +929,19 @@ export const makeMessagesSocket = (config: SocketConfig) => {
logger.debug({ jid }, 'adding device identity')
}
const contactTcTokenData =
!isGroup && !isRetryResend && !isStatus ? await authState.keys.get('contacts-tc-token', [destinationJid]) : {}
const tcTokenBuffer = contactTcTokenData[destinationJid]?.token
if (tcTokenBuffer) {
(stanza.content as BinaryNode[]).push({
tag: 'tctoken',
attrs: {},
content: tcTokenBuffer
})
}
if (additionalNodes && additionalNodes.length > 0) {
;(stanza.content as BinaryNode[]).push(...additionalNodes)
}
+1
View File
@@ -80,6 +80,7 @@ export type SignalDataTypeMap = {
'app-state-sync-version': LTHashState
'lid-mapping': string
'device-list': string[]
'contacts-tc-token': { token: Buffer; timestamp?: string }
}
export type SignalDataSet = { [T in keyof SignalDataTypeMap]?: { [id: string]: SignalDataTypeMap[T] | null } }
+1 -1
View File
@@ -75,7 +75,7 @@ export function makeCacheableSignalKeyStore(
const item = fetched[id]
if (item) {
data[id] = item
cache.set(getUniqueId(type, id), item)
cache.set(getUniqueId(type, id), item as SignalDataTypeMap[keyof SignalDataTypeMap])
}
}
}
+1
View File
@@ -102,6 +102,7 @@ export const downloadAndProcessHistorySyncNotification = async (
} else {
historyMsg = await downloadHistory(msg, options)
}
return processHistoryMessage(historyMsg)
}