Update voiceai-server.js

This commit is contained in:
2026-04-13 07:07:37 +00:00
parent a7b8297d48
commit 0a433f7ea4

View File

@@ -2,7 +2,6 @@ const express = require('express');
const twilio = require('twilio'); const twilio = require('twilio');
const axios = require('axios'); const axios = require('axios');
const fs = require('fs'); const fs = require('fs');
const path = require('path');
const app = express(); const app = express();
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
@@ -16,8 +15,6 @@ const BRIAN_VOICE_ID = 'Afeo3giYvnyOZXdF5ct9';
const PINK_PULSE_NUMBER = '+12134182600'; const PINK_PULSE_NUMBER = '+12134182600';
const RYAN_CELL = '+18632382563'; const RYAN_CELL = '+18632382563';
const JOSH_CELL = '+19547067488'; const JOSH_CELL = '+19547067488';
const NOTIFY_EMAILS = ['ryan.salazar@thepinkpulse.com', 'josh.menghi@thepinkpulse.com'];
const AI_NODE = 'http://10.10.0.67:8765'; const AI_NODE = 'http://10.10.0.67:8765';
const AI_KEY = 'b1ca95570932bd14ae75426095f436912bb88e6b217712f0811e6501c68df465'; const AI_KEY = 'b1ca95570932bd14ae75426095f436912bb88e6b217712f0811e6501c68df465';
const AI_MODEL = 'deepseek-r1:latest'; const AI_MODEL = 'deepseek-r1:latest';
@@ -27,89 +24,105 @@ const pending = {};
const audioCache = {}; const audioCache = {};
const PROPS = { 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. Keep responses under 40 words.'}, '+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. Keep responses 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. Keep responses 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. Keep responses 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. Keep responses 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. Keep responses under 40 words.' }
}; };
const twilioClient = twilio(TWILIO_SID, TWILIO_AUTH); const twilioClient = twilio(TWILIO_SID, TWILIO_AUTH);
function isBusinessHours() { function isBusinessHours() {
const now = new Date(); const now = new Date();
const et = new Date(now.toLocaleString('en-US', {timeZone: 'America/New_York'})); const et = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' }));
const day = et.getDay(); const day = et.getDay();
const hour = et.getHours(); const hour = et.getHours();
return day >= 1 && day <= 5 && hour >= 9 && hour < 17; return day >= 1 && day <= 5 && hour >= 9 && hour < 17;
} }
async function generateElevenLabsAudio(text, voiceId, cacheKey) { async function generateAudio(text, cacheKey) {
if (audioCache[cacheKey]) return audioCache[cacheKey]; if (audioCache[cacheKey]) return audioCache[cacheKey];
try { try {
const response = await axios.post( const response = await axios({
`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, method: 'post',
{ text, model_id: 'eleven_flash_v2_5', voice_settings: { stability: 0.3, similarity_boost: 0.75, style: 0.5, use_speaker_boost: true } url: 'https://api.elevenlabs.io/v1/text-to-speech/' + BRIAN_VOICE_ID,
{ headers: { 'xi-api-key': ELEVENLABS_KEY, 'Content-Type': 'application/json' }, responseType: 'arraybuffer' } data: {
); text: text,
const filename = `/opt/voiceai/audio/${cacheKey}.mp3`; model_id: 'eleven_flash_v2_5',
voice_settings: {
stability: 0.3,
similarity_boost: 0.75,
style: 0.5,
use_speaker_boost: true
}
},
headers: {
'xi-api-key': ELEVENLABS_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
});
fs.mkdirSync('/opt/voiceai/audio', { recursive: true }); fs.mkdirSync('/opt/voiceai/audio', { recursive: true });
fs.writeFileSync(filename, response.data); fs.writeFileSync('/opt/voiceai/audio/' + cacheKey + '.mp3', response.data);
const url = `https://voice.onsethost.com/audio/${cacheKey}.mp3`; const url = 'https://voice.onsethost.com/audio/' + cacheKey + '.mp3';
audioCache[cacheKey] = url; audioCache[cacheKey] = url;
console.log('Generated audio:', url); console.log('Audio generated:', url);
return url; return url;
} catch (e) { } catch (e) {
console.error('ElevenLabs error:', e.message); console.error('ElevenLabs error:', e.message, e.response && e.response.data ? Buffer.from(e.response.data).toString() : '');
return null; return null;
} }
} }
async function askAI(prompt) { async function askAI(prompt) {
try { 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 r = await axios.post(AI_NODE + '/queue/add', {
prompt: prompt,
model: AI_MODEL,
site: 'voiceai',
job_type: 'text'
}, { headers: { 'x-api-key': AI_KEY }, timeout: 5000 });
const jobId = r.data.job_id; const jobId = r.data.job_id;
for(let i=0;i<120;i++){ console.log('Job submitted:', jobId);
await new Promise(r=>setTimeout(r,500)); for (let i = 0; i < 120; i++) {
const j = await axios.get(AI_NODE+'/jobs',{headers:{'x-api-key':AI_KEY},timeout:3000}); await new Promise(r => setTimeout(r, 500));
const job = (j.data.jobs||[]).find(x=>x.job_id===jobId); const j = await axios.get(AI_NODE + '/jobs', { headers: { 'x-api-key': AI_KEY }, timeout: 3000 });
if(job&&job.status==='done'&&job.result) return job.result.replace(/```json|```/g,'').trim(); const job = (j.data.jobs || []).find(x => x.job_id === jobId);
if (job && job.status === 'done' && job.result) {
return job.result.replace(/```json|```/g, '').trim();
}
} }
} catch(e){console.error('AI error:',e.message);} } catch (e) {
console.error('AI error:', e.message);
}
return null; return null;
} }
async function sendNotifications(callerNumber, transcript, department) { async function sendSMS(to, message) {
const message = `New Pink Pulse voicemail from ${callerNumber}\nDepartment: ${department}\n\nTranscript:\n${transcript}`;
try { try {
await twilioClient.messages.create({ body: message, from: PINK_PULSE_NUMBER, to: RYAN_CELL }); await twilioClient.messages.create({ body: message, from: PINK_PULSE_NUMBER, to: to });
await twilioClient.messages.create({ body: message, from: PINK_PULSE_NUMBER, to: JOSH_CELL }); console.log('SMS sent to', to);
console.log('SMS notifications sent'); } catch (e) {
} catch(e) { console.error('SMS error:', e.message); } console.error('SMS error:', e.message);
}
} }
app.use('/audio', express.static('/opt/voiceai/audio')); app.use('/audio', express.static('/opt/voiceai/audio'));
// ============================================================
// PINK PULSE AUTO-ATTENDANT
// ============================================================
app.post('/pinkpulse', async (req, res) => { app.post('/pinkpulse', async (req, res) => {
const sid = req.body.CallSid; const sid = req.body.CallSid;
conversations[sid] = { department: null, history: [], callerNumber: req.body.From }; conversations[sid] = { department: null, history: [], callerNumber: req.body.From };
console.log('Pink Pulse call from', req.body.From, sid); console.log('Pink Pulse call from', req.body.From, sid);
const greetingText = "Thanks for calling The Pink Pulse, covering over 138 gay communities worldwide! I'm Brian, your fabulous AI assistant. Press 1 for Advertising and Sponsorships. Press 2 for Editorial and Story Tips. Press 3 for Events and Partnerships. Press 4 for General Inquiries. Or stay on the line and I'll connect you with our team!"; const greetingText = "Hey gorgeous! Thanks for calling The Pink Pulse, where your world gets gayed up! I'm Brian, your fabulous AI assistant. Press 1 for Advertising and Sponsorships. Press 2 for Editorial and Story Tips. Press 3 for Events and Partnerships. Press 4 for General Inquiries.";
const audioUrl = await generateAudio(greetingText, 'pp_greeting');
let audioUrl = await generateElevenLabsAudio(greetingText, BRIAN_VOICE_ID, 'pp_greeting');
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
const g = t.gather({ numDigits: 1, action: 'https://voice.onsethost.com/pinkpulse/menu', method: 'POST', timeout: 10 }); const g = t.gather({ numDigits: 1, action: 'https://voice.onsethost.com/pinkpulse/menu', method: 'POST', timeout: 10 });
if (audioUrl) { if (audioUrl) {
g.play(audioUrl); g.play(audioUrl);
} else { } else {
g.say({ voice: 'Polly.Joanna-Neural' }, greetingText); g.say({ voice: 'Polly.Joanna-Neural' }, greetingText);
} }
t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/menu?Digits=4'); t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/menu?Digits=4');
res.type('text/xml'); res.type('text/xml');
res.send(t.toString()); res.send(t.toString());
@@ -119,29 +132,25 @@ app.post('/pinkpulse/menu', async (req, res) => {
const sid = req.body.CallSid; const sid = req.body.CallSid;
const digit = req.body.Digits || '4'; const digit = req.body.Digits || '4';
const sess = conversations[sid] || { department: null, history: [], callerNumber: req.body.From }; const sess = conversations[sid] || { department: null, history: [], callerNumber: req.body.From };
const departments = { '1': 'Advertising and Sponsorships', '2': 'Editorial and Story Tips', '3': 'Events and Partnerships', '4': 'General Inquiries' };
const departments = { '1': 'Advertising & Sponsorships', '2': 'Editorial & Story Tips', '3': 'Events & Partnerships', '4': 'General Inquiries' };
const dept = departments[digit] || 'General Inquiries'; const dept = departments[digit] || 'General Inquiries';
sess.department = dept; sess.department = dept;
conversations[sid] = sess; conversations[sid] = sess;
console.log('Menu selection:', digit, dept); console.log('Menu selection:', digit, dept);
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
if (isBusinessHours()) { if (isBusinessHours()) {
console.log('Business hours - attempting transfer to Ryan'); console.log('Business hours - transferring to Ryan');
const transferText = `Fabulous choice! Let me connect you with our team regarding ${dept}. One moment honey!`; const transferText = 'Fabulous choice honey! Let me connect you with our team right now. One moment!';
let audioUrl = await generateElevenLabsAudio(transferText, BRIAN_VOICE_ID, `pp_transfer_${digit}`); const audioUrl = await generateAudio(transferText, 'pp_transfer_' + digit);
if (audioUrl) t.play(audioUrl); if (audioUrl) t.play(audioUrl);
else t.say({ voice: 'Polly.Joanna-Neural' }, transferText); else t.say({ voice: 'Polly.Joanna-Neural' }, transferText);
const dial = t.dial({ action: 'https://voice.onsethost.com/pinkpulse/transfer?sid=' + sid + '&dept=' + encodeURIComponent(dept), method: 'POST', timeout: 20 });
const dial = t.dial({ action: `https://voice.onsethost.com/pinkpulse/transfer?sid=${sid}&dept=${encodeURIComponent(dept)}`, method: 'POST', timeout: 20 });
dial.number(RYAN_CELL); dial.number(RYAN_CELL);
} else { } else {
console.log('After hours - going to AI message taker'); console.log('After hours - AI message taker');
t.redirect({ method: 'POST' }, `https://voice.onsethost.com/pinkpulse/message?sid=${sid}&dept=${encodeURIComponent(dept)}&attempt=0`); t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/message?sid=' + sid + '&dept=' + encodeURIComponent(dept));
} }
res.type('text/xml'); res.type('text/xml');
@@ -153,26 +162,16 @@ app.post('/pinkpulse/transfer', async (req, res) => {
const dept = req.query.dept || 'General Inquiries'; const dept = req.query.dept || 'General Inquiries';
const dialStatus = req.body.DialCallStatus; const dialStatus = req.body.DialCallStatus;
console.log('Transfer status:', dialStatus); console.log('Transfer status:', dialStatus);
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
if (dialStatus === 'completed') { if (dialStatus === 'completed') {
t.hangup(); t.hangup();
} else { } else {
console.log('Transfer failed, going to AI message taker'); console.log('Transfer failed - going to AI message taker');
const missedText = "Oh no honey, looks like our team is busy right now! But don't worry, I'll take a message and make sure they get back to you. What's your name?";
let audioUrl = await generateElevenLabsAudio(missedText, BRIAN_VOICE_ID, 'pp_missed');
const sess = conversations[sid] || { department: dept, history: [], callerNumber: '' }; const sess = conversations[sid] || { department: dept, history: [], callerNumber: '' };
sess.history = []; sess.history = [];
sess.history.push({ role: 'assistant', content: "What's your name?" });
conversations[sid] = sess; conversations[sid] = sess;
pending[sid] = null; t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/message?sid=' + sid + '&dept=' + encodeURIComponent(dept));
const g = t.gather({ input: 'speech', action: `https://voice.onsethost.com/pinkpulse/collect?sid=${sid}&dept=${encodeURIComponent(dept)}`, method: 'POST', speechTimeout: 'auto' });
if (audioUrl) g.play(audioUrl);
else g.say({ voice: 'Polly.Joanna-Neural' }, missedText);
} }
res.type('text/xml'); res.type('text/xml');
res.send(t.toString()); res.send(t.toString());
}); });
@@ -184,14 +183,13 @@ app.post('/pinkpulse/message', async (req, res) => {
sess.history = []; sess.history = [];
conversations[sid] = sess; conversations[sid] = sess;
const messageText = "Unfortunately, our team isn't available right this moment, but I'll make sure your message gets to someone within minutes! Can I start with your name?"; const messageText = "Hey darling! Our team isn't available right now, but I'm Brian and I'll personally make sure your message gets to the right fabulous person. Can I start with your name?";
let audioUrl = await generateElevenLabsAudio(messageText, BRIAN_VOICE_ID, 'pp_afterhours'); const audioUrl = await generateAudio(messageText, 'pp_afterhours');
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
const g = t.gather({ input: 'speech', action: `https://voice.onsethost.com/pinkpulse/collect?sid=${sid}&dept=${encodeURIComponent(dept)}`, method: 'POST', speechTimeout: 'auto' }); const g = t.gather({ input: 'speech', action: 'https://voice.onsethost.com/pinkpulse/collect?sid=' + sid + '&dept=' + encodeURIComponent(dept), method: 'POST', speechTimeout: 'auto' });
if (audioUrl) g.play(audioUrl); if (audioUrl) g.play(audioUrl);
else g.say({ voice: 'Polly.Joanna-Neural' }, messageText); else g.say({ voice: 'Polly.Joanna-Neural' }, messageText);
res.type('text/xml'); res.type('text/xml');
res.send(t.toString()); res.send(t.toString());
}); });
@@ -201,35 +199,25 @@ app.post('/pinkpulse/collect', async (req, res) => {
const dept = req.query.dept || 'General Inquiries'; const dept = req.query.dept || 'General Inquiries';
const speech = req.body.SpeechResult || ''; const speech = req.body.SpeechResult || '';
const sess = conversations[sid] || { department: dept, history: [], callerNumber: req.body.From || '' }; const sess = conversations[sid] || { department: dept, history: [], callerNumber: req.body.From || '' };
console.log('Collect speech:', speech); console.log('Collect speech:', speech);
sess.history.push({ role: 'user', content: speech }); sess.history.push({ role: 'user', content: speech });
conversations[sid] = sess; conversations[sid] = sess;
const hist = sess.history.map(m => (m.role === 'user' ? 'Caller' : 'Brian') + ': ' + m.content).join('\n'); const hist = sess.history.map(m => (m.role === 'user' ? 'Caller' : 'Brian') + ': ' + m.content).join('\n');
const prompt = `You are Brian, the fabulous AI assistant for The Pink Pulse, an LGBTQ+ entertainment and nightlife media property. You are warm, sassy, inclusive and fun. You are collecting a message from a caller who selected: ${dept}. const prompt = 'You are Brian, the fabulous AI assistant for The Pink Pulse, an LGBTQ+ entertainment and nightlife media publication. You are warm, sassy, inclusive and fun. You are collecting a message from a caller about: ' + dept + '.\n\nYour job is to collect: 1) Their name 2) Their phone number 3) Their message or reason for calling.\n\nConversation so far:\n' + hist + '\n\nIf you have collected all 3 pieces of information, end your response with exactly [COMPLETE]. If not, ask for the next missing piece naturally and fabulously. Keep responses under 50 words. No markdown or special characters.';
Your job is to get: 1) Their name 2) Their phone number 3) Their message/reason for calling pending[sid] = undefined;
Conversation so far:
${hist}
If you have all 3 pieces of information, end your response with [COMPLETE].
If not, ask for the next piece naturally and fabulously.
Keep responses under 50 words. No markdown.`;
pending[sid] = null;
askAI(prompt).then(async reply => { askAI(prompt).then(async reply => {
if (!reply) reply = "Could you repeat that for me darling?"; if (!reply) reply = 'Could you repeat that for me darling?';
const isComplete = reply.includes('[COMPLETE]'); const isComplete = reply.includes('[COMPLETE]');
reply = reply.replace('[COMPLETE]', '').trim(); reply = reply.replace('[COMPLETE]', '').trim();
pending[sid] = { reply, isComplete, hist: hist + '\nBrian: ' + reply }; pending[sid] = { reply: reply, isComplete: isComplete, hist: hist + '\nBrian: ' + reply };
console.log('Brian reply ready:', reply, 'complete:', isComplete); console.log('Brian reply ready:', reply.substring(0, 80), 'complete:', isComplete);
}); });
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
t.pause({ length: 1 }); t.pause({ length: 1 });
t.redirect({ method: 'POST' }, `https://voice.onsethost.com/pinkpulse/collect-check?sid=${sid}&dept=${encodeURIComponent(dept)}&attempt=0`); t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/collect-check?sid=' + sid + '&dept=' + encodeURIComponent(dept) + '&attempt=0');
res.type('text/xml'); res.type('text/xml');
res.send(t.toString()); res.send(t.toString());
}); });
@@ -240,33 +228,33 @@ app.post('/pinkpulse/collect-check', async (req, res) => {
const attempt = parseInt(req.query.attempt) || 0; const attempt = parseInt(req.query.attempt) || 0;
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
if (pending[sid] && pending[sid].reply) { if (pending[sid] !== undefined && pending[sid] !== null && typeof pending[sid] === 'object') {
const { reply, isComplete, hist } = pending[sid]; const data = pending[sid];
delete pending[sid]; delete pending[sid];
const audioUrl = await generateAudio(data.reply, 'pp_collect_' + sid + '_' + attempt);
let audioUrl = await generateElevenLabsAudio(reply, BRIAN_VOICE_ID, `pp_collect_${sid}_${attempt}`); if (data.isComplete) {
if (isComplete) {
const sess = conversations[sid] || { callerNumber: '' }; const sess = conversations[sid] || { callerNumber: '' };
await sendNotifications(sess.callerNumber, hist, dept); const notification = 'New Pink Pulse message from ' + sess.callerNumber + '\nDepartment: ' + dept + '\n\nTranscript:\n' + data.hist;
await sendSMS(RYAN_CELL, notification);
await sendSMS(JOSH_CELL, notification);
const byeText = "Fabulous! I've got all your details and our team will be in touch soon. Thanks for calling The Pink Pulse gorgeous. Have a fierce day!"; const byeText = 'Fabulous! I have got everything I need. Our team will be in touch soon. Thanks for calling The Pink Pulse gorgeous. Have a fierce day!';
let byeUrl = await generateElevenLabsAudio(byeText, BRIAN_VOICE_ID, `pp_bye_${sid}`); const byeUrl = await generateAudio(byeText, 'pp_bye_' + sid);
if (byeUrl) t.play(byeUrl); if (byeUrl) t.play(byeUrl);
else t.say({ voice: 'Polly.Joanna-Neural' }, byeText); else t.say({ voice: 'Polly.Joanna-Neural' }, byeText);
t.hangup(); t.hangup();
} else { } else {
const sess = conversations[sid] || { department: dept, history: [], callerNumber: '' }; const sess = conversations[sid] || { department: dept, history: [], callerNumber: '' };
sess.history.push({ role: 'assistant', content: reply }); sess.history.push({ role: 'assistant', content: data.reply });
conversations[sid] = sess; conversations[sid] = sess;
const g = t.gather({ input: 'speech', action: 'https://voice.onsethost.com/pinkpulse/collect?sid=' + sid + '&dept=' + encodeURIComponent(dept), method: 'POST', speechTimeout: 'auto' });
const g = t.gather({ input: 'speech', action: `https://voice.onsethost.com/pinkpulse/collect?sid=${sid}&dept=${encodeURIComponent(dept)}`, method: 'POST', speechTimeout: 'auto' });
if (audioUrl) g.play(audioUrl); if (audioUrl) g.play(audioUrl);
else g.say({ voice: 'Polly.Joanna-Neural' }, reply); else g.say({ voice: 'Polly.Joanna-Neural' }, data.reply);
} }
} else if (attempt < 20) { } else if (attempt < 30) {
t.pause({ length: 1 }); t.pause({ length: 1 });
t.redirect({ method: 'POST' }, `https://voice.onsethost.com/pinkpulse/collect-check?sid=${sid}&dept=${encodeURIComponent(dept)}&attempt=${attempt + 1}`); t.redirect({ method: 'POST' }, 'https://voice.onsethost.com/pinkpulse/collect-check?sid=' + sid + '&dept=' + encodeURIComponent(dept) + '&attempt=' + (attempt + 1));
} else { } else {
t.say({ voice: 'Polly.Joanna-Neural' }, 'Thank you for calling The Pink Pulse. Goodbye!'); t.say({ voice: 'Polly.Joanna-Neural' }, 'Thank you for calling The Pink Pulse. Goodbye!');
t.hangup(); t.hangup();
@@ -276,18 +264,11 @@ app.post('/pinkpulse/collect-check', async (req, res) => {
res.send(t.toString()); res.send(t.toString());
}); });
// ============================================================
// OTHER PROPERTIES - AI ATTENDANT
// ============================================================
app.post('/voice', async (req, res) => { app.post('/voice', async (req, res) => {
const sid = req.body.CallSid, to = req.body.To; const sid = req.body.CallSid;
if (to === PINK_PULSE_NUMBER) { const to = req.body.To;
req.url = '/pinkpulse';
return app._router.handle(req, res, () => {});
}
const prop = PROPS[to] || PROPS['+12132977880']; const prop = PROPS[to] || PROPS['+12132977880'];
conversations[sid] = { prop, history: [] }; conversations[sid] = { prop: prop, history: [] };
console.log('Call to', to, sid); console.log('Call to', to, sid);
const t = new twilio.twiml.VoiceResponse(); 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' }); const g = t.gather({ input: 'speech', action: 'https://voice.onsethost.com/respond', method: 'POST', speechTimeout: 'auto', language: 'en-US' });
@@ -298,7 +279,8 @@ app.post('/voice', async (req, res) => {
}); });
app.post('/respond', async (req, res) => { app.post('/respond', async (req, res) => {
const sid = req.body.CallSid, speech = req.body.SpeechResult || ''; const sid = req.body.CallSid;
const speech = req.body.SpeechResult || '';
const sess = conversations[sid] || { prop: PROPS['+12132977880'], history: [] }; const sess = conversations[sid] || { prop: PROPS['+12132977880'], history: [] };
console.log('Speech:', speech); console.log('Speech:', speech);
sess.history.push({ role: 'user', content: speech }); sess.history.push({ role: 'user', content: speech });
@@ -317,7 +299,8 @@ app.post('/respond', async (req, res) => {
}); });
app.post('/check', async (req, res) => { app.post('/check', async (req, res) => {
const sid = req.query.sid, attempt = parseInt(req.query.attempt) || 0; const sid = req.query.sid;
const attempt = parseInt(req.query.attempt) || 0;
const t = new twilio.twiml.VoiceResponse(); const t = new twilio.twiml.VoiceResponse();
if (pending[sid]) { if (pending[sid]) {
const reply = pending[sid]; const reply = pending[sid];