cat > /tmp/server.js << 'ENDOFFILE' const express = require('express'); const twilio = require('twilio'); const axios = require('axios'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); const PORT = process.env.PORT || 3000; const AI_NODE = 'http://10.10.0.67:8765'; const AI_KEY = 'b1ca95570932bd14ae75426095f436912bb88e6b217712f0811e6501c68df465'; const AI_MODEL = 'llama3.1:70b'; const conversations = {}; const PROPS = {'+12133547300':{name:'broadcastbeat',greeting:'Thank you for calling Broadcast Beat. How can I help you today?',system:'You are the AI receptionist for Broadcast Beat. Be professional. Under 40 words.'},'+12134182600':{name:'pinkpulse',greeting:'Hey gorgeous! You have reached The Pink Pulse. How can I help you today?',system:'You are the AI receptionist for The Pink Pulse, an LGBTQ+ publication. Be warm and fabulous. Under 40 words.'},'+12136529233':{name:'avbeat',greeting:'Thank you for calling AV Beat. How can I help you today?',system:'You are the AI receptionist for AV Beat. Be professional. Under 40 words.'},'+12132977880':{name:'onsethost',greeting:'Thank you for calling OnSetHost. How can I help you today?',system:'You are the AI receptionist for OnSetHost, web hosting for production professionals. Under 40 words.'}}; async function askAI(prompt) { try { const r = await axios.post(AI_NODE+'/queue/add',{prompt,model:AI_MODEL,site:'voiceai',job_type:'text'},{headers:{'x-api-key':AI_KEY},timeout:5000}); const jobId = r.data.job_id; console.log('Job submitted:',jobId); for(let i=0;i<30;i++){ await new Promise(r=>setTimeout(r,500)); const j = await axios.get(AI_NODE+'/jobs',{headers:{'x-api-key':AI_KEY},timeout:3000}); const job = (j.data.jobs||[]).find(x=>x.job_id===jobId); if(job&&job.status==='done'&&job.result){console.log('AI:',job.result.substring(0,80));return job.result.replace(/```json|```/g,'').trim();} } } catch(e){console.error('AI error:',e.message);} return null; } app.post('/voice',async(req,res)=>{ const sid=req.body.CallSid,to=req.body.To; const prop=PROPS[to]||PROPS['+12132977880']; conversations[sid]={prop,history:[]}; console.log('Call to',to,sid); const t=new twilio.twiml.VoiceResponse(); const g=t.gather({input:'speech',action:'https://voice.onsethost.com/respond',method:'POST',speechTimeout:'auto',language:'en-US'}); g.say({voice:'Polly.Joanna-Neural'},prop.greeting); t.hangup(); res.type('text/xml');res.send(t.toString()); }); app.post('/respond',async(req,res)=>{ const sid=req.body.CallSid,speech=req.body.SpeechResult||''; const sess=conversations[sid]||{prop:PROPS['+12132977880'],history:[]}; console.log('Speech:',speech); sess.history.push({role:'user',content:speech}); const hist=sess.history.map(m=>(m.role==='user'?'Caller':'Assistant')+': '+m.content).join('\n'); const prompt=sess.prop.system+'\n\nConversation:\n'+hist+'\n\nRespond as AI receptionist. Under 40 words. No markdown.'; let reply=await askAI(prompt); if(!reply)reply='I apologize for the difficulty. Please try again shortly.'; sess.history.push({role:'assistant',content:reply}); conversations[sid]=sess; console.log('Reply:',reply); const t=new twilio.twiml.VoiceResponse(); const g=t.gather({input:'speech',action:'https://voice.onsethost.com/respond',method:'POST',speechTimeout:'auto',language:'en-US'}); g.say({voice:'Polly.Joanna-Neural'},reply); t.say({voice:'Polly.Joanna-Neural'},'Thank you for calling. Goodbye!'); t.hangup(); res.type('text/xml');res.send(t.toString()); }); app.get('/health',(req,res)=>res.json({status:'online',service:'VoiceAI'})); app.listen(PORT,'0.0.0.0',()=>console.log('VoiceAI running on port '+PORT)); ENDOFFILE