Compare commits

..

1 Commits

Author SHA1 Message Date
Matheus Filype 6afde71691 feat: groups mention all (#2396)
* fix: improve message resend logic by adding checks for message IDs

* Revert "fix: improve message resend logic by adding checks for message IDs"

This reverts commit c03f9d8e6fc6cbfbb9d1f8f67c169700e704213d.

* feat: add mentionAll support to message context for group mentions

* fix: improve readability of condition for mentions and mentionAll in generateWAMessageContent

* Apply suggestions from code review

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

---------

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
2026-03-13 19:49:16 +02:00
6 changed files with 20 additions and 54 deletions
+3 -12
View File
@@ -36,7 +36,7 @@ import {
signedKeyPair, signedKeyPair,
xmppSignedPreKey xmppSignedPreKey
} from '../Utils' } from '../Utils'
import { getPlatformId, isAndroidBrowser } from '../Utils/browser-utils' import { getPlatformId } from '../Utils/browser-utils'
import { import {
assertNodeErrorFree, assertNodeErrorFree,
type BinaryNode, type BinaryNode,
@@ -758,15 +758,6 @@ export const makeSocket = (config: SocketConfig) => {
id: jidEncode(phoneNumber, 's.whatsapp.net'), id: jidEncode(phoneNumber, 's.whatsapp.net'),
name: '~' name: '~'
} }
// Pair code companion_platform_id must be Chrome (1) when using Android
// browser preset. ANDROID_PHONE (16) causes silent timeout, UWP (21)
// causes rejection. Only Chrome (1) works for pair code via web protocol.
// The device still appears as "Android" in linked devices because
// DeviceProps.platformType=ANDROID_PHONE is set in the registration node.
const isAndroid = isAndroidBrowser(browser)
const pairPlatformId = isAndroid ? getPlatformId('Chrome') : getPlatformId(browser[1])
const pairPlatformDisplay = isAndroid ? 'Chrome (Mac OS)' : `${browser[1]} (${browser[0]})`
ev.emit('creds.update', authState.creds) ev.emit('creds.update', authState.creds)
await sendNode({ await sendNode({
tag: 'iq', tag: 'iq',
@@ -799,12 +790,12 @@ export const makeSocket = (config: SocketConfig) => {
{ {
tag: 'companion_platform_id', tag: 'companion_platform_id',
attrs: {}, attrs: {},
content: pairPlatformId content: getPlatformId(browser[1])
}, },
{ {
tag: 'companion_platform_display', tag: 'companion_platform_display',
attrs: {}, attrs: {},
content: pairPlatformDisplay content: `${browser[1]} (${browser[0]})`
}, },
{ {
tag: 'link_code_pairing_nonce', tag: 'link_code_pairing_nonce',
+2
View File
@@ -113,6 +113,8 @@ export interface WAUrlInfo {
type Mentionable = { type Mentionable = {
/** list of jids that are mentioned in the accompanying text */ /** list of jids that are mentioned in the accompanying text */
mentions?: string[] mentions?: string[]
/** mention all */
mentionAll?: boolean
} }
type Contextable = { type Contextable = {
/** add contextInfo to the message */ /** add contextInfo to the message */
-1
View File
@@ -22,7 +22,6 @@ export type BrowsersMap = {
baileys(browser: string): [string, string, string] baileys(browser: string): [string, string, string]
windows(browser: string): [string, string, string] windows(browser: string): [string, string, string]
appropriate(browser: string): [string, string, string] appropriate(browser: string): [string, string, string]
android(apiLevel: string): [string, string, string]
} }
export enum DisconnectReason { export enum DisconnectReason {
+2 -25
View File
@@ -22,33 +22,10 @@ export const Browsers: BrowsersMap = {
baileys: browser => ['Baileys', browser, '6.5.0'], baileys: browser => ['Baileys', browser, '6.5.0'],
windows: browser => ['Windows', browser, '10.0.22631'], windows: browser => ['Windows', browser, '10.0.22631'],
/** The appropriate browser based on your OS & release */ /** The appropriate browser based on your OS & release */
appropriate: browser => [PLATFORM_MAP[platform()] || 'Ubuntu', browser, release()], appropriate: browser => [PLATFORM_MAP[platform()] || 'Ubuntu', browser, release()]
/** Android companion device. apiLevel is the Android API level (e.g. '14') */
android: (apiLevel: string) => [apiLevel, 'Android', '']
}
/**
* Checks if the browser tuple represents an Android companion device.
* @param browser - Browser tuple [os, platform, version]
* @returns True if platform is 'Android' (case-insensitive)
*/
export const isAndroidBrowser = (browser: [string, string, string]): boolean => {
return browser[1]?.toUpperCase() === 'ANDROID'
} }
export const getPlatformId = (browser: string) => { export const getPlatformId = (browser: string) => {
const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase() as any] const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase() as any]
if (platformType !== undefined) { return platformType ? platformType.toString() : '1' //chrome
return platformType.toString()
}
// 'ANDROID' is not in the PlatformType enum — map to ANDROID_PHONE
if (browser.toUpperCase() === 'ANDROID') {
const androidPhone = proto.DeviceProps.PlatformType['ANDROID_PHONE' as any]
if (androidPhone !== undefined) {
return androidPhone.toString()
}
}
return '1' // Chrome
} }
+12 -6
View File
@@ -609,14 +609,20 @@ export const generateWAMessageContent = async (
m = { viewOnceMessage: { message: m } } m = { viewOnceMessage: { message: m } }
} }
if (hasOptionalProperty(message, 'mentions') && message.mentions?.length) { if (
(hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)
) {
const messageType = Object.keys(m)[0]! as Extract<keyof proto.IMessage, MessageWithContextInfo> const messageType = Object.keys(m)[0]! as Extract<keyof proto.IMessage, MessageWithContextInfo>
const key = m[messageType] const key = m[messageType]
if ('contextInfo' in key! && !!key.contextInfo) { if (key && 'contextInfo' in key) {
key.contextInfo.mentionedJid = message.mentions key.contextInfo = key.contextInfo || {}
} else if (key!) { if (message.mentions?.length) {
key.contextInfo = { key.contextInfo.mentionedJid = message.mentions
mentionedJid: message.mentions }
if (message.mentionAll) {
key.contextInfo.nonJidMentions = 1
} }
} }
} }
+1 -10
View File
@@ -14,17 +14,13 @@ import { encodeBigEndian } from './generics'
import { createSignalIdentity } from './signal' import { createSignalIdentity } from './signal'
const getUserAgent = (config: SocketConfig): proto.ClientPayload.IUserAgent => { const getUserAgent = (config: SocketConfig): proto.ClientPayload.IUserAgent => {
// Always use MACOS platform for UserAgent — we connect via web protocol
// (WA\x06\x03) so the server expects a web-compatible identity.
// Using WEB causes 405 errors; using SMB_ANDROID breaks pair code.
// Android identity is only set in DeviceProps (registration node).
return { return {
appVersion: { appVersion: {
primary: config.version[0], primary: config.version[0],
secondary: config.version[1], secondary: config.version[1],
tertiary: config.version[2] tertiary: config.version[2]
}, },
platform: proto.ClientPayload.UserAgent.Platform.MACOS, platform: proto.ClientPayload.UserAgent.Platform.WEB,
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE, releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
osVersion: '0.1', osVersion: '0.1',
device: 'Desktop', device: 'Desktop',
@@ -83,11 +79,6 @@ export const generateLoginNode = (userJid: string, config: SocketConfig): proto.
const getPlatformType = (platform: string): proto.DeviceProps.PlatformType => { const getPlatformType = (platform: string): proto.DeviceProps.PlatformType => {
const platformType = platform.toUpperCase() const platformType = platform.toUpperCase()
// 'ANDROID' is not in PlatformType enum — map to ANDROID_PHONE
if (platformType === 'ANDROID') {
return proto.DeviceProps.PlatformType.ANDROID_PHONE
}
return ( return (
proto.DeviceProps.PlatformType[platformType as keyof typeof proto.DeviceProps.PlatformType] || proto.DeviceProps.PlatformType[platformType as keyof typeof proto.DeviceProps.PlatformType] ||
proto.DeviceProps.PlatformType.CHROME proto.DeviceProps.PlatformType.CHROME