[globals] MVPCC_QUEUE_CODE=voice_lab MVPCC_AI_QUEUE_CODE=voice_lab_ai MVPCC_IVR_QUEUE_CODE=voice_lab_ivr MVPCC_IVR_FALLBACK_QUEUE_CODE=voice_lab MVPCC_MONITOR_DIR=/var/spool/asterisk/monitor/mvpcc MVPCC_HUMAN_DIAL_TARGETS=PJSIP/2001&PJSIP/2002 MVPCC_AI_AUDIOSOCKET_SERVICE=127.0.0.1:9024 MVPCC_IVR_FASTAGI_HOSTPORT=127.0.0.1:4573 [from-softphones] exten => 7000,1,NoOp(MVPCC human baseline entrypoint) same => n,Answer() same => n,Set(MVPCC_QUEUE=${MVPCC_QUEUE_CODE}) same => n,Gosub(mvpcc-call-init,s,1(${EXTEN})) same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID})) same => n,Hangup() exten => 7100,1,NoOp(MVPCC AI-first voice lab entrypoint) same => n,Set(MVPCC_QUEUE=${IF($["${MVPCC_AI_QUEUE_OVERRIDE}" != ""]?${MVPCC_AI_QUEUE_OVERRIDE}:${MVPCC_AI_QUEUE_CODE})}) same => n,GotoIf($["${MVPCC_AI_REUSE_CALL}" = "1"]?ai-reuse,1) same => n,Answer() same => n,Gosub(mvpcc-call-init,s,1(${EXTEN})) same => n,Goto(ai-audiosocket,1) exten => ai-reuse,1,NoOp(MVPCC AI handoff reusing active IVR call) same => n,Goto(ai-audiosocket,1) exten => ai-audiosocket,1,NoOp(MVPCC AI audio bridge start) same => n,GotoIf($["${MVPCC_AI_AUDIOSOCKET_SERVICE}" = ""]?ai-human-fallback,1) same => n,Set(CHANNEL(audioreadformat)=slin) same => n,Set(CHANNEL(audiowriteformat)=slin) same => n,Set(MVPCC_MEDIA_UUID=${SHELL(uuidgen | tr -d '\r\n')}) same => n,UserEvent(MVPCCAIAudioBridgeRequested,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL}) same => n,UserEvent(MVPCCAIAudioSocketAttempt,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL}) same => n,TryExec(AudioSocket(${MVPCC_MEDIA_UUID},${MVPCC_AI_AUDIOSOCKET_SERVICE})) same => n,UserEvent(MVPCCAIAudioSocketResult,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},TryExecStatus:${TRYSTATUS},Channel:${CHANNEL}) same => n,GotoIf($["${TRYSTATUS}" = "SUCCESS"]?ai-bridge-ended,1) same => n,UserEvent(MVPCCAIFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Reason:audiosocket_failed,TryExecStatus:${TRYSTATUS},Channel:${CHANNEL}) same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID})) same => n,Hangup() exten => ai-bridge-ended,1,UserEvent(MVPCCAIAudioBridgeEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL}) same => n,Hangup() exten => ai-human-fallback,1,UserEvent(MVPCCAIFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Reason:audiosocket_service_missing,Channel:${CHANNEL}) same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID})) same => n,Hangup() exten => 7200,1,NoOp(MVPCC IVR staging entrypoint) same => n,Answer() same => n,Set(MVPCC_QUEUE=${MVPCC_IVR_QUEUE_CODE}) same => n,Set(MVPCC_IVR_FALLBACK_QUEUE_CODE=${GLOBAL(MVPCC_IVR_FALLBACK_QUEUE_CODE)}) same => n,Gosub(mvpcc-call-init,s,1(${EXTEN})) same => n,Set(MVPCC_IVR_RESULT=started) same => n,Set(MVPCC_IVR_TARGET_QUEUE_CODE=) same => n,Set(MVPCC_IVR_TARGET_EXTENSION=) same => n,Set(MVPCC_IVR_SESSION_ID=) same => n,GotoIf($["${MVPCC_IVR_FASTAGI_HOSTPORT}" = ""]?ivr-human-fallback,1) same => n,AGI(agi://${MVPCC_IVR_FASTAGI_HOSTPORT}/ivr,${MVPCC_CALL_ID},${MVPCC_LINKED_ID},${MVPCC_QUEUE},${MVPCC_IVR_FALLBACK_QUEUE_CODE},${EXTEN}) same => n,GotoIf($["${MVPCC_IVR_TARGET_EXTENSION}" = ""]?ivr-human-fallback,1) same => n,Goto(mvpcc-transfer,${MVPCC_IVR_TARGET_EXTENSION},1) exten => ivr-human-fallback,1,UserEvent(MVPCCIVRFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},FallbackQueueCode:${MVPCC_IVR_FALLBACK_QUEUE_CODE},Extension:${MVPCC_ENTRY_EXTENSION},Result:${MVPCC_IVR_RESULT},TargetQueueCode:${MVPCC_IVR_TARGET_QUEUE_CODE},TargetExtension:${MVPCC_IVR_TARGET_EXTENSION},Channel:${CHANNEL}) same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID})) same => n,Hangup() exten => h,1,StopMixMonitor() same => n,UserEvent(MVPCCCallEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},HangupCause:${HANGUPCAUSE},DurationSeconds:${CDR(duration)}) same => n,UserEvent(MVPCCRecordingReady,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},RemotePath:${MONITOR_FILENAME},FileName:${MVPCC_RECORD_FILE},MimeType:audio/wav,DurationSeconds:${CDR(duration)}) [mvpcc-call-init] exten => s,1,Set(MVPCC_ENTRY_EXTENSION=${ARG1}) same => n,Set(MVPCC_CALL_ID=${UNIQUEID}) same => n,Set(MVPCC_LINKED_ID=${CHANNEL(linkedid)}) same => n,Set(MVPCC_RECORD_FILE=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${UNIQUEID}.wav) same => n,Set(MONITOR_FILENAME=${MVPCC_MONITOR_DIR}/${MVPCC_RECORD_FILE}) same => n,UserEvent(MVPCCCallStarted,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},CallerNumber:${CALLERID(num)},CallerName:${CALLERID(name)},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Direction:inbound,Channel:${CHANNEL}) same => n,MixMonitor(${MONITOR_FILENAME},a) same => n,Return() [mvpcc-connected] exten => s,1,Set(MVPCC_OPERATOR=${IF($["${ARG3}" != ""]?${ARG3}:${CHANNEL(peername)})}) same => n,Set(MVPCC_OPERATOR=${IF($["${MVPCC_OPERATOR}" = ""]?${EXTEN}:${MVPCC_OPERATOR})}) same => n,UserEvent(MVPCCOperatorConnected,CallID:${ARG1},LinkedID:${ARG2},OperatorExtension:${MVPCC_OPERATOR},Context:${CONTEXT},Channel:${CHANNEL},ClaimedByUser:${MVPCC_OPERATOR}) same => n,Return() [mvpcc-claim] exten => _X.,1,NoOp(Claimed call routed to operator ${EXTEN}) same => n,Set(MVPCC_CALL_ID=${CHANNEL(linkedid)}) same => n,Set(MVPCC_LINKED_ID=${CHANNEL(linkedid)}) same => n,Dial(PJSIP/${EXTEN},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}^${EXTEN})) same => n,Hangup() [mvpcc-transfer] exten => 7000,1,NoOp(Queue transfer target ${EXTEN}) same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40) same => n,Hangup() exten => 7100,1,NoOp(Queue transfer target ${EXTEN}) same => n,Goto(from-softphones,7100,1) exten => 7200,1,NoOp(Queue transfer target ${EXTEN}) same => n,Goto(from-softphones,7200,1) exten => _X.,1,NoOp(Blind transfer target ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}^${EXTEN})) same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?transfer-finished,1) same => n,GotoIf($["${MVPCC_IVR_FALLBACK_QUEUE_CODE}" = ""]?transfer-finished,1) same => n,Set(MVPCC_IVR_RESULT=fallback_transfer_unavailable) same => n,Goto(from-softphones,ivr-human-fallback,1) exten => transfer-finished,1,Hangup() exten => h,1,StopMixMonitor() same => n,UserEvent(MVPCCCallEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},HangupCause:${HANGUPCAUSE},DurationSeconds:${CDR(duration)}) same => n,UserEvent(MVPCCRecordingReady,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},RemotePath:${MONITOR_FILENAME},FileName:${MVPCC_RECORD_FILE},MimeType:audio/wav,DurationSeconds:${CDR(duration)}) [mvpcc-operators] exten => _X.,1,Dial(PJSIP/${EXTEN},30) same => n,Hangup() [from-telecom] ; Incoming calls from Telecom.kz SIP account 86205414 / DID 87273901485. ; Route them directly to the AI-first entrypoint 7100. exten => 86205414,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI) same => n,Set(CALLERID(dnid)=87273901485) same => n,Goto(from-softphones,7100,1) exten => 87273901485,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI) same => n,Goto(from-softphones,7100,1) exten => _X.,1,NoOp(Incoming from telecom.kz, DID=${EXTEN}) same => n,Goto(from-softphones,7100,1)