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:
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user