diff --git a/src/Defaults/index.ts b/src/Defaults/index.ts index 5fb51da3..a21c4eb8 100644 --- a/src/Defaults/index.ts +++ b/src/Defaults/index.ts @@ -47,9 +47,13 @@ export const PROCESSABLE_HISTORY_TYPES = [ proto.HistorySync.HistorySyncType.INITIAL_STATUS_V3 ] +// 6 hours in milliseconds +const SIX_HOURS_MS = 6 * 60 * 60 * 1000 + export const DEFAULT_CONNECTION_CONFIG: SocketConfig = { version: version as WAVersion, fetchLatestVersion: false, + versionCheckIntervalMs: SIX_HOURS_MS, browser: Browsers.macOS('Chrome'), waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat', connectTimeoutMs: 20_000, diff --git a/src/Socket/index.ts b/src/Socket/index.ts index cd76a368..9207e390 100644 --- a/src/Socket/index.ts +++ b/src/Socket/index.ts @@ -1,8 +1,24 @@ import { DEFAULT_CONNECTION_CONFIG } from '../Defaults' -import type { UserFacingSocketConfig } from '../Types' +import type { UserFacingSocketConfig, WAVersion } from '../Types' import { fetchLatestWaWebVersion } from '../Utils/generics' 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 const makeWASocket = (config: UserFacingSocketConfig) => { const newConfig = { @@ -21,14 +37,26 @@ const makeWASocket = (config: UserFacingSocketConfig) => { } /** - * Creates a WhatsApp socket connection with automatic version fetching. - * Fetches the latest WhatsApp Web version from web.whatsapp.com before connecting. - * Falls back to bundled version if fetch fails. + * Creates a WhatsApp socket connection with automatic version fetching + * and periodic version checks (soft update - transparent to user). + * + * 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 * ```typescript * 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 + let currentVersion = mergedConfig.version + let versionCheckInterval: ReturnType | null = null - // Fetch latest version + // Fetch latest version before connecting try { logger?.info('Fetching latest WhatsApp Web version...') const result = await fetchLatestWaWebVersion() @@ -48,6 +78,7 @@ export const makeWASocketAutoVersion = async (config: UserFacingSocketConfig) => if (result.isLatest) { logger?.info({ version: result.version }, 'Using latest WhatsApp Web version') mergedConfig.version = result.version + currentVersion = result.version } else { logger?.warn( { 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 diff --git a/src/Types/Events.ts b/src/Types/Events.ts index bf0d1d07..18c2fba8 100644 --- a/src/Types/Events.ts +++ b/src/Types/Events.ts @@ -107,6 +107,18 @@ export type BaileysEventMap = { /** Settings and actions sync events */ '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': | { setting: 'unarchiveChats'; value: boolean } | { setting: 'locale'; value: string } diff --git a/src/Types/Socket.ts b/src/Types/Socket.ts index 1fb23b8c..9c7a61f0 100644 --- a/src/Types/Socket.ts +++ b/src/Types/Socket.ts @@ -56,6 +56,13 @@ export type SocketConfig = { * @default false */ 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 */ browser: WABrowserDescription /** agent used for fetch requests -- uploading/downloading media */