4b02652369
Applied yarn lint --fix to auto-correct: - Import spacing and sorting across multiple files - Trailing commas - Arrow function formatting - Object literal formatting - Indentation consistency - Removed unused imports (BaileysEventType, jest from tests) This commit addresses the linting errors reported in GitHub Actions: - Fixed import ordering in libsignal.ts - Fixed trailing commas in Defaults/index.ts - Cleaned up formatting across 63 files - Reduced line count by ~220 lines through formatting optimization Note: Some lint errors remain (75 errors, 239 warnings) mainly: - @typescript-eslint/no-unused-vars (variables assigned but not used) - @typescript-eslint/no-explicit-any (type safety warnings) These remaining issues are non-blocking and can be addressed incrementally. https://claude.ai/code/session_015R3U3kiprQiNTTNNt31Sg6
221 lines
6.8 KiB
TypeScript
221 lines
6.8 KiB
TypeScript
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
|
|
import type { UserFacingSocketConfig, WAVersion } from '../Types'
|
|
import type { VersionCacheLogger } from '../Utils/version-cache'
|
|
import { clearVersionCache, getCachedVersion, getVersionCacheStatus, refreshVersionCache } from '../Utils/version-cache'
|
|
import { makeCommunitiesSocket } from './communities'
|
|
|
|
/**
|
|
* Adapts Baileys logger to VersionCacheLogger interface
|
|
*/
|
|
const createCacheLogger = (logger: any): VersionCacheLogger | undefined => {
|
|
if (!logger) return undefined
|
|
return {
|
|
info: (obj: unknown, msg?: string) => logger.info(obj, msg),
|
|
debug: (obj: unknown, msg?: string) => logger.debug(obj, msg),
|
|
warn: (obj: unknown, msg?: string) => logger.warn(obj, msg)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = {
|
|
...DEFAULT_CONNECTION_CONFIG,
|
|
...config
|
|
}
|
|
|
|
return makeCommunitiesSocket(newConfig)
|
|
}
|
|
|
|
/**
|
|
* Creates a WhatsApp socket connection with automatic version fetching
|
|
* and periodic version checks (soft update - transparent to user).
|
|
*
|
|
* Features:
|
|
* - **Shared cache**: 150 connections = 1 request (not 150)
|
|
* - **Persistent cache**: Survives server restarts
|
|
* - Fetches latest version on connect (uses cache if valid)
|
|
* - 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
|
|
* // All connections share the same cached version
|
|
* const sock = await makeWASocketAutoVersion({
|
|
* 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
|
|
* })
|
|
* ```
|
|
*/
|
|
export const makeWASocketAutoVersion = async (config: UserFacingSocketConfig) => {
|
|
const mergedConfig = {
|
|
...DEFAULT_CONNECTION_CONFIG,
|
|
...config
|
|
}
|
|
|
|
const logger = mergedConfig.logger
|
|
const cacheLogger = createCacheLogger(logger)
|
|
const checkIntervalMs = mergedConfig.versionCheckIntervalMs
|
|
|
|
// Track version separately to avoid mutating config (Fix #7)
|
|
let trackedVersion: WAVersion = [...mergedConfig.version] as WAVersion
|
|
let versionCheckInterval: ReturnType<typeof setInterval> | null = null
|
|
let isSocketClosed = false
|
|
|
|
/**
|
|
* Cleans up the version check interval
|
|
*/
|
|
const cleanupInterval = () => {
|
|
if (versionCheckInterval) {
|
|
clearInterval(versionCheckInterval)
|
|
versionCheckInterval = null
|
|
logger?.debug('Stopped periodic version check')
|
|
}
|
|
}
|
|
|
|
// Fetch latest version using SHARED CACHE
|
|
// 150 connections starting = 1 request (deduplication)
|
|
try {
|
|
const { version, fromCache, age } = await getCachedVersion({
|
|
cacheTtlMs: checkIntervalMs,
|
|
logger: cacheLogger
|
|
})
|
|
|
|
logger?.info(
|
|
{
|
|
version,
|
|
fromCache,
|
|
ageMinutes: fromCache ? Math.round(age / 60000) : 0
|
|
},
|
|
fromCache ? 'Using cached WhatsApp Web version' : 'Fetched fresh WhatsApp Web version'
|
|
)
|
|
|
|
mergedConfig.version = version
|
|
trackedVersion = [...version] as WAVersion
|
|
} catch (error) {
|
|
logger?.warn({ error, fallbackVersion: mergedConfig.version }, 'Error fetching version, using bundled version')
|
|
}
|
|
|
|
// Create the socket
|
|
const sock = makeWASocket(mergedConfig)
|
|
|
|
// Listen for connection close to cleanup interval (Fix #1, #6)
|
|
// This handles both explicit sock.end() and internal disconnections
|
|
sock.ev.on('connection.update', update => {
|
|
if (update.connection === 'close') {
|
|
isSocketClosed = true
|
|
cleanupInterval()
|
|
} else if (update.connection === 'open') {
|
|
isSocketClosed = false
|
|
}
|
|
})
|
|
|
|
// Setup periodic version check if interval > 0
|
|
if (checkIntervalMs > 0) {
|
|
logger?.info({ intervalHours: checkIntervalMs / (60 * 60 * 1000) }, 'Starting periodic version check')
|
|
|
|
versionCheckInterval = setInterval(async () => {
|
|
// Skip if socket is closed (Fix #8 - race condition)
|
|
if (isSocketClosed) {
|
|
cleanupInterval()
|
|
return
|
|
}
|
|
|
|
try {
|
|
logger?.debug('Checking for WhatsApp Web version update...')
|
|
|
|
// Check cache status first
|
|
const cacheStatus = getVersionCacheStatus(checkIntervalMs)
|
|
|
|
// Only refresh if cache is expired (one socket refreshes, others use cache)
|
|
let newVersion: WAVersion
|
|
let fetchSuccess = true
|
|
|
|
if (cacheStatus.isExpired) {
|
|
logger?.debug('Cache expired, refreshing...')
|
|
const result = await refreshVersionCache({ logger: cacheLogger })
|
|
newVersion = result.version
|
|
fetchSuccess = result.success
|
|
|
|
// Don't update to fallback version on transient network errors
|
|
if (!fetchSuccess) {
|
|
logger?.warn({ fallbackVersion: result.version }, 'Failed to fetch latest version, keeping current version')
|
|
return // Skip version update on fetch failure
|
|
}
|
|
} else if (cacheStatus.version) {
|
|
// Cache still valid, use cached version directly (no file I/O)
|
|
newVersion = cacheStatus.version
|
|
} else {
|
|
// No cache available, skip this check
|
|
return
|
|
}
|
|
|
|
// Double-check socket is still open after async operation (Fix #8)
|
|
if (isSocketClosed) {
|
|
cleanupInterval()
|
|
return
|
|
}
|
|
|
|
if (versionsAreDifferent(trackedVersion, newVersion)) {
|
|
const isCritical = isCriticalVersionChange(trackedVersion, newVersion)
|
|
const previousVersion = trackedVersion
|
|
|
|
logger?.info(
|
|
{
|
|
currentVersion: previousVersion,
|
|
newVersion: newVersion,
|
|
isCritical
|
|
},
|
|
'New WhatsApp Web version detected! Will use on next reconnection.'
|
|
)
|
|
|
|
// Update tracked version for next reconnection (Fix #7)
|
|
trackedVersion = [...newVersion] as WAVersion
|
|
|
|
// Emit event for user to handle (only if socket still open)
|
|
if (!isSocketClosed) {
|
|
sock.ev.emit('version.update', {
|
|
currentVersion: previousVersion,
|
|
newVersion: newVersion,
|
|
isCritical
|
|
})
|
|
}
|
|
} else {
|
|
logger?.debug({ version: trackedVersion }, 'Version is up to date')
|
|
}
|
|
} catch (error) {
|
|
logger?.warn({ error }, 'Error checking for version update')
|
|
}
|
|
}, checkIntervalMs)
|
|
}
|
|
|
|
return sock
|
|
}
|
|
|
|
// Export cache utilities for manual control
|
|
export { getCachedVersion, refreshVersionCache, clearVersionCache, getVersionCacheStatus }
|
|
|
|
export default makeWASocket
|