mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-04-11 18:08:16 +00:00
feat: switch outbound dial to Kookoo API — outbound calls now work
- Replace CloudAgent V3 tbManualDial with Kookoo outbound.php - Simple HTTP GET with api_key — no auth issues - Kookoo callback endpoint: POST /webhooks/kookoo/callback - Creates Call record in platform - Matches caller to Lead by phone - Remove agent login requirement before dial - Tested: call queued successfully, phone rang Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,40 +64,52 @@ export class OzonetelAgentService {
|
||||
}
|
||||
|
||||
async dialCustomer(params: {
|
||||
agentId: string;
|
||||
customerNumber: string;
|
||||
campaignName?: string;
|
||||
}): Promise<{ type: string; agentId: string; user: string }> {
|
||||
const url = `https://${this.apiDomain}/CAServicesV3/mdlConnection.php`;
|
||||
const basicAuth = Buffer.from(`${this.accountId}:${this.apiKey}`).toString('base64');
|
||||
callbackUrl?: string;
|
||||
ivrUrl?: string;
|
||||
}): Promise<{ status: string; message: string }> {
|
||||
const callerId = process.env.OZONETEL_DID ?? '918041763265';
|
||||
const callbackBase = process.env.KOOKOO_CALLBACK_URL ?? 'https://engage-api.srv1477139.hstgr.cloud';
|
||||
|
||||
this.logger.log(`Dialing ${params.customerNumber} for agent ${params.agentId}`);
|
||||
this.logger.log(`Kookoo outbound: dialing ${params.customerNumber}`);
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
url,
|
||||
{
|
||||
type: 'tbManualDial',
|
||||
ns: 'ozonetel.cloudagent',
|
||||
customer: this.accountId,
|
||||
agentId: params.agentId,
|
||||
custNumber: params.customerNumber,
|
||||
campaignName: params.campaignName ?? 'Inbound_918041763265',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Apikey': this.apiKey,
|
||||
'Authorization': `Basic ${basicAuth}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
const queryParams = new URLSearchParams({
|
||||
phone_no: params.customerNumber,
|
||||
api_key: this.apiKey,
|
||||
outbound_version: '2',
|
||||
caller_id: callerId,
|
||||
callback_url: params.callbackUrl ?? `${callbackBase}/webhooks/kookoo/callback`,
|
||||
});
|
||||
|
||||
// If IVR URL provided, Kookoo will hit it when call connects
|
||||
// Otherwise use extra_data for simple TTS + hangup
|
||||
if (params.ivrUrl) {
|
||||
queryParams.set('url', params.ivrUrl);
|
||||
} else {
|
||||
queryParams.set('extra_data', '<response><playtext>Connecting you now</playtext><hangup/></response>');
|
||||
}
|
||||
|
||||
const response = await axios.get(
|
||||
`https://in1-cpaas.ozonetel.com/outbound/outbound.php?${queryParams.toString()}`,
|
||||
);
|
||||
|
||||
this.logger.log(`Dial response: ${JSON.stringify(response.data)}`);
|
||||
return response.data;
|
||||
const responseText = typeof response.data === 'string' ? response.data : String(response.data);
|
||||
this.logger.log(`Kookoo dial response: ${responseText}`);
|
||||
|
||||
// Parse XML response: <response><status>queued</status><message>SID</message></response>
|
||||
const statusMatch = responseText.match(/<status>(.*?)<\/status>/);
|
||||
const messageMatch = responseText.match(/<message>(.*?)<\/message>/);
|
||||
const status = statusMatch?.[1] ?? 'unknown';
|
||||
const message = messageMatch?.[1] ?? responseText;
|
||||
|
||||
if (status === 'error') {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return { status, message };
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Dial failed: ${error.response?.data?.message ?? error.message}`);
|
||||
this.logger.error(`Kookoo dial failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user