feat: send unified session (#2294)

* 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 unified session handling and time constants

* refactor: improve socket variable destructuring and presence update logic

* fix: remove unnecessary semicolons in socket and time constants definitions

* fix: handle invalid server time offset parsing in makeSocket function
This commit is contained in:
Matheus Filype
2026-01-24 20:51:47 -03:00
committed by GitHub
parent f829b6d7a8
commit d514764686
3 changed files with 82 additions and 3 deletions
+7
View File
@@ -137,3 +137,10 @@ export const DEFAULT_CACHE_TTLS = {
CALL_OFFER: 5 * 60, // 5 minutes CALL_OFFER: 5 * 60, // 5 minutes
USER_DEVICES: 5 * 60 // 5 minutes USER_DEVICES: 5 * 60 // 5 minutes
} }
export const TimeMs = {
Minute: 60 * 1000,
Hour: 60 * 60 * 1000,
Day: 24 * 60 * 60 * 1000,
Week: 7 * 24 * 60 * 60 * 1000
}
+18 -3
View File
@@ -68,7 +68,17 @@ export const makeChatsSocket = (config: SocketConfig) => {
getMessage getMessage
} = config } = config
const sock = makeSocket(config) const sock = makeSocket(config)
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError } = sock const {
ev,
ws,
authState,
generateMessageTag,
sendNode,
query,
signalRepository,
onUnexpectedError,
sendUnifiedSession
} = sock
let privacySettings: { [_: string]: string } | undefined let privacySettings: { [_: string]: string } | undefined
@@ -659,13 +669,18 @@ export const makeChatsSocket = (config: SocketConfig) => {
const sendPresenceUpdate = async (type: WAPresence, toJid?: string) => { const sendPresenceUpdate = async (type: WAPresence, toJid?: string) => {
const me = authState.creds.me! const me = authState.creds.me!
if (type === 'available' || type === 'unavailable') { const isAvailableType = type === 'available'
if (isAvailableType || type === 'unavailable') {
if (!me.name) { if (!me.name) {
logger.warn('no name present, ignoring presence update request...') logger.warn('no name present, ignoring presence update request...')
return return
} }
ev.emit('connection.update', { isOnline: type === 'available' }) ev.emit('connection.update', { isOnline: isAvailableType })
if (isAvailableType) {
void sendUnifiedSession()
}
await sendNode({ await sendNode({
tag: 'presence', tag: 'presence',
+57
View File
@@ -11,6 +11,7 @@ import {
MIN_UPLOAD_INTERVAL, MIN_UPLOAD_INTERVAL,
NOISE_WA_HEADER, NOISE_WA_HEADER,
PROCESSABLE_HISTORY_TYPES, PROCESSABLE_HISTORY_TYPES,
TimeMs,
UPLOAD_TIMEOUT UPLOAD_TIMEOUT
} from '../Defaults' } from '../Defaults'
import type { LIDMapping, SocketConfig } from '../Types' import type { LIDMapping, SocketConfig } from '../Types'
@@ -77,6 +78,8 @@ export const makeSocket = (config: SocketConfig) => {
const publicWAMBuffer = new BinaryInfo() const publicWAMBuffer = new BinaryInfo()
let serverTimeOffsetMs = 0
const uqTagId = generateMdTagPrefix() const uqTagId = generateMdTagPrefix()
const generateMessageTag = () => `${uqTagId}${epoch++}` const generateMessageTag = () => `${uqTagId}${epoch++}`
@@ -895,6 +898,7 @@ export const makeSocket = (config: SocketConfig) => {
ws.on('CB:iq,,pair-success', async (stanza: BinaryNode) => { ws.on('CB:iq,,pair-success', async (stanza: BinaryNode) => {
logger.debug('pair success recv') logger.debug('pair success recv')
try { try {
updateServerTimeOffset(stanza)
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds) const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds)
logger.info( logger.info(
@@ -906,6 +910,7 @@ export const makeSocket = (config: SocketConfig) => {
ev.emit('connection.update', { isNewLogin: true, qr: undefined }) ev.emit('connection.update', { isNewLogin: true, qr: undefined })
await sendNode(reply) await sendNode(reply)
void sendUnifiedSession()
} catch (error: any) { } catch (error: any) {
logger.info({ trace: error.stack }, 'error in pairing') logger.info({ trace: error.stack }, 'error in pairing')
void end(error) void end(error)
@@ -914,6 +919,7 @@ export const makeSocket = (config: SocketConfig) => {
// login complete // login complete
ws.on('CB:success', async (node: BinaryNode) => { ws.on('CB:success', async (node: BinaryNode) => {
try { try {
updateServerTimeOffset(node)
await uploadPreKeysToServerIfRequired() await uploadPreKeysToServerIfRequired()
await sendPassiveIq('active') await sendPassiveIq('active')
@@ -933,6 +939,7 @@ export const makeSocket = (config: SocketConfig) => {
ev.emit('creds.update', { me: { ...authState.creds.me!, lid: node.attrs.lid } }) ev.emit('creds.update', { me: { ...authState.creds.me!, lid: node.attrs.lid } })
ev.emit('connection.update', { connection: 'open' }) ev.emit('connection.update', { connection: 'open' })
void sendUnifiedSession()
if (node.attrs.lid && authState.creds.me?.id) { if (node.attrs.lid && authState.creds.me?.id) {
const myLID = node.attrs.lid const myLID = node.attrs.lid
@@ -1041,6 +1048,54 @@ export const makeSocket = (config: SocketConfig) => {
Object.assign(creds, update) Object.assign(creds, update)
}) })
const updateServerTimeOffset = ({ attrs }: BinaryNode) => {
const tValue = attrs?.t
if (!tValue) {
return
}
const parsed = Number(tValue)
if (Number.isNaN(parsed) || parsed <= 0) {
return
}
const localMs = Date.now()
serverTimeOffsetMs = parsed * 1000 - localMs
logger.debug({ offset: serverTimeOffsetMs }, 'calculated server time offset')
}
const getUnifiedSessionId = () => {
const offsetMs = 3 * TimeMs.Day
const now = Date.now() + serverTimeOffsetMs
const id = (now + offsetMs) % TimeMs.Week
return id.toString()
}
const sendUnifiedSession = async () => {
if (!ws.isOpen) {
return
}
const node = {
tag: 'ib',
attrs: {},
content: [
{
tag: 'unified_session',
attrs: {
id: getUnifiedSessionId()
}
}
]
}
try {
await sendNode(node)
} catch (error) {
logger.debug({ error }, 'failed to send unified_session telemetry')
}
}
return { return {
type: 'md' as 'md', type: 'md' as 'md',
ws, ws,
@@ -1064,6 +1119,8 @@ export const makeSocket = (config: SocketConfig) => {
digestKeyBundle, digestKeyBundle,
rotateSignedPreKey, rotateSignedPreKey,
requestPairingCode, requestPairingCode,
updateServerTimeOffset,
sendUnifiedSession,
wamBuffer: publicWAMBuffer, wamBuffer: publicWAMBuffer,
/** Waits for the connection to WA to reach a state */ /** Waits for the connection to WA to reach a state */
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev), waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),