WIP - placing calls from softphone working

This commit is contained in:
Francisco Gaona
2026-01-03 23:31:19 +01:00
parent 801644f396
commit 37b2972231
2 changed files with 84 additions and 67 deletions

View File

@@ -98,20 +98,55 @@ export class VoiceController {
async outboundTwiml(@Req() req: FastifyRequest, @Res() res: FastifyReply) {
const body = req.body as any;
const to = body.To;
const from = body.From;
const callSid = body.CallSid;
this.logger.log(`Outbound call to: ${to}`);
this.logger.log(`=== TwiML OUTBOUND REQUEST RECEIVED ===`);
this.logger.log(`CallSid: ${callSid}, Body From: ${from}, Body To: ${to}`);
this.logger.log(`Full body: ${JSON.stringify(body)}`);
// TwiML to dial the number and setup media stream for OpenAI
const twiml = `<?xml version="1.0" encoding="UTF-8"?>
try {
// Extract tenant domain from Host header
const host = req.headers.host || '';
const tenantDomain = host.split('.')[0]; // e.g., "tenant1" from "tenant1.routebox.co"
this.logger.log(`Extracted tenant domain: ${tenantDomain}`);
// Look up tenant's Twilio phone number from config
let callerId = to; // Fallback (will cause error if not found)
try {
// Get Twilio config to find the phone number
const { config } = await this.voiceService['getTwilioClient'](tenantDomain);
callerId = config.phoneNumber;
this.logger.log(`Retrieved Twilio phone number for tenant: ${callerId}`);
} catch (error: any) {
this.logger.error(`Failed to get Twilio config: ${error.message}`);
}
const dialNumber = to;
this.logger.log(`Using callerId: ${callerId}, dialNumber: ${dialNumber}`);
// Return TwiML to DIAL the phone number with proper callerId
const twiml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Start>
<Stream url="wss://${req.headers.host}/api/voice/stream" />
</Start>
<Say>Connecting your call</Say>
<Dial>${to}</Dial>
<Dial callerId="${callerId}">
<Number>${dialNumber}</Number>
</Dial>
</Response>`;
res.type('text/xml').send(twiml);
this.logger.log(`Returning TwiML with Dial verb - callerId: ${callerId}, to: ${dialNumber}`);
res.type('text/xml').send(twiml);
} catch (error: any) {
this.logger.error(`=== ERROR GENERATING TWIML ===`);
this.logger.error(`Error: ${error.message}`);
this.logger.error(`Stack: ${error.stack}`);
const errorTwiml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>An error occurred while processing your call.</Say>
</Response>`;
res.type('text/xml').send(errorTwiml);
}
}
/**
@@ -154,30 +189,8 @@ export class VoiceController {
const status = body.CallStatus;
const duration = body.CallDuration ? parseInt(body.CallDuration) : undefined;
this.logger.log(`Call status update: ${callSid} -> ${status}`);
// TODO: Extract tenant ID from call record
// For now, we'll need to lookup the call to get tenant ID
// This is a limitation - we should store tenantId in call metadata
try {
// Update call status
// await this.voiceService.updateCallStatus({
// callSid,
// tenantId: 'LOOKUP_NEEDED',
// status,
// duration,
// });
// Notify user via WebSocket
// await this.voiceGateway.notifyCallUpdate(userId, {
// callSid,
// status,
// duration,
// });
} catch (error) {
this.logger.error('Failed to process status webhook', error);
}
this.logger.log(`Call status webhook - CallSid: ${callSid}, Status: ${status}, Duration: ${duration}`);
this.logger.log(`Full status webhook body:`, JSON.stringify(body));
return { success: true };
}
@@ -189,30 +202,11 @@ export class VoiceController {
async recordingWebhook(@Req() req: FastifyRequest) {
const body = req.body as any;
const callSid = body.CallSid;
const recordingUrl = body.RecordingUrl;
const recordingSid = body.RecordingSid;
const recordingStatus = body.RecordingStatus;
this.logger.log(`Recording available for call ${callSid}: ${recordingUrl}`);
// TODO: Update call record with recording URL
// TODO: Trigger transcription if needed
this.logger.log(`Recording webhook - CallSid: ${callSid}, RecordingSid: ${recordingSid}, Status: ${recordingStatus}`);
return { success: true };
}
/**
* WebSocket endpoint for Twilio Media Streams
*/
@Post('stream')
async mediaStream(@Req() req: FastifyRequest, @Res() res: FastifyReply) {
// Twilio Media Streams use WebSocket protocol
// This would need to be handled by the WebSocket server
// In Fastify, we need to upgrade the connection
this.logger.log('Media stream connection requested');
// TODO: Implement WebSocket upgrade for media streams
// This will handle bidirectional audio streaming between Twilio and OpenAI
res.send({ message: 'WebSocket upgrade required' });
}
}