1require('dotenv').config(); 2const express = require('express'); 3const nodemailer = require('nodemailer'); 4 5const app = express(); 6const port = process.env.PORT || 5500; 7 8// Parse JSON bodies 9app.use(express.json()); 10app.use((req, res, next) => { 11 res.header('Access-Control-Allow-Origin', '*'); 12 res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); 13 res.header('Access-Control-Allow-Headers', 'Content-Type'); 14 next(); 15}); 16 17 18// -------------------- SEND SUPPORT TICKET -------------------- 19app.post('/support_ai.html/send-data', (req, res) => { 20 const { username, phoneNumber, chatHistory, whatsapp } = req.body; 21 22 if (!username || !phoneNumber || typeof whatsapp !== 'boolean') { 23 return res.status(400).json({ 24 message: 'Missing or invalid fields: username, phoneNumber and whatsapp (true/false) are required.' 25 }); 26 } 27 28 const transporter = nodemailer.createTransport({ 29 host: process.env.SMTP_HOST, 30 port: Number(process.env.SMTP_PORT), 31 secure: true, 32 auth: { 33 user: process.env.EMAIL_USER, 34 pass: process.env.EMAIL_PASS, 35 }, 36 }); 37 38 const contactPref = whatsapp ? 'WhatsApp' : 'Telefono'; 39 40const mailOptions = { 41 from: process.env.EMAIL_USER, 42 to: process.env.RECEIVER_EMAIL, 43 subject: `ð© Nuova Richiesta di Supporto per: ${username}`, 44 html: ` 45 <h2>ð© Nuova Richiesta di Supporto</h2> 46 <p><strong>ð¤ Nome Utente:</strong> ${username}</p> 47 <p><strong>ð Telefono:</strong> ${phoneNumber}</p> 48 <p><strong>ð¬ Preferenza contatto:</strong> ${contactPref}</p> 49 50 <h3>ð¬ Storico Chat:</h3> 51<div style="font-family: Arial, sans-serif; max-width:600px;"> 52 ${chatHistory 53 ? chatHistory 54 .split(/\r?\n/) // suddivide per linee 55 .map(line => { 56 let isUser = line.toLowerCase().includes('user:'); 57 let isAssistant = line.toLowerCase().includes('assistant:'); 58 59 let bgColor = isUser ? '#e3f2fd' : isAssistant ? '#f1f8e9' : '#fff3e0'; 60 let borderColor = isUser ? '#2196f3' : isAssistant ? '#4caf50' : '#ff9800'; 61 let icon = isUser ? 'ð¤' : isAssistant ? 'ð¤' : 'âï¸'; 62 let label = isUser ? 'Utente' : isAssistant ? 'Assistente' : 'Sistema'; 63 64 // rimuove il prefisso "user:" o "assistant:" dalla linea 65 let messageText = line.replace(/^user:\s*/i, '').replace(/^assistant:\s*/i, ''); 66 67 return ` 68 <div style="margin:6px 0; padding:8px; background:${bgColor}; border-left:4px solid ${borderColor}; border-radius:6px;"> 69 <div style="display:flex; align-items:center; margin-bottom:4px;"> 70 <strong style="color:${isUser ? '#1976d2' : '#388e3c'}">${icon} ${label}</strong> 71 </div> 72 <p style="margin:0; font-size:14px; line-height:1.4; color:#333;">${messageText}</p> 73 </div> 74 `; 75 }) 76 .join('') 77 : '<p>Nessuno storico disponibile</p>' 78 } 79</div> 80 81 <hr style="margin:20px 0;border:none;border-top:1px solid #ddd;"> 82 <p style="color:#666;font-size:12px;text-align:center;">Email generata automaticamente dal sistema TonyAI</p> 83 `, 84}; 85 transporter.sendMail(mailOptions, (err, info) => { 86 if (err) { 87 console.error('â Error sending support ticket email:', err); 88 return res.status(500).json({ message: 'Error sending email', err }); 89 } 90 console.log('â Support ticket email sent:', info.response); 91 res.json({ message: 'Email sent successfully', info }); 92 }); 93}); 94 95 96// -------------------- SEND CHAT TRANSCRIPT -------------------- 97function getBrowserName(userAgent) { 98 if (!userAgent) return 'Non disponibile'; 99 100 if (userAgent.includes('Edg/')) return 'Microsoft Edge'; 101 if (userAgent.includes('Chrome/') && !userAgent.includes('Edg/')) return 'Google Chrome'; 102 if (userAgent.includes('Firefox/')) return 'Mozilla Firefox'; 103 if (userAgent.includes('Safari/') && !userAgent.includes('Chrome/')) return 'Safari'; 104 if (userAgent.includes('Opera/') || userAgent.includes('OPR/')) return 'Opera'; 105 if (userAgent.includes('Brave/')) return 'Brave'; 106 107 return 'Browser Sconosciuto'; 108} 109 110 111const transporterTony = nodemailer.createTransport({ 112 host: process.env.SMTP_HOST, 113 port: Number(process.env.SMTP_PORT), 114 secure: true, 115 auth: { 116 user: process.env.EMAIL_USER, 117 pass: process.env.EMAIL_PASS, 118 }, 119}); 120 121app.post('/support_ai.html/send-chat-transcript', async (req, res) => { 122 console.log('ðµ Endpoint hit!'); // Add this first 123 console.log('ð¦ Request body:', JSON.stringify(req.body, null, 2)); // Log full body 124 125 try { 126 const { transcript, timestamp, BrowserType, endReason, messagesCount } = req.body; 127 128 console.log('ð Parsed data:', { 129 transcriptLength: transcript?.length, 130 timestamp, 131 BrowserType, 132 endReason, 133 messagesCount 134 });
135 136 if (!transcript || !Array.isArray(transcript) || transcript.length === 0) { 137 console.log('â Validation failed: transcript invalid'); 138 return res.status(400).json({ success: false, error: 'Transcript vuoto o non valido' }); 139 } 140 141 const browserName = getBrowserName(BrowserType); 142 console.log('ð Browser detected:', browserName); 143 144 const htmlContent = ` 145 <h2>ð¤ Nuova Conversazione TonyAI</h2> 146 <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 10px 0;"> 147 <p><strong>ð Data:</strong> ${new Date(timestamp).toLocaleString('it-IT')}</p> 148 <p><strong>ð Browser:</strong> ${browserName || 'Non disponibile'}</p> 149 <p><strong>ð Numero messaggi:</strong> ${messagesCount || transcript.length}</p> 150 <p><strong>ð Motivo chiusura:</strong> ${endReason || 'Non specificato'}</p> 151 </div> 152 153 <h3>ð¬ Conversazione:</h3> 154 <div style="font-family: Arial, sans-serif; max-width: 600px;"> 155 ${transcript.map(msg => { 156 const messageText = msg.message || msg.content || msg.text || 'Messaggio non disponibile'; 157 const messageType = msg.type === 'assistant' ? 'assistant' : 'user'; 158 const bgColor = messageType === 'user' ? '#e3f2fd' : '#f1f8e9'; 159 const borderColor = messageType === 'user' ? '#2196f3' : '#4caf50'; 160 const textColor = messageType === 'user' ? '#1976d2' : '#388e3c'; 161 const icon = messageType === 'user' ? 'ð¤' : 'ð¤'; 162 const label = messageType === 'user' ? 'Utente' : 'Assistente'; 163 const timestampMsg = msg.timestamp || timestamp; 164 return ` 165 <div style="margin:15px 0; padding:15px; background:${bgColor}; border-radius:8px; border-left:4px solid ${borderColor};"> 166 <div style="display:flex; align-items:center; margin-bottom:8px;"> 167 <strong style="color:${textColor}; font-size:16px;">${icon} ${label}</strong> 168 <small style="color:#666; margin-left:auto; font-size:12px;">${new Date(timestampMsg).toLocaleString('it-IT')}</small> 169 </div> 170 <p style="margin:0; font-size:14px; line-height:1.4; color:#333;">${messageText}</p> 171 </div> 172 `; 173 }).join('')} 174 </div> 175 176 <hr style="margin:30px 0; border: none; border-top: 1px solid #ddd;"> 177 <p style="color:#666; font-size:12px; text-align: center;"> 178 Email generata automaticamente dal sistema TonyAI 179 </p> 180 `; 181 182 console.log('ð§ Attempting to send email...'); 183 const info = await transporterTony.sendMail({ 184 from: process.env.EMAIL_USER, 185 to: process.env.RECEIVER_EMAIL, 186 subject: `ð¬ Conversazione TonyAI - ${new Date(timestamp).toLocaleString('it-IT')}`, 187 html: htmlContent, 188 }); 189 190 console.log('â Email sent successfully:', info.messageId); 191 res.json({ success: true, messageId: info.messageId }); 192 193 } catch (error) { 194 console.error('ð¥ Error sending transcript:', error); 195 res.status(500).json({ success: false, error: error.message }); 196 } 197}); 198 199// -------------------- HEARTBEAT -------------------- 200app.post('/support_ai.html/heartbeat', (req, res) => { 201 res.json({ status: 'ok', timestamp: Date.now() }); 202}); 203 204// Serve static files 205app.use(express.static(__dirname)); 206 207// Error handling middleware 208app.use((err, req, res, next) => { 209 console.error('Unhandled error:', err); 210 res.status(500).json({ success: false, error: 'Internal server error' }); 211}); 212 213// Start server 214app.listen(port, () => { 215 console.log(`ð Server running at http://localhost:${port}/`); 216 console.log(`ð§ Endpoint ticket: http://localhost:${port}/support_ai.html/send-data`); 217 console.log(`ð§ Endpoint chat transcript: http://localhost:${port}/support_ai.html/send-chat-transcript`); 218});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.