/* eslint-disable max-depth, @typescript-eslint/no-unused-vars */ /** * Custom Logger for Baileys/WhatsApp * * Provides: * - Pre-configured logger for Baileys/WhatsApp context * - Event categorization by type (connection, message, media, etc.) * - Specific filters to reduce noise * - WhatsApp event metrics * - Optimized formatting for debugging * * @module Utils/baileys-logger */ import type { ILogger } from './logger.js' import { createStructuredLogger, type LogEntry, type LogLevel, StructuredLogger } from './structured-logger.js' /** * Baileys-specific log categories */ export type BaileysLogCategory = | 'connection' // WebSocket connection events | 'auth' // Authentication and QR code | 'message' // Message send/receive | 'media' // Media upload/download | 'group' // Group operations | 'presence' // Presence status | 'call' // Voice/video calls | 'sync' // Data synchronization | 'encryption' // Encryption operations | 'retry' // Operation retries | 'socket' // Low-level socket events | 'binary' // Binary encoding/decoding | 'unknown' // Unknown category /** * Baileys Logger configuration */ export interface BaileysLoggerConfig { /** Default log level */ level: LogLevel /** Categories to ignore */ ignoredCategories?: BaileysLogCategory[] /** Categories with elevated log level (always debug) */ verboseCategories?: BaileysLogCategory[] /** Whether to log message payloads (may be sensitive) */ logMessagePayloads?: boolean /** Whether to log binary data in hex */ logBinaryData?: boolean /** Instance identifier prefix */ instanceId?: string /** Handler for specific events */ eventHandler?: (category: BaileysLogCategory, entry: LogEntry) => void /** Size limit for logged payloads (bytes) */ maxPayloadSize?: number } /** * Baileys-specific metrics */ export interface BaileysLoggerMetrics { connectionAttempts: number connectionSuccesses: number connectionFailures: number messagesSent: number messagesReceived: number mediaUploads: number mediaDownloads: number retryAttempts: number encryptionOperations: number errorsByCategory: Record lastConnectionTime?: string lastMessageTime?: string } /** * Patterns to detect log category */ const CATEGORY_PATTERNS: Array<{ pattern: RegExp; category: BaileysLogCategory }> = [ { pattern: /connect|disconnect|socket|ws|websocket|open|close/i, category: 'connection' }, { pattern: /auth|qr|pairing|login|logout|creds/i, category: 'auth' }, { pattern: /message|msg|chat|text|send|recv|read|receipt/i, category: 'message' }, { pattern: /media|image|video|audio|document|sticker|upload|download/i, category: 'media' }, { pattern: /group|participant|admin|subject|invite/i, category: 'group' }, { pattern: /presence|online|offline|typing|available/i, category: 'presence' }, { pattern: /call|voice|video|ring/i, category: 'call' }, { pattern: /sync|history|initial|full/i, category: 'sync' }, { pattern: /encrypt|decrypt|signal|key|cipher/i, category: 'encryption' }, { pattern: /retry|attempt|backoff|reconnect/i, category: 'retry' }, { pattern: /binary|encode|decode|proto|buffer/i, category: 'binary' } ] /** * Custom logger for Baileys * * @example * ```typescript * const logger = createBaileysLogger({ * level: 'debug', * instanceId: 'session-1' * }) * * logger.logConnection('connected', { duration: 1500 }) * logger.logMessage('send', 'text', 'user@s.whatsapp.net') * ``` */ export class BaileysLogger implements ILogger { private structuredLogger: StructuredLogger private config: Required private metrics: BaileysLoggerMetrics private childContext: Record = {} constructor(config: Partial = {}) { this.config = { level: config.level || 'info', ignoredCategories: config.ignoredCategories || [], verboseCategories: config.verboseCategories || [], logMessagePayloads: config.logMessagePayloads ?? false, logBinaryData: config.logBinaryData ?? false, instanceId: config.instanceId || this.generateInstanceId(), eventHandler: config.eventHandler || (() => {}), maxPayloadSize: config.maxPayloadSize || 1024 } this.structuredLogger = createStructuredLogger({ level: this.config.level, name: `baileys:${this.config.instanceId}`, jsonFormat: process.env.NODE_ENV === 'production', redactFields: ['password', 'token', 'secret', 'key', 'authKey', 'macKey'] }) this.metrics = this.createInitialMetrics() } private generateInstanceId(): string { return Math.random().toString(36).substring(2, 8) } private createInitialMetrics(): BaileysLoggerMetrics { return { connectionAttempts: 0, connectionSuccesses: 0, connectionFailures: 0, messagesSent: 0, messagesReceived: 0, mediaUploads: 0, mediaDownloads: 0, retryAttempts: 0, encryptionOperations: 0, errorsByCategory: { connection: 0, auth: 0, message: 0, media: 0, group: 0, presence: 0, call: 0, sync: 0, encryption: 0, retry: 0, socket: 0, binary: 0, unknown: 0 } } } get level(): string { return this.config.level } set level(newLevel: string) { this.config.level = newLevel as LogLevel this.structuredLogger.level = newLevel } /** * Create child logger with additional context */ child(obj: Record): BaileysLogger { const childLogger = new BaileysLogger(this.config) childLogger.childContext = { ...this.childContext, ...obj } return childLogger } /** * Detect log category based on content */ private detectCategory(obj: unknown, msg?: string): BaileysLogCategory { const searchText = [ msg || '', typeof obj === 'string' ? obj : '', typeof obj === 'object' && obj !== null ? JSON.stringify(obj) : '' ].join(' ') for (const { pattern, category } of CATEGORY_PATTERNS) { if (pattern.test(searchText)) { return category } } return 'unknown' } /** * Check if category should be logged */ private shouldLogCategory(category: BaileysLogCategory, _level: LogLevel): boolean { if (this.config.ignoredCategories.includes(category)) { return false } // Verbose categories always log at debug or higher if (this.config.verboseCategories.includes(category)) { return true } return true } /** * Sanitize message payload */ private sanitizePayload(obj: unknown): unknown { if (!this.config.logMessagePayloads) { if (typeof obj === 'object' && obj !== null) { const sanitized = { ...(obj as Record) } // Remove sensitive message fields const sensitiveFields = ['body', 'text', 'content', 'caption', 'payload', 'data'] for (const field of sensitiveFields) { if (field in sanitized) { const value = sanitized[field] if (typeof value === 'string' && value.length > 0) { sanitized[field] = `[${value.length} chars]` } else if (Buffer.isBuffer(value)) { sanitized[field] = `[Buffer: ${value.length} bytes]` } } } return sanitized } } // Limit payload size if (typeof obj === 'object' && obj !== null) { const str = JSON.stringify(obj) if (str.length > this.config.maxPayloadSize) { return { _truncated: true, _originalSize: str.length, _preview: str.substring(0, 200) + '...' } } } return obj } /** * Update metrics based on log */ private updateMetrics(category: BaileysLogCategory, level: LogLevel, obj: unknown): void { const objStr = typeof obj === 'object' ? JSON.stringify(obj) : String(obj) switch (category) { case 'connection': if (/attempt|trying|connecting/i.test(objStr)) { this.metrics.connectionAttempts++ } else if (/success|connected|open/i.test(objStr)) { this.metrics.connectionSuccesses++ this.metrics.lastConnectionTime = new Date().toISOString() } else if (/fail|error|close/i.test(objStr)) { this.metrics.connectionFailures++ } break case 'message': if (/send|sent|outgoing/i.test(objStr)) { this.metrics.messagesSent++ this.metrics.lastMessageTime = new Date().toISOString() } else if (/recv|received|incoming/i.test(objStr)) { this.metrics.messagesReceived++ this.metrics.lastMessageTime = new Date().toISOString() } break case 'media': if (/upload/i.test(objStr)) { this.metrics.mediaUploads++ } else if (/download/i.test(objStr)) { this.metrics.mediaDownloads++ } break case 'retry': this.metrics.retryAttempts++ break case 'encryption': this.metrics.encryptionOperations++ break } if (level === 'error' || level === 'fatal') { this.metrics.errorsByCategory[category]++ } } /** * Main log method */ private log(level: LogLevel, obj: unknown, msg?: string): void { const category = this.detectCategory(obj, msg) if (!this.shouldLogCategory(category, level)) { return } // Update metrics this.updateMetrics(category, level, obj) // Sanitize payload const sanitizedObj = this.sanitizePayload(obj) // Add Baileys context const enrichedObj = { category, instanceId: this.config.instanceId, ...this.childContext, ...(typeof sanitizedObj === 'object' && sanitizedObj !== null ? sanitizedObj : { value: sanitizedObj }) } // Structured log (skip if level is 'silent') if (level !== 'silent') { const logMethod = ( this.structuredLogger as unknown as Record void) | undefined> )[level] if (logMethod) { logMethod.call(this.structuredLogger, enrichedObj, msg) } } // Event handler if (this.config.eventHandler) { const entry: LogEntry = { timestamp: new Date().toISOString(), level, levelValue: 0, message: msg || '', name: `baileys:${this.config.instanceId}`, data: enrichedObj } this.config.eventHandler(category, entry) } } trace(obj: unknown, msg?: string): void { this.log('trace', obj, msg) } debug(obj: unknown, msg?: string): void { this.log('debug', obj, msg) } info(obj: unknown, msg?: string): void { this.log('info', obj, msg) } warn(obj: unknown, msg?: string): void { this.log('warn', obj, msg) } error(obj: unknown, msg?: string): void { this.log('error', obj, msg) } /** * Log connection-specific event */ logConnection(event: 'connecting' | 'connected' | 'disconnected' | 'error', details?: Record): void { const level: LogLevel = event === 'error' ? 'error' : event === 'disconnected' ? 'warn' : 'info' this.log(level, { event, ...details, category: 'connection' }, `Connection ${event}`) } /** * Log message-specific event */ logMessage(direction: 'send' | 'receive', messageType: string, jid: string, details?: Record): void { const sanitizedJid = this.sanitizeJid(jid) this.log( 'info', { direction, messageType, jid: sanitizedJid, ...details, category: 'message' }, `Message ${direction}: ${messageType}` ) } /** * Log media-specific event */ logMedia(operation: 'upload' | 'download', mediaType: string, size: number, details?: Record): void { this.log( 'info', { operation, mediaType, sizeBytes: size, sizeFormatted: this.formatBytes(size), ...details, category: 'media' }, `Media ${operation}: ${mediaType}` ) } /** * Sanitize JID for logging (mask part of number) */ private sanitizeJid(jid: string): string { if (process.env.NODE_ENV === 'production') { // In production, mask part of the number const parts = jid.split('@') const localPart = parts[0] const domainPart = parts[1] if (parts.length === 2 && localPart && domainPart && localPart.length > 4) { return `${localPart.substring(0, 4)}****@${domainPart}` } } return jid } /** * Format bytes for human readability */ private formatBytes(bytes: number): string { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}` } /** * Get logger metrics */ getMetrics(): BaileysLoggerMetrics { return { ...this.metrics } } /** * Get internal structured logger metrics */ getStructuredMetrics() { return this.structuredLogger.getMetrics() } /** * Reset metrics */ resetMetrics(): void { this.metrics = this.createInitialMetrics() this.structuredLogger.resetMetrics() } /** * Get instance ID */ getInstanceId(): string { return this.config.instanceId } } /** * Factory to create Baileys Logger */ export function createBaileysLogger(config?: Partial): BaileysLogger { return new BaileysLogger(config) } /** * Default Baileys logger singleton */ let defaultBaileysLogger: BaileysLogger | null = null export function getDefaultBaileysLogger(): BaileysLogger { if (!defaultBaileysLogger) { defaultBaileysLogger = createBaileysLogger({ level: 'info' }) } return defaultBaileysLogger } export function setDefaultBaileysLogger(logger: BaileysLogger): void { defaultBaileysLogger = logger } // ============================================================================ // CONSOLE-FRIENDLY LOGGING FUNCTIONS WITH [BAILEYS] PREFIX // ============================================================================ /** * Check if Baileys logging is enabled via environment variable * BAILEYS_LOG=false disables all [BAILEYS] console logs */ function isBaileysLogEnabled(): boolean { return process.env.BAILEYS_LOG !== 'false' } /** * Safely stringify a value, handling circular references, Errors, and special types */ function safeStringify(value: unknown, seen: WeakSet = new WeakSet()): string { // Handle primitives if (value === null) return 'null' if (value === undefined) return 'undefined' if (typeof value === 'string') return value if (typeof value === 'number' || typeof value === 'boolean') return String(value) if (typeof value === 'function') return '[Function]' if (typeof value === 'symbol') return value.toString() if (typeof value === 'bigint') return `${value}n` // Handle objects if (typeof value === 'object') { // Check for circular reference if (seen.has(value)) return '[Circular]' seen.add(value) // Handle Error objects if (value instanceof Error) { return `${value.name}: ${value.message}` } // Handle Date objects if (value instanceof Date) { return value.toISOString() } // Handle Arrays if (Array.isArray(value)) { if (value.length === 0) return '[]' if (value.length <= 3) { const items = value.map(v => safeStringify(v, seen)) return `[${items.join(', ')}]` } return `[Array(${value.length})]` } // Handle plain objects try { const keys = Object.keys(value as Record) if (keys.length === 0) return '{}' if (keys.length <= 5) { const pairs = keys.map(k => { const v = (value as Record)[k] return `${k}: ${safeStringify(v, seen)}` }) return `{${pairs.join(', ')}}` } return `{Object(${keys.length} keys)}` } catch { return '[Object]' } } return String(value) } /** * Format data object for single-line or multi-line output * Handles circular references, Error objects, arrays, and undefined values */ function formatLogData(data: Record, singleLine = true): string { if (!data || Object.keys(data).length === 0) return '' const seen = new WeakSet() if (singleLine) { // Single line format: { key1: value1, key2: value2 } const pairs = Object.entries(data).map(([k, v]) => { return `${k}: ${safeStringify(v, seen)}` }) return `{ ${pairs.join(', ')} }` } // Multi-line format - use safe replacer for JSON.stringify try { return JSON.stringify( data, (key, value) => { if (value instanceof Error) { return { name: value.name, message: value.message, stack: value.stack } } if (typeof value === 'bigint') { return `${value}n` } return value }, 2 ) } catch { // Fallback for circular references or other issues return safeStringify(data, seen) } } /** * Event buffer logging types */ export type EventBufferLogType = | 'buffer_start' | 'buffer_flush' | 'buffer_overflow' | 'buffer_timeout' | 'cache_cleanup' | 'adaptive_mode' /** * Log event buffer operations with emoji and [BAILEYS] prefix * * @example * logEventBuffer('buffer_start') * // Output: [BAILEYS] ๐Ÿ“ฆ Event buffering started * * logEventBuffer('buffer_flush', { flushCount: 10, historyCacheSize: 5, mode: 'aggressive' }) * // Output: [BAILEYS] ๐Ÿ”„ Event buffer flushed { flushCount: 10, historyCacheSize: 5, mode: 'aggressive' } */ export function logEventBuffer(type: EventBufferLogType, data?: Record, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const dataStr = data ? ' ' + formatLogData(data) : '' switch (type) { case 'buffer_start': console.log(`${prefix} ๐Ÿ“ฆ Event buffering started${dataStr}`) break case 'buffer_flush': console.log(`${prefix} ๐Ÿ”„ Event buffer flushed${dataStr}`) break case 'buffer_overflow': console.log(`${prefix} โš ๏ธ Buffer overflow detected${dataStr}`) break case 'buffer_timeout': console.log(`${prefix} โฐ Buffer timeout reached${dataStr}`) break case 'cache_cleanup': console.log(`${prefix} ๐Ÿงน History cache cleanup${dataStr}`) break case 'adaptive_mode': console.log(`${prefix} ๐Ÿง  Adaptive mode updated${dataStr}`) break } } /** * Log buffer metrics in a formatted way * * @example * logBufferMetrics({ * itemsBuffered: 0, * flushCount: 120, * historyCacheSize: 0, * buffersInProgress: 0, * adaptive: { mode: 'aggressive', timeout: 1000, eventRate: 1.34, isHealthy: true } * }) */ export function logBufferMetrics( metrics: { itemsBuffered: number flushCount: number historyCacheSize: number buffersInProgress: number adaptive?: { mode: string timeout: number eventRate: number isHealthy: boolean } }, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' console.log(`${prefix} ๐Ÿ“Š Buffer Metrics {`) console.log(`${prefix} itemsBuffered: ${metrics.itemsBuffered},`) console.log(`${prefix} flushCount: ${metrics.flushCount},`) console.log(`${prefix} historyCacheSize: ${metrics.historyCacheSize},`) console.log(`${prefix} buffersInProgress: ${metrics.buffersInProgress}${metrics.adaptive ? ',' : ''}`) if (metrics.adaptive) { console.log(`${prefix} adaptive: {`) console.log(`${prefix} mode: '${metrics.adaptive.mode}',`) console.log(`${prefix} timeout: ${metrics.adaptive.timeout},`) console.log(`${prefix} eventRate: ${metrics.adaptive.eventRate.toFixed(2)},`) console.log(`${prefix} isHealthy: ${metrics.adaptive.isHealthy}`) console.log(`${prefix} }`) } console.log(`${prefix} }`) } /** * Log message sent event * * @example * logMessageSent('3EB02FA562D6CCC0876CDE', '5511999999999@s.whatsapp.net') * // Output: [BAILEYS] ๐Ÿ“ค Message sent: 3EB02FA562D6CCC0876CDE โ†’ 5511999999999@s.whatsapp.net */ export function logMessageSent(messageId: string, recipientJid: string, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' console.log(`${prefix} ๐Ÿ“ค Message sent: ${messageId} โ†’ ${recipientJid}`) } /** * Log message received event * * @example * logMessageReceived('A5E0349897A3F16F3F2778EEF94A065F', '238315571802285@lid') * // Output: [BAILEYS] ๐Ÿ“ฅ Message received: A5E0349897A3F16F3F2778EEF94A065F โ† 238315571802285@lid */ export function logMessageReceived(messageId: string, senderJid: string, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' console.log(`${prefix} ๐Ÿ“ฅ Message received: ${messageId} โ† ${senderJid}`) } /** * Log connection event * * @example * logConnection('connecting') * // Output: [BAILEYS] ๐Ÿ”Œ Connecting to WhatsApp... * * logConnection('open') * // Output: [BAILEYS] โœ… Connected to WhatsApp */ export function logConnection( event: 'connecting' | 'open' | 'close' | 'reconnecting' | 'error', details?: Record, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const dataStr = details ? ' ' + formatLogData(details) : '' switch (event) { case 'connecting': console.log(`${prefix} ๐Ÿ”Œ Connecting to WhatsApp...${dataStr}`) break case 'open': console.log(`${prefix} โœ… Connected to WhatsApp${dataStr}`) break case 'close': console.log(`${prefix} ๐Ÿ”ด Disconnected from WhatsApp${dataStr}`) break case 'reconnecting': console.log(`${prefix} ๐Ÿ”„ Reconnecting to WhatsApp...${dataStr}`) break case 'error': console.log(`${prefix} โŒ Connection error${dataStr}`) break } } /** * Log authentication event * * @example * logAuth('qr_generated') * // Output: [BAILEYS] ๐Ÿ“ฑ QR Code generated - scan with WhatsApp */ export function logAuth( event: 'qr_generated' | 'pairing_code' | 'authenticated' | 'logout' | 'creds_updated', details?: Record, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const dataStr = details ? ' ' + formatLogData(details) : '' switch (event) { case 'qr_generated': console.log(`${prefix} ๐Ÿ“ฑ QR Code generated - scan with WhatsApp${dataStr}`) break case 'pairing_code': console.log(`${prefix} ๐Ÿ”‘ Pairing code generated${dataStr}`) break case 'authenticated': console.log(`${prefix} โœ… Authentication successful${dataStr}`) break case 'logout': console.log(`${prefix} ๐Ÿšช Logged out${dataStr}`) break case 'creds_updated': console.log(`${prefix} ๐Ÿ” Credentials updated${dataStr}`) break } } /** * Log circuit breaker event * * @example * logCircuitBreaker('open', { failures: 5 }) * // Output: [BAILEYS] โšก Circuit breaker OPEN { failures: 5 } */ export function logCircuitBreaker( state: 'open' | 'closed' | 'half_open', details?: Record, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const dataStr = details ? ' ' + formatLogData(details) : '' switch (state) { case 'open': console.log(`${prefix} โšก Circuit breaker OPEN${dataStr}`) break case 'closed': console.log(`${prefix} โœ… Circuit breaker CLOSED${dataStr}`) break case 'half_open': console.log(`${prefix} ๐Ÿ”ถ Circuit breaker HALF-OPEN${dataStr}`) break } } /** * Log retry event * * @example * logRetry(2, 3, 5000, 'connection') * // Output: [BAILEYS] ๐Ÿ” Retry attempt 2/3 for connection (delay: 5000ms) */ export function logRetry( attempt: number, maxAttempts: number, delayMs: number, operation: string, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' console.log(`${prefix} ๐Ÿ” Retry attempt ${attempt}/${maxAttempts} for ${operation} (delay: ${delayMs}ms)`) } /** * Log generic Baileys info message * * @example * logInfo('PreKey validation passed') * // Output: [BAILEYS] โ„น๏ธ PreKey validation passed */ export function logInfo(message: string, data?: Record, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const formatted = data ? formatLogData(data) : '' const dataStr = formatted ? ' ' + formatted : '' console.log(`${prefix} โ„น๏ธ ${message}${dataStr}`) } /** * Log generic Baileys warning message * * @example * logWarn('Rate limit approaching') * // Output: [BAILEYS] โš ๏ธ Rate limit approaching */ export function logWarn(message: string, data?: Record, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const formatted = data ? formatLogData(data) : '' const dataStr = formatted ? ' ' + formatted : '' console.log(`${prefix} โš ๏ธ ${message}${dataStr}`) } /** * Log generic Baileys error message * * @example * logError('Failed to send message', { error: 'timeout' }) * // Output: [BAILEYS] โŒ Failed to send message { error: 'timeout' } */ export function logError(message: string, data?: Record, sessionName?: string): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const formatted = data ? formatLogData(data) : '' const dataStr = formatted ? ' ' + formatted : '' console.error(`${prefix} โŒ ${message}${dataStr}`) } /** * Log LID mapping store event */ export function logLidMapping( event: 'initialized' | 'lookup' | 'store' | 'batch_resolved', data?: Record, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const dataStr = data ? ' ' + formatLogData(data) : '' switch (event) { case 'initialized': console.log(`${prefix} ๐Ÿ—‚๏ธ LID Mapping Store initialized${dataStr}`) break case 'lookup': console.log(`${prefix} ๐Ÿ” LID lookup${dataStr}`) break case 'store': console.log(`${prefix} ๐Ÿ’พ LID stored${dataStr}`) break case 'batch_resolved': console.log(`${prefix} ๐Ÿ“ฆ LID batch resolved${dataStr}`) break } } /** * Log tctoken lifecycle events * * @example * logTcToken('fetch', { jid: '5511999999999@s.whatsapp.net' }) * // Output: [BAILEYS] ๐Ÿ”‘ TcToken fetch โ†’ 5511999999999@s.whatsapp.net * * logTcToken('expired', { jid: '5511999999999@s.whatsapp.net', age: '32d' }) * // Output: [BAILEYS] ๐Ÿ”‘ TcToken expired โ†’ 5511999999999@s.whatsapp.net { age: 32d } */ export function logTcToken( event: 'stored' | 'expired' | 'fetch' | 'fetched' | 'reissue' | 'reissue_ok' | 'reissue_fail' | 'prune' | 'error_463' | 'error_479' | 'attached' | 'retry_463_ok', data?: Record, sessionName?: string ): void { if (!isBaileysLogEnabled()) return const prefix = sessionName ? `[BAILEYS] [${sessionName}]` : '[BAILEYS]' const jid = data?.jid ? ` โ†’ ${data.jid}` : '' const rest = data ? { ...data } : undefined if (rest) delete rest.jid const extraStr = rest && Object.keys(rest).length > 0 ? ' ' + formatLogData(rest) : '' switch (event) { case 'stored': console.log(`${prefix} ๐Ÿ”‘ TcToken stored${jid}${extraStr}`) break case 'expired': console.log(`${prefix} ๐Ÿ”‘ TcToken expired${jid}${extraStr}`) break case 'fetch': console.log(`${prefix} ๐Ÿ”‘ TcToken fetch${jid}${extraStr}`) break case 'fetched': console.log(`${prefix} ๐Ÿ”‘ TcToken fetched${jid}${extraStr}`) break case 'reissue': console.log(`${prefix} ๐Ÿ”‘ TcToken reissue${jid}${extraStr}`) break case 'reissue_ok': console.log(`${prefix} ๐Ÿ”‘ TcToken reissue OK${jid}${extraStr}`) break case 'reissue_fail': console.log(`${prefix} ๐Ÿ”‘ TcToken reissue failed${jid}${extraStr}`) break case 'prune': console.log(`${prefix} ๐Ÿ”‘ TcToken prune${extraStr}`) break case 'attached': console.log(`${prefix} ๐Ÿ”‘ TcToken attached${jid}${extraStr}`) break case 'error_463': console.log(`${prefix} โš ๏ธ TcToken missing (463)${jid}${extraStr}`) break case 'error_479': console.log(`${prefix} โš ๏ธ TcToken smax-invalid (479)${jid}${extraStr}`) break case 'retry_463_ok': console.log(`${prefix} ๐Ÿ”„ TcToken retry 463 OK${jid}${extraStr}`) break } } export default BaileysLogger