Skip to main content
Glama
server.js33.7 kB
// HTTP server with WhatsApp integration const http = require('http'); const url = require('url'); // Import WhatsApp integration (but don't wait for it) const whatsapp = require('./whatsapp-integration'); // Direct reference to the WhatsApp client for MCP-compatible endpoints let whatsappClient = null; // Set the WhatsApp client reference when it's ready whatsapp.onClientReady((client) => { console.log('[Server] WhatsApp client reference received'); whatsappClient = client; }); // Start logging immediately console.log(`[STARTUP] Starting HTTP server with WhatsApp integration`); console.log(`[STARTUP] Node version: ${process.version}`); console.log(`[STARTUP] Platform: ${process.platform}`); console.log(`[STARTUP] PORT: ${process.env.PORT || 3000}`); // Start WhatsApp initialization in the background WITHOUT awaiting // This is critical - we don't block server startup setTimeout(() => { console.log('[STARTUP] Starting WhatsApp client initialization in the background'); whatsapp.initializeWhatsAppClient().catch(err => { console.error('[STARTUP] Error initializing WhatsApp client:', err); // Non-blocking - server continues running even if WhatsApp fails }); }, 2000); // Short delay to ensure server is fully up first // Create server with no dependencies const server = http.createServer((req, res) => { const url = req.url; console.log(`[${new Date().toISOString()}] ${req.method} ${url}`); // Health check endpoint - handle both with and without trailing space if (url === '/health' || url === '/health ' || url === '/health%20') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() })); return; } // Root endpoint if (url === '/' || url === '/%20') { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head><title>WhatsApp API Server</title></head> <body> <h1>WhatsApp API Server</h1> <p>Server is running successfully</p> <p>Server time: ${new Date().toISOString()}</p> <p>Node version: ${process.version}</p> <p>Available endpoints:</p> <ul> <li><a href="/health">Health Check</a></li> <li><a href="/status">WhatsApp Status</a></li> <li><a href="/qr">WhatsApp QR Code</a> (when available)</li> </ul> </body> </html> `); return; } // WhatsApp Status endpoint if (url === '/status' || url === '/status%20') { const status = whatsapp.getStatus(); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: status.status, error: status.error, timestamp: new Date().toISOString() })); return; } // WhatsApp QR Code endpoint if (url === '/qr' || url === '/qr%20') { try { // Async function so we need to handle it carefully whatsapp.getQRCode().then(qrCode => { if (!qrCode) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'QR code not available', status: whatsapp.getStatus().status })); return; } res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head><title>WhatsApp QR Code</title></head> <body> <h1>WhatsApp QR Code</h1> <p>Scan with your WhatsApp mobile app:</p> <img src="${qrCode}" alt="WhatsApp QR Code" style="max-width: 300px;"/> <p>Status: ${whatsapp.getStatus().status}</p> <p><a href="/qr">Refresh</a> | <a href="/status">Check Status</a></p> </body> </html> `); }).catch(err => { console.error('[Server] Error generating QR code:', err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Failed to generate QR code', details: err.message })); }); } catch (err) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'QR code generation error', details: err.message })); } return; } // API Key endpoint - simple way to get the current API key if (url === '/wa-api' || url === '/wa-api/') { const status = whatsapp.getStatus(); if (status.status === 'ready' && status.apiKey) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head><title>WhatsApp API Key</title></head> <body> <h1>WhatsApp API Key</h1> <p>Current status: <strong>${status.status}</strong></p> <p>API Key: <code>${status.apiKey}</code></p> <p>MCP command:</p> <pre>wweb-mcp -m mcp -s local -c api -t command --api-base-url https://whatsapp-integration-u4q0.onrender.com/api --api-key ${status.apiKey}</pre> </body> </html> `); } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head><title>WhatsApp API Key</title></head> <body> <h1>WhatsApp API Key</h1> <p>Current status: <strong>${status.status}</strong></p> <p>API Key not available yet. WhatsApp must be in 'ready' state first.</p> <p><a href="/api">Refresh</a> | <a href="/status">Check Status</a> | <a href="/qr">Scan QR Code</a></p> </body> </html> `); } return; } // MCP Tool specific endpoint - status check with API key (required by wweb-mcp) if (url === '/api/status' || url.startsWith('/api/status?')) { const status = whatsapp.getStatus(); const clientApiKey = status.apiKey; // Only validate API key if client is ready and has an API key if (status.status === 'ready' && clientApiKey) { // Extract API key from request (if any) const urlParams = new URL('http://dummy.com' + req.url).searchParams; const requestApiKey = urlParams.get('api_key') || urlParams.get('apiKey'); const headerApiKey = req.headers['x-api-key'] || req.headers['authorization']; const providedApiKey = requestApiKey || (headerApiKey && headerApiKey.replace('Bearer ', '')); // Validate API key if provided if (providedApiKey && providedApiKey !== clientApiKey) { console.log(`[${new Date().toISOString()}] Invalid API key for /api/status endpoint`); res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid API key' })); return; } } console.log(`[${new Date().toISOString()}] MCP status check: ${status.status}`); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: true, connected: status.status === 'ready', status: status.status, error: status.error, timestamp: new Date().toISOString() })); return; } // Debug endpoint for WhatsApp client state if (url === '/api/debug') { const status = whatsapp.getStatus(); const clientInfo = { status: status.status, connected: status.connected, authenticated: status.authenticated || false, clientExists: !!whatsappClient, clientInfo: whatsappClient ? { info: whatsappClient.info ? Object.keys(whatsappClient.info) : null, hasChats: typeof whatsappClient.getChats === 'function', hasContacts: typeof whatsappClient.getContacts === 'function' } : null }; res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify(clientInfo)); return; } // MCP Tool endpoint - get all chats (required by wweb-mcp) if (url === '/api/chats' || url.startsWith('/api/chats?')) { const status = whatsapp.getStatus(); const clientApiKey = status.apiKey; // Only validate API key if client is ready and has an API key if (status.status === 'ready' && clientApiKey) { // Extract API key from request (if any) const urlParams = new URL('http://dummy.com' + req.url).searchParams; const requestApiKey = urlParams.get('api_key') || urlParams.get('apiKey'); const headerApiKey = req.headers['x-api-key'] || req.headers['authorization']; const providedApiKey = requestApiKey || (headerApiKey && headerApiKey.replace('Bearer ', '')); // Validate API key if provided if (providedApiKey && providedApiKey !== clientApiKey) { console.log(`[${new Date().toISOString()}] Invalid API key for /api/chats endpoint`); res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid API key' })); return; } } // Handle case where WhatsApp is not ready if (status.status !== 'ready') { console.log(`[${new Date().toISOString()}] /api/chats called but WhatsApp is not ready. Status: ${status.status}`); res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `WhatsApp not ready. Current status: ${status.status}`, status: status.status })); return; } // Forward the request to the wweb-mcp library console.log(`[${new Date().toISOString()}] MCP get_chats request forwarded to WhatsApp client`); // Check if WhatsApp client reference is valid if (!whatsappClient) { console.error(`[${new Date().toISOString()}] WhatsApp client reference is null or undefined`); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'WhatsApp client not properly initialized' })); return; } // Using whatsapp-web.js getChats() function with timeout try { // Create a timeout promise that rejects after 15 seconds const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Request timed out after 15 seconds')), 15000); }); // Debug the client's info console.log(`[${new Date().toISOString()}] WhatsApp client info:`, { id: whatsappClient.info ? whatsappClient.info.wid : 'unknown', platform: whatsappClient.info ? whatsappClient.info.platform : 'unknown', phone: whatsappClient.info ? whatsappClient.info.phone : 'unknown' }); // Enhanced implementation of getChats that's more reliable in containerized environments const getChatsCustom = async () => { console.log(`[${new Date().toISOString()}] Using enhanced getChats implementation...`); // First try the standard getChats method try { console.log(`[${new Date().toISOString()}] Attempting primary getChats method...`); const primaryChats = await whatsappClient.getChats(); if (primaryChats && primaryChats.length > 0) { console.log(`[${new Date().toISOString()}] Successfully retrieved ${primaryChats.length} chats using primary method`); return primaryChats; } } catch (err) { console.warn(`[${new Date().toISOString()}] Primary getChats method failed:`, err.message); } // Next try to access the internal _chats collection which might be more stable if (whatsappClient._chats && whatsappClient._chats.length > 0) { console.log(`[${new Date().toISOString()}] Found ${whatsappClient._chats.length} chats in internal collection`); return whatsappClient._chats; } // Next try the store which is another way to access chats if (whatsappClient.store && typeof whatsappClient.store.getChats === 'function') { console.log(`[${new Date().toISOString()}] Attempting to get chats from store...`); try { const storeChats = await whatsappClient.store.getChats(); if (storeChats && storeChats.length > 0) { console.log(`[${new Date().toISOString()}] Found ${storeChats.length} chats in store`); return storeChats; } } catch (err) { console.error(`[${new Date().toISOString()}] Error getting chats from store:`, err); } } // Try to get chats using direct access to WA-JS methods (more advanced approach) try { console.log(`[${new Date().toISOString()}] Attempting to use WA-JS direct access...`); const wajs = whatsappClient.pupPage ? await whatsappClient.pupPage.evaluate(() => { return window.WWebJS.getChats().map(c => ({ id: c.id, name: c.name, timestamp: c.t, isGroup: c.isGroup, unreadCount: c.unreadCount || 0 })); }) : null; if (wajs && wajs.length > 0) { console.log(`[${new Date().toISOString()}] Found ${wajs.length} chats using WA-JS direct access`); return wajs.map(chat => ({ id: { _serialized: chat.id }, name: chat.name || '', isGroup: chat.isGroup, timestamp: chat.timestamp, unreadCount: chat.unreadCount })); } } catch (err) { console.error(`[${new Date().toISOString()}] Error using WA-JS direct access:`, err); } // As a fallback, provide at least one mock chat for MCP compatibility console.log(`[${new Date().toISOString()}] All methods failed. Falling back to mock chat data`); return [{ id: { _serialized: 'mock-chat-id-1' }, name: 'Mock Chat (Fallback)', isGroup: false, timestamp: Date.now() / 1000, unreadCount: 0 }]; }; // Race between the custom chat implementation and the timeout Promise.race([ getChatsCustom(), timeoutPromise ]).then(chats => { console.log(`[${new Date().toISOString()}] Successfully retrieved ${chats.length} chats`); // Transform the chats to the format expected by the MCP tool const formattedChats = chats.map(chat => ({ id: chat.id._serialized, name: chat.name || '', isGroup: chat.isGroup, timestamp: chat.timestamp ? new Date(chat.timestamp * 1000).toISOString() : null, unreadCount: chat.unreadCount || 0 })); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: true, chats: formattedChats })); }).catch(err => { console.error(`[${new Date().toISOString()}] Error getting chats:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); }); } catch (err) { console.error(`[${new Date().toISOString()}] Exception getting chats:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); } return; } // MCP Tool endpoint - get messages from a specific chat if (url.startsWith('/api/messages/')) { const status = whatsapp.getStatus(); const clientApiKey = status.apiKey; // Only validate API key if client is ready and has an API key if (status.status === 'ready' && clientApiKey) { // Extract API key from request (if any) const urlParams = new URL('http://dummy.com' + req.url).searchParams; const requestApiKey = urlParams.get('api_key') || urlParams.get('apiKey'); const headerApiKey = req.headers['x-api-key'] || req.headers['authorization']; const providedApiKey = requestApiKey || (headerApiKey && headerApiKey.replace('Bearer ', '')); // Validate API key if provided if (providedApiKey && providedApiKey !== clientApiKey) { console.log(`[${new Date().toISOString()}] Invalid API key for /api/messages endpoint`); res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid API key' })); return; } } // Handle case where WhatsApp is not ready if (status.status !== 'ready') { console.log(`[${new Date().toISOString()}] /api/messages called but WhatsApp is not ready. Status: ${status.status}`); res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `WhatsApp not ready. Current status: ${status.status}`, status: status.status })); return; } // Extract chat ID from URL const pathParts = url.split('?')[0].split('/'); const chatId = pathParts[3]; // /api/messages/{chatId} if (!chatId) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Missing chat ID in URL' })); return; } // Get the limit from query params const urlParams = new URL('http://dummy.com' + req.url).searchParams; const limit = parseInt(urlParams.get('limit') || '20', 10); // Get messages for this chat console.log(`[${new Date().toISOString()}] MCP get_messages request for chat ${chatId}`); try { // Format chat ID correctly for whatsapp-web.js const formattedChatId = chatId.includes('@') ? chatId : `${chatId}@c.us`; // First get the chat object whatsappClient.getChatById(formattedChatId).then(chat => { // Then fetch messages chat.fetchMessages({ limit }).then(messages => { // Format the messages as required by the MCP tool const formattedMessages = messages.map(msg => ({ id: msg.id._serialized, body: msg.body || '', timestamp: msg.timestamp ? new Date(msg.timestamp * 1000).toISOString() : null, from: msg.from || '', fromMe: msg.fromMe || false, type: msg.type || 'chat' })); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: true, messages: formattedMessages })); }).catch(err => { console.error(`[${new Date().toISOString()}] Error fetching messages:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); }); }).catch(err => { console.error(`[${new Date().toISOString()}] Error getting chat by ID:`, err); res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `Chat not found: ${err.message}` })); }); } catch (err) { console.error(`[${new Date().toISOString()}] Exception getting messages:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); } return; } // Support OPTIONS requests for CORS if (req.method === 'OPTIONS') { res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key' }); res.end(); return; } // Add a test message endpoint to validate sending works if (url === '/api/test-message') { const status = whatsapp.getStatus(); // Check if WhatsApp is ready if (status.status !== 'ready') { console.log(`[${new Date().toISOString()}] /api/test-message called but WhatsApp is not ready. Status: ${status.status}`); res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `WhatsApp not ready. Current status: ${status.status}`, status: status.status })); return; } // Send a test message to the specified number const testNumber = '16505578984'; const testMessage = `Test message from WhatsApp API at ${new Date().toISOString()}`; console.log(`[${new Date().toISOString()}] Sending test message to ${testNumber}`); whatsapp.sendMessage(testNumber, testMessage) .then(result => { console.log(`[${new Date().toISOString()}] Test message sent successfully to ${testNumber}`); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, message: 'Test message sent successfully', to: testNumber, messageId: result.id ? result.id._serialized : 'sent', content: testMessage })); }) .catch(err => { console.error(`[${new Date().toISOString()}] Error sending test message:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); }); return; } // Handle API message send endpoint POST /api/send if (url === '/api/send' && req.method === 'POST') { const status = whatsapp.getStatus(); const clientApiKey = status.apiKey; // Only validate API key if client is ready and has an API key if (status.status === 'ready' && clientApiKey) { // Extract API key from request (if any) const headerApiKey = req.headers['x-api-key'] || req.headers['authorization']; const providedApiKey = headerApiKey && headerApiKey.replace('Bearer ', ''); // Validate API key if provided if (!providedApiKey || providedApiKey !== clientApiKey) { console.log(`[${new Date().toISOString()}] Invalid or missing API key for /api/send endpoint`); res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid or missing API key' })); return; } } else { // Handle case where WhatsApp is not ready console.log(`[${new Date().toISOString()}] /api/send called but WhatsApp is not ready. Status: ${status.status}`); res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `WhatsApp not ready. Current status: ${status.status}`, status: status.status })); return; } // Get request body let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', async () => { try { const data = JSON.parse(body); const { to, message } = data; if (!to || !message) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Missing required fields: to, message' })); return; } console.log(`[${new Date().toISOString()}] Sending message to ${to}`); try { const result = await whatsapp.sendMessage(to, message); console.log(`[${new Date().toISOString()}] Message sent successfully to ${to}`); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: true, messageId: result.id ? result.id._serialized : 'sent', to: result.to || to })); } catch (err) { console.error(`[${new Date().toISOString()}] Error sending message:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); } } catch (err) { console.error(`[${new Date().toISOString()}] Error parsing JSON:`, err); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid JSON in request body' })); } }); return; } // Handle preflight CORS requests if (req.method === 'OPTIONS') { res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-API-Key', 'Access-Control-Max-Age': '86400' }); res.end(); return; } // Add endpoint to get the most recent message from a chat if (url === '/api/recent-message' || url.startsWith('/api/recent-message?')) { const status = whatsapp.getStatus(); const clientApiKey = status.apiKey; // Only validate API key if client is ready and has an API key if (status.status === 'ready' && clientApiKey) { // Extract API key from request (if any) const urlParams = new URL('http://dummy.com' + req.url).searchParams; const requestApiKey = urlParams.get('api_key') || urlParams.get('apiKey'); const headerApiKey = req.headers['x-api-key'] || req.headers['authorization']; const providedApiKey = requestApiKey || (headerApiKey && headerApiKey.replace('Bearer ', '')); // Validate API key if provided if (providedApiKey && providedApiKey !== clientApiKey) { console.log(`[${new Date().toISOString()}] Invalid API key for /api/recent-message endpoint`); res.writeHead(401, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Invalid API key' })); return; } } // Handle case where WhatsApp is not ready if (status.status !== 'ready') { console.log(`[${new Date().toISOString()}] /api/recent-message called but WhatsApp is not ready. Status: ${status.status}`); res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: `WhatsApp not ready. Current status: ${status.status}`, status: status.status })); return; } console.log(`[${new Date().toISOString()}] Getting most recent chat messages...`); // Check if WhatsApp client reference is valid if (!whatsappClient) { console.error(`[${new Date().toISOString()}] WhatsApp client reference is null or undefined`); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'WhatsApp client not properly initialized' })); return; } // Using enhanced method to get chats first // Wrap the whole logic in an async IIFE (Immediately Invoked Function Expression) (async () => { try { // Create a timeout promise const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Request timed out after 15 seconds')), 15000); }); // Enhanced method to get recent message const getRecentMessage = async () => { try { // First get chats using our enhanced method const getChatsCustom = async () => { // Try the standard getChats method first try { const primaryChats = await whatsappClient.getChats(); if (primaryChats && primaryChats.length > 0) { return primaryChats; } } catch (err) { console.warn(`[${new Date().toISOString()}] Standard getChats failed:`, err.message); } // Try direct access to _chats if (whatsappClient._chats && whatsappClient._chats.length > 0) { return whatsappClient._chats; } // Try using store if (whatsappClient.store && typeof whatsappClient.store.getChats === 'function') { try { const storeChats = await whatsappClient.store.getChats(); if (storeChats && storeChats.length > 0) { return storeChats; } } catch (err) {} } // Try WA-JS direct access try { const wajs = whatsappClient.pupPage ? await whatsappClient.pupPage.evaluate(() => { return window.WWebJS.getChats().map(c => ({ id: c.id, name: c.name, timestamp: c.t, isGroup: c.isGroup })); }) : null; if (wajs && wajs.length > 0) { return wajs.map(chat => ({ id: { _serialized: chat.id }, name: chat.name || '', isGroup: chat.isGroup, timestamp: chat.timestamp })); } } catch (err) {} // Fallback return []; }; // Get chats using our enhanced method const chats = await getChatsCustom(); console.log(`[${new Date().toISOString()}] Retrieved ${chats.length} chats`); if (chats.length === 0) { return { noChats: true }; } // Sort chats by timestamp if available const sortedChats = chats.sort((a, b) => { const timeA = a.timestamp || 0; const timeB = b.timestamp || 0; return timeB - timeA; // Newest first }); // Get most recent chat const recentChat = sortedChats[0]; if (!recentChat || !recentChat.id || !recentChat.id._serialized) { return { noValidChat: true, chats: sortedChats.length }; } // Get messages from this chat console.log(`[${new Date().toISOString()}] Getting messages from chat: ${recentChat.id._serialized}`); try { // Format chat ID correctly for whatsapp-web.js const formattedChatId = recentChat.id._serialized; const chat = await whatsappClient.getChatById(formattedChatId); const messages = await chat.fetchMessages({ limit: 1 }); if (messages && messages.length > 0) { return { success: true, chat: { id: recentChat.id._serialized, name: recentChat.name || '', isGroup: recentChat.isGroup || false }, message: { id: messages[0].id._serialized, body: messages[0].body || '', timestamp: messages[0].timestamp ? new Date(messages[0].timestamp * 1000).toISOString() : null, from: messages[0].from || '', fromMe: messages[0].fromMe || false } }; } else { return { noMessages: true, chatId: formattedChatId }; } } catch (err) { console.error(`[${new Date().toISOString()}] Error getting chat by ID:`, err); return { chatError: err.message, chatId: recentChat.id._serialized }; } } catch (err) { console.error(`[${new Date().toISOString()}] Error in getRecentMessage:`, err); return { error: err.message }; } }; // Race between the fetching and the timeout const result = await Promise.race([ getRecentMessage(), timeoutPromise ]); console.log(`[${new Date().toISOString()}] Recent message request result:`, JSON.stringify(result)); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify(result)); } catch (err) { console.error(`[${new Date().toISOString()}] Exception getting recent message:`, err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: err.message })); } })(); return; } // 404 for everything else res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); }); // Listen on all interfaces const PORT = process.env.PORT || 3000; server.listen(PORT, '0.0.0.0', () => { console.log(`[${new Date().toISOString()}] Server listening on port ${PORT}`); }); // Handle termination gracefully process.on('SIGINT', () => { console.log(`[${new Date().toISOString()}] Server shutting down`); process.exit(0); }); // Handle uncaught exceptions process.on('uncaughtException', error => { console.error(`[${new Date().toISOString()}] Uncaught exception: ${error.message}`); console.error(error.stack); // Keep server running despite errors });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fyimail/whatsapp-mcp2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server