CoolFace
Apppublic

ckriti/HuggingClaw

sourceHugging Facemitupdated 7mo agoView on Hugging Face
0likes
qr-detection-manager.cjs385 linesDownload Raw Back to scripts
1#!/usr/bin/env node2 3/**4 * QR Detection Manager for OpenClaw AI5 * MANDATORY QR Wait/Notify Implementation6 * 7 * When WhatsApp login requires QR code scan:8 * - STOP all debug operations9 * - Wait for QR code scan10 * - Clear user prompts11 * - Only continue after successful scan12 */13 14const fs = require('fs');15const path = require('path');16const { WebSocket } = require('ws');17const readline = require('readline');18 19class QRDetectionManager {20    constructor() {21        this.ws = null;22        this.isPaused = false;23        this.qrDetected = false;24        this.qrSourcePath = null;25        this.scanCompleted = false;26        this.timeout = null;27        this.qrTimeout = 300000; // 5 minutes timeout28        29        // Setup structured logging30        this.log = (level, message, data = {}) => {31            const logEntry = {32                timestamp: new Date().toISOString(),33                level,34                module: 'qr-detection-manager',35                message,36                ...data37            };38            console.log(JSON.stringify(logEntry));39        };40        41        this.log('info', 'QR Detection Manager initialized');42    }43    44    async connectWebSocket(spaceUrl) {45        try {46            // Handle spaceUrl being just a hostname or full URL47            let host = spaceUrl.replace(/^https?:\/\//, '').replace(/\/$/, '');48            const wsUrl = `wss://${host}`;49            const fullWsUrl = `${wsUrl}/queue/join`;50            51            this.log('info', 'Connecting to WebSocket', { url: fullWsUrl });52            53            this.ws = new WebSocket(fullWsUrl);54            55            this.ws.on('open', () => {56                this.log('info', 'WebSocket connection established');57                this.startMonitoring();58            });59            60            this.ws.on('message', (data) => {61                this.handleWebSocketMessage(data);62            });63            64            this.ws.on('error', (error) => {65                this.log('error', 'WebSocket error', { error: error.message });66            });67            68            this.ws.on('close', () => {69                this.log('info', 'WebSocket connection closed');70            });71            72        } catch (error) {73            this.log('error', 'Failed to connect to WebSocket', { error: error.message });74        }75    }76 77    handleWebSocketMessage(data) {78        // Placeholder for future WS message handling if needed79        // Currently we rely mostly on log/file monitoring80    }81 82    startMonitoring() {83        this.log('info', 'Starting QR code monitoring');84        85        // Send initial ping to keep connection alive86        const pingInterval = setInterval(() => {87            if (this.ws && this.ws.readyState === WebSocket.OPEN) {88                this.ws.ping();89            } else {90                clearInterval(pingInterval);91            }92        }, 30000);93        94        // Watch for QR code detection95        this.setupQRDetection();96    }97    98    setupQRDetection() {99        this.log('info', 'Setting up QR code detection');100        101        // Start timeout for QR scan102        this.timeout = setTimeout(() => {103            if (!this.scanCompleted) {104                this.log('warning', 'QR scan timeout reached');105                this.outputQRPrompt('❌ QR scan timeout. Please restart the process.', 'timeout');106                process.exit(1);107            }108        }, this.qrTimeout);109        110        // Monitor for QR code in logs or filesystem111        this.monitorForQR();112    }113    114    monitorForQR() {115        const homeDir = process.env.HOME || '/home/node';116        // Check for QR code file in actual HF Spaces paths117        const qrCheckInterval = setInterval(() => {118            if (this.scanCompleted) {119                clearInterval(qrCheckInterval);120                return;121            }122 123            // Check actual QR code file locations for HF Spaces OpenClaw124            const qrPaths = [125                path.join(homeDir, '.openclaw/credentials/whatsapp/qr.png'),126                path.join(homeDir, '.openclaw/workspace/qr.png'),127                path.join(homeDir, 'logs/qr.png'),128            ];129 130            for (const qrPath of qrPaths) {131                if (fs.existsSync(qrPath)) {132                    this.qrSourcePath = qrPath;133                    this.handleQRDetected(qrPath);134                    break;135                }136            }137 138            // Also check for QR code in recent logs139            this.checkLogsForQR();140        }, 2000); // Check every 2 seconds141    }142    143    checkLogsForQR() {144        try {145            const homeDir = process.env.HOME || '/home/node';146            const logPaths = [147                path.join(homeDir, 'logs/app.log'),148                path.join(homeDir, '.openclaw/workspace/startup.log'),149                path.join(homeDir, '.openclaw/workspace/sync.log'),150            ];151            152            for (const logPath of logPaths) {153                if (fs.existsSync(logPath)) {154                    const logContent = fs.readFileSync(logPath, 'utf8');155                    if (this.isQRInLogContent(logContent)) {156                        this.handleQRDetected('log');157                        break;158                    }159                }160            }161        } catch (error) {162            // Ignore log reading errors163        }164    }165    166    isQRInLogContent(content) {167        // Look for QR-related log entries168        const qrPatterns = [169            /qr code/i,170            /scan.*qr/i,171            /please scan/i,172            /waiting.*qr/i,173            /login.*qr/i,174            /whatsapp.*qr/i,175            /authentication.*qr/i176        ];177        178        return qrPatterns.some(pattern => pattern.test(content));179    }180    181    handleQRDetected(source) {182        if (this.qrDetected) {183            return; // Already detected184        }185        186        this.qrDetected = true;187        this.log('info', 'QR code detected', { source });188        189        // MANDATORY: Stop all debug operations190        this.isPaused = true;191        192        // MANDATORY: Clear user prompts193        this.outputQRPrompt('⏳ Waiting for WhatsApp QR code scan...', 'waiting');194        this.outputQRPrompt('📱 Please scan the QR code with your phone to continue.', 'qr');195        196        // Start monitoring for scan completion197        this.monitorScanCompletion();198    }199    200    outputQRPrompt(message, type) {201        // Clear console for better visibility202        process.stdout.write('\x1b[2J\x1b[0f');203        204        // Output formatted QR prompt205        const separator = '='.repeat(60);206        console.log(`\n${separator}`);207        console.log(`🔐 WHATSAPP LOGIN REQUIRED`);208        console.log(`${separator}\n`);209        console.log(message);210        console.log(`\n${separator}`);211        212        // Add visual indicators based on type213        if (type === 'waiting') {214            console.log('⏳ Operation paused - waiting for QR scan...');215        } else if (type === 'qr') {216            console.log('📱 Use your WhatsApp app to scan the QR code');217        } else if (type === 'success') {218            console.log('✅ QR scan completed successfully!');219        } else if (type === 'timeout') {220            console.log('❌ QR scan timeout - please try again');221        }222        223        console.log(`${separator}\n`);224        225        // Also log as JSON for structured processing226        this.log(type === 'success' ? 'info' : 'warning', 'QR prompt output', { 227            message, 228            type,229            isPaused: this.isPaused 230        });231    }232    233    monitorScanCompletion() {234        this.log('info', 'Monitoring for QR scan completion');235        236        // Monitor for scan completion signals237        const completionCheck = setInterval(() => {238            if (this.checkScanCompletion()) {239                clearInterval(completionCheck);240                this.handleScanCompleted();241            }242        }, 1000);243    }244    245    checkScanCompletion() {246        const homeDir = process.env.HOME || '/home/node';247 248        // 1. Check if QR file was removed (only if we know which file was detected)249        if (this.qrSourcePath && !fs.existsSync(this.qrSourcePath)) {250            return true;251        }252 253        // 2. Check for successful login in logs254        try {255            const logPaths = [256                path.join(homeDir, 'logs/app.log'),257                path.join(homeDir, '.openclaw/workspace/startup.log'),258                path.join(homeDir, '.openclaw/workspace/sync.log'),259            ];260 261            for (const logPath of logPaths) {262                if (fs.existsSync(logPath)) {263                    const logContent = fs.readFileSync(logPath, 'utf8');264                    if (this.isLoginInLogContent(logContent)) {265                        return true;266                    }267                }268            }269        } catch (error) {270            // Ignore log reading errors271        }272 273        // 3. Check for WhatsApp session/creds files in actual HF Spaces paths274        const sessionPaths = [275            path.join(homeDir, '.openclaw/credentials/whatsapp/creds.json'),276            path.join(homeDir, '.openclaw/credentials/whatsapp/session.json'),277        ];278 279        for (const sessionPath of sessionPaths) {280            if (fs.existsSync(sessionPath)) {281                return true;282            }283        }284 285        return false;286    }287    288    isLoginInLogContent(content) {289        // Look for successful login patterns290        const loginPatterns = [291            /login.*successful/i,292            /authentication.*success/i,293            /session.*established/i,294            /connected.*whatsapp/i,295            /qr.*scanned/i,296            /scan.*completed/i,297            /user.*authenticated/i298        ];299        300        return loginPatterns.some(pattern => pattern.test(content));301    }302    303    handleScanCompleted() {304        this.scanCompleted = true;305        this.isPaused = false;306        307        // Clear timeout308        if (this.timeout) {309            clearTimeout(this.timeout);310        }311        312        // MANDATORY: Clear success notification313        this.outputQRPrompt('✅ QR code scanned successfully. Login completed.', 'success');314        315        this.log('info', 'QR scan completed, resuming operations');316        317        // Wait a moment for user to see the success message318        setTimeout(() => {319            // Exit the process to allow main application to continue320            process.exit(0);321        }, 3000);322    }323    324    async waitForQRScan() {325        return new Promise((resolve, reject) => {326            const checkInterval = setInterval(() => {327                if (this.scanCompleted) {328                    clearInterval(checkInterval);329                    resolve();330                }331            }, 1000);332            333            // Timeout after 5 minutes334            setTimeout(() => {335                clearInterval(checkInterval);336                reject(new Error('QR scan timeout'));337            }, this.qrTimeout);338        });339    }340    341    close() {342        if (this.ws) {343            this.ws.close();344        }345        if (this.timeout) {346            clearTimeout(this.timeout);347        }348        this.log('info', 'QR Detection Manager closed');349    }350}351 352// Command line interface353async function main() {354    const args = process.argv.slice(2);355    const spaceUrl = args[0] || process.env.SPACE_HOST || '';356    357    const manager = new QRDetectionManager();358    359    try {360        await manager.connectWebSocket(spaceUrl);361        362        // Keep the process running363        process.on('SIGINT', () => {364            manager.log('info', 'Received SIGINT, shutting down gracefully');365            manager.close();366            process.exit(0);367        });368        369        process.on('SIGTERM', () => {370            manager.log('info', 'Received SIGTERM, shutting down gracefully');371            manager.close();372            process.exit(0);373        });374        375    } catch (error) {376        manager.log('error', 'QR Detection Manager failed', { error: error.message });377        process.exit(1);378    }379}380 381if (require.main === module) {382    main();383}384 385module.exports = QRDetectionManager;