feat: add periodic version check with soft reconnection

Implements automatic version updates that are transparent to users:

- Checks for new WhatsApp Web version every 6 hours (configurable)
- When new version detected, saves it for next natural reconnection
- Emits 'version.update' event so users can track updates
- No disconnection required - WhatsApp naturally reconnects every 30min-2h
- Cleans up interval when socket closes

Configuration:
```typescript
const sock = await makeWASocketAutoVersion({
    auth: state,
    versionCheckIntervalMs: 6 * 60 * 60 * 1000 // 6 hours (default)
})

sock.ev.on('version.update', ({ currentVersion, newVersion, isCritical }) => {
    console.log(`New version: ${newVersion.join('.')}`)
})
```
This commit is contained in:
Claude
2026-01-21 16:51:39 +00:00
parent 76e080daec
commit 805fdd3525
4 changed files with 122 additions and 7 deletions
+4
View File
@@ -47,9 +47,13 @@ export const PROCESSABLE_HISTORY_TYPES = [
proto.HistorySync.HistorySyncType.INITIAL_STATUS_V3 proto.HistorySync.HistorySyncType.INITIAL_STATUS_V3
] ]
// 6 hours in milliseconds
const SIX_HOURS_MS = 6 * 60 * 60 * 1000
export const DEFAULT_CONNECTION_CONFIG: SocketConfig = { export const DEFAULT_CONNECTION_CONFIG: SocketConfig = {
version: version as WAVersion, version: version as WAVersion,
fetchLatestVersion: false, fetchLatestVersion: false,
versionCheckIntervalMs: SIX_HOURS_MS,
browser: Browsers.macOS('Chrome'), browser: Browsers.macOS('Chrome'),
waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat', waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
connectTimeoutMs: 20_000, connectTimeoutMs: 20_000,
+99 -7
View File
@@ -1,8 +1,24 @@
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults' import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
import type { UserFacingSocketConfig } from '../Types' import type { UserFacingSocketConfig, WAVersion } from '../Types'
import { fetchLatestWaWebVersion } from '../Utils/generics' import { fetchLatestWaWebVersion } from '../Utils/generics'
import { makeCommunitiesSocket } from './communities' import { makeCommunitiesSocket } from './communities'
/**
* Compares two WhatsApp versions
* @returns true if versions are different
*/
const versionsAreDifferent = (v1: WAVersion, v2: WAVersion): boolean => {
return v1[0] !== v2[0] || v1[1] !== v2[1] || v1[2] !== v2[2]
}
/**
* Checks if a version change is critical (major or minor version changed)
*/
const isCriticalVersionChange = (oldVersion: WAVersion, newVersion: WAVersion): boolean => {
// Major version change (index 0) or minor version change (index 1)
return oldVersion[0] !== newVersion[0] || oldVersion[1] !== newVersion[1]
}
// export the last socket layer // export the last socket layer
const makeWASocket = (config: UserFacingSocketConfig) => { const makeWASocket = (config: UserFacingSocketConfig) => {
const newConfig = { const newConfig = {
@@ -21,14 +37,26 @@ const makeWASocket = (config: UserFacingSocketConfig) => {
} }
/** /**
* Creates a WhatsApp socket connection with automatic version fetching. * Creates a WhatsApp socket connection with automatic version fetching
* Fetches the latest WhatsApp Web version from web.whatsapp.com before connecting. * and periodic version checks (soft update - transparent to user).
* Falls back to bundled version if fetch fails. *
* Features:
* - Fetches latest version on connect
* - Checks for new versions every 6 hours (configurable)
* - Updates version on next natural reconnection (transparent)
* - Emits 'version.update' event when new version is detected
* *
* @example * @example
* ```typescript * ```typescript
* const sock = await makeWASocketAutoVersion({ * const sock = await makeWASocketAutoVersion({
* auth: state * auth: state,
* versionCheckIntervalMs: 6 * 60 * 60 * 1000 // 6 hours (default)
* })
*
* // Listen for version updates
* sock.ev.on('version.update', ({ currentVersion, newVersion, isCritical }) => {
* console.log(`New version detected: ${newVersion.join('.')}`)
* // Version will be used on next reconnection automatically
* }) * })
* ``` * ```
*/ */
@@ -39,8 +67,10 @@ export const makeWASocketAutoVersion = async (config: UserFacingSocketConfig) =>
} }
const logger = mergedConfig.logger const logger = mergedConfig.logger
let currentVersion = mergedConfig.version
let versionCheckInterval: ReturnType<typeof setInterval> | null = null
// Fetch latest version // Fetch latest version before connecting
try { try {
logger?.info('Fetching latest WhatsApp Web version...') logger?.info('Fetching latest WhatsApp Web version...')
const result = await fetchLatestWaWebVersion() const result = await fetchLatestWaWebVersion()
@@ -48,6 +78,7 @@ export const makeWASocketAutoVersion = async (config: UserFacingSocketConfig) =>
if (result.isLatest) { if (result.isLatest) {
logger?.info({ version: result.version }, 'Using latest WhatsApp Web version') logger?.info({ version: result.version }, 'Using latest WhatsApp Web version')
mergedConfig.version = result.version mergedConfig.version = result.version
currentVersion = result.version
} else { } else {
logger?.warn( logger?.warn(
{ error: result.error, fallbackVersion: mergedConfig.version }, { error: result.error, fallbackVersion: mergedConfig.version },
@@ -61,7 +92,68 @@ export const makeWASocketAutoVersion = async (config: UserFacingSocketConfig) =>
) )
} }
return makeWASocket(mergedConfig) // Create the socket
const sock = makeWASocket(mergedConfig)
// Setup periodic version check if interval > 0
const checkIntervalMs = mergedConfig.versionCheckIntervalMs
if (checkIntervalMs > 0) {
logger?.info(
{ intervalHours: checkIntervalMs / (60 * 60 * 1000) },
'Starting periodic version check'
)
versionCheckInterval = setInterval(async () => {
try {
logger?.debug('Checking for WhatsApp Web version update...')
const result = await fetchLatestWaWebVersion()
if (result.isLatest && versionsAreDifferent(currentVersion, result.version)) {
const isCritical = isCriticalVersionChange(currentVersion, result.version)
logger?.info(
{
currentVersion,
newVersion: result.version,
isCritical
},
'New WhatsApp Web version detected! Will use on next reconnection.'
)
// Emit event for user to handle
sock.ev.emit('version.update', {
currentVersion,
newVersion: result.version,
isCritical
})
// Update the version for next reconnection
// This is the "soft" update - it will be used when WhatsApp naturally reconnects
currentVersion = result.version
mergedConfig.version = result.version
} else if (result.isLatest) {
logger?.debug({ version: currentVersion }, 'Version is up to date')
} else {
logger?.warn({ error: result.error }, 'Failed to check for version update')
}
} catch (error) {
logger?.warn({ error }, 'Error checking for version update')
}
}, checkIntervalMs)
// Clean up interval when socket closes
const originalEnd = sock.end.bind(sock)
sock.end = (error) => {
if (versionCheckInterval) {
clearInterval(versionCheckInterval)
versionCheckInterval = null
logger?.debug('Stopped periodic version check')
}
return originalEnd(error)
}
}
return sock
} }
export default makeWASocket export default makeWASocket
+12
View File
@@ -107,6 +107,18 @@ export type BaileysEventMap = {
/** Settings and actions sync events */ /** Settings and actions sync events */
'chats.lock': { id: string; locked: boolean } 'chats.lock': { id: string; locked: boolean }
/**
* Emitted when a new WhatsApp Web version is detected.
* The new version will be used on the next reconnection (soft update).
*/
'version.update': {
/** Previous version */
currentVersion: [number, number, number]
/** New version detected */
newVersion: [number, number, number]
/** Whether the update is critical (major version change) */
isCritical: boolean
}
'settings.update': 'settings.update':
| { setting: 'unarchiveChats'; value: boolean } | { setting: 'unarchiveChats'; value: boolean }
| { setting: 'locale'; value: string } | { setting: 'locale'; value: string }
+7
View File
@@ -56,6 +56,13 @@ export type SocketConfig = {
* @default false * @default false
*/ */
fetchLatestVersion: boolean fetchLatestVersion: boolean
/**
* Interval in milliseconds to check for new WhatsApp Web versions.
* When a new version is detected, it will be used on the next reconnection.
* Set to 0 to disable periodic checks.
* @default 21600000 (6 hours)
*/
versionCheckIntervalMs: number
/** override browser config */ /** override browser config */
browser: WABrowserDescription browser: WABrowserDescription
/** agent used for fetch requests -- uploading/downloading media */ /** agent used for fetch requests -- uploading/downloading media */