diff --git a/src/ai/ai-chat.controller.ts b/src/ai/ai-chat.controller.ts index 639f896..e2a9d9f 100644 --- a/src/ai/ai-chat.controller.ts +++ b/src/ai/ai-chat.controller.ts @@ -271,7 +271,7 @@ export class AiChatController { inputSchema: z.object({}), execute: async () => { const data = await platformService.queryWithAuth( - `{ calls(first: 100, filter: { callStatus: { eq: MISSED }, callbackstatus: { eq: PENDING_CALLBACK } }) { edges { node { id callerNumber { primaryPhoneNumber } startedAt agentName sla } } } }`, + `{ calls(first: 100, filter: { callStatus: { eq: MISSED }, callbackStatus: { eq: PENDING_CALLBACK } }) { edges { node { id callerNumber { primaryPhoneNumber } startedAt agentName sla } } } }`, undefined, auth, ); const breached = data.calls.edges diff --git a/src/ozonetel/ozonetel-agent.controller.ts b/src/ozonetel/ozonetel-agent.controller.ts index 64f5230..e574056 100644 --- a/src/ozonetel/ozonetel-agent.controller.ts +++ b/src/ozonetel/ozonetel-agent.controller.ts @@ -157,7 +157,7 @@ export class OzonetelAgentController { if (newStatus) { try { await this.platform.query( - `mutation { updateCall(id: "${body.missedCallId}", data: { callbackstatus: ${newStatus} }) { id } }`, + `mutation { updateCall(id: "${body.missedCallId}", data: { callbackStatus: ${newStatus} }) { id } }`, ); } catch (err) { this.logger.warn(`Failed to update missed call status: ${err}`); @@ -191,19 +191,28 @@ export class OzonetelAgentController { @Post('dial') async dial( - @Body() body: { phoneNumber: string; campaignName?: string; leadId?: string }, + @Body() body: { phoneNumber: string; agentId?: string; campaignName?: string; leadId?: string }, ) { if (!body.phoneNumber) { throw new HttpException('phoneNumber required', 400); } - const campaignName = body.campaignName ?? this.telephony.getConfig().ozonetel.campaignName ?? 'Inbound_918041763265'; + const agentId = body.agentId ?? this.defaultAgentId; + const campaignName = body.campaignName + ?? this.telephony.getConfig().ozonetel.campaignName + ?? this.telephony.getConfig().ozonetel.did + ? `Inbound_${this.telephony.getConfig().ozonetel.did}` + : ''; - this.logger.log(`[DIAL] phone=${body.phoneNumber} campaign=${campaignName} agentId=${this.defaultAgentId} lead=${body.leadId ?? 'none'}`); + if (!campaignName) { + throw new HttpException('Campaign name not configured — set in Telephony settings or pass campaignName', 400); + } + + this.logger.log(`[DIAL] phone=${body.phoneNumber} campaign=${campaignName} agentId=${agentId} lead=${body.leadId ?? 'none'}`); try { const result = await this.ozonetelAgent.manualDial({ - agentId: this.defaultAgentId, + agentId, campaignName, customerNumber: body.phoneNumber, }); diff --git a/src/rules-engine/facts/call-facts.provider.ts b/src/rules-engine/facts/call-facts.provider.ts index 3248740..256faa7 100644 --- a/src/rules-engine/facts/call-facts.provider.ts +++ b/src/rules-engine/facts/call-facts.provider.ts @@ -18,10 +18,10 @@ export class CallFactsProvider implements FactProvider { 'call.status': call.callStatus ?? null, 'call.disposition': call.disposition ?? null, 'call.durationSeconds': call.durationSeconds ?? call.durationSec ?? 0, - 'call.callbackStatus': call.callbackstatus ?? call.callbackStatus ?? null, + 'call.callbackStatus': call.callbackStatus ?? call.callbackStatus ?? null, 'call.slaElapsedPercent': slaElapsedPercent, 'call.slaBreached': slaElapsedPercent > 100, - 'call.missedCount': call.missedcallcount ?? call.missedCount ?? 0, + 'call.missedCount': call.missedCallCount ?? call.missedCount ?? 0, 'call.taskType': taskType, }; } diff --git a/src/worklist/missed-call-webhook.controller.ts b/src/worklist/missed-call-webhook.controller.ts index 386e79f..cf922bd 100644 --- a/src/worklist/missed-call-webhook.controller.ts +++ b/src/worklist/missed-call-webhook.controller.ts @@ -147,8 +147,8 @@ export class MissedCallWebhookController { }; // Set callback tracking fields for missed calls so they appear in the worklist if (data.callStatus === 'MISSED') { - callData.callbackstatus = 'PENDING_CALLBACK'; - callData.missedcallcount = 1; + callData.callbackStatus = 'PENDING_CALLBACK'; + callData.missedCallCount = 1; } if (data.recordingUrl) { callData.recording = { primaryLinkUrl: data.recordingUrl, primaryLinkLabel: 'Recording' }; diff --git a/src/worklist/missed-queue.service.ts b/src/worklist/missed-queue.service.ts index b2d613b..e755344 100644 --- a/src/worklist/missed-queue.service.ts +++ b/src/worklist/missed-queue.service.ts @@ -97,19 +97,19 @@ export class MissedQueueService implements OnModuleInit { const existing = await this.platform.query( `{ calls(first: 1, filter: { - callbackstatus: { eq: PENDING_CALLBACK }, + callbackStatus: { eq: PENDING_CALLBACK }, callerNumber: { primaryPhoneNumber: { eq: "${phone}" } } - }) { edges { node { id missedcallcount } } } }`, + }) { edges { node { id missedCallCount } } } }`, ); const existingNode = existing?.calls?.edges?.[0]?.node; if (existingNode) { - const newCount = (existingNode.missedcallcount || 1) + 1; + const newCount = (existingNode.missedCallCount || 1) + 1; const updateParts = [ - `missedcallcount: ${newCount}`, + `missedCallCount: ${newCount}`, `startedAt: "${callTime}"`, - `callsourcenumber: "${did}"`, + `callSourceNumber: "${did}"`, ]; if (leadId) updateParts.push(`leadId: "${leadId}"`); if (leadName) updateParts.push(`leadName: "${leadName}"`); @@ -123,9 +123,9 @@ export class MissedQueueService implements OnModuleInit { `callStatus: MISSED`, `direction: INBOUND`, `callerNumber: { primaryPhoneNumber: "${phone}", primaryPhoneCallingCode: "+91" }`, - `callsourcenumber: "${did}"`, - `callbackstatus: PENDING_CALLBACK`, - `missedcallcount: 1`, + `callSourceNumber: "${did}"`, + `callbackStatus: PENDING_CALLBACK`, + `missedCallCount: 1`, `startedAt: "${callTime}"`, ]; if (leadId) dataParts.push(`leadId: "${leadId}"`); @@ -160,12 +160,12 @@ export class MissedQueueService implements OnModuleInit { // Find oldest unassigned PENDING_CALLBACK call (empty agentName) let result = await this.platform.query( `{ calls(first: 1, filter: { - callbackstatus: { eq: PENDING_CALLBACK }, + callbackStatus: { eq: PENDING_CALLBACK }, agentName: { eq: "" } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { id callerNumber { primaryPhoneNumber } - startedAt callsourcenumber missedcallcount + startedAt callSourceNumber missedCallCount } } } }`, ); @@ -176,12 +176,12 @@ export class MissedQueueService implements OnModuleInit { if (!call) { result = await this.platform.query( `{ calls(first: 1, filter: { - callbackstatus: { eq: PENDING_CALLBACK }, + callbackStatus: { eq: PENDING_CALLBACK }, agentName: { is: NULL } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { id callerNumber { primaryPhoneNumber } - startedAt callsourcenumber missedcallcount + startedAt callSourceNumber missedCallCount } } } }`, ); @@ -209,13 +209,13 @@ export class MissedQueueService implements OnModuleInit { throw new Error(`Invalid status: ${status}. Must be one of: ${validStatuses.join(', ')}`); } - const dataParts: string[] = [`callbackstatus: ${status}`]; + const dataParts: string[] = [`callbackStatus: ${status}`]; if (status === 'CALLBACK_ATTEMPTED') { - dataParts.push(`callbackattemptedat: "${new Date().toISOString()}"`); + dataParts.push(`callbackAttemptedAt: "${new Date().toISOString()}"`); } return this.platform.queryWithAuth( - `mutation { updateCall(id: "${callId}", data: { ${dataParts.join(', ')} }) { id callbackstatus callbackattemptedat } }`, + `mutation { updateCall(id: "${callId}", data: { ${dataParts.join(', ')} }) { id callbackStatus callbackAttemptedAt } }`, undefined, authHeader, ); @@ -230,12 +230,12 @@ export class MissedQueueService implements OnModuleInit { const fields = `id name createdAt direction callStatus agentName callerNumber { primaryPhoneNumber } startedAt endedAt durationSec disposition leadId - callbackstatus callsourcenumber missedcallcount callbackattemptedat`; + callbackStatus callSourceNumber missedCallCount callbackAttemptedAt`; const buildQuery = (status: string) => `{ calls(first: 50, filter: { agentName: { eq: "${agentName}" }, callStatus: { eq: MISSED }, - callbackstatus: { eq: ${status} } + callbackStatus: { eq: ${status} } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { ${fields} } } } }`; try { diff --git a/src/worklist/worklist.service.ts b/src/worklist/worklist.service.ts index 00ededc..ee37b1f 100644 --- a/src/worklist/worklist.service.ts +++ b/src/worklist/worklist.service.ts @@ -97,13 +97,13 @@ export class WorklistService { try { // FIFO ordering (AscNullsLast) — oldest first. No agentName filter — missed calls are a shared queue. const data = await this.platform.queryWithAuth( - `{ calls(first: 20, filter: { callStatus: { eq: MISSED }, callbackstatus: { in: [PENDING_CALLBACK, CALLBACK_ATTEMPTED] } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { + `{ calls(first: 20, filter: { callStatus: { eq: MISSED }, callbackStatus: { in: [PENDING_CALLBACK, CALLBACK_ATTEMPTED] } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { id name createdAt direction callStatus agentName callerNumber { primaryPhoneNumber } startedAt endedAt durationSec disposition leadId - callbackstatus callsourcenumber missedcallcount callbackattemptedat + callbackStatus callSourceNumber missedCallCount callbackAttemptedAt } } } }`, undefined, authHeader,