; =============================
; extensions.conf - Full Ready-to-Deploy
; =============================

[general]
static=yes
writeprotect=no
autofallthrough=yes

; =============================
; Context: WebRTC Call Forwarding with Monitor/Whisper
; =============================
[webrtc-calls]

; WebRTC agent calls customer
exten => _X.,1,NoOp(WebRTC call to customer)
 same => n,Answer()
 same => n,Set(CUSTOMER_CHANNEL=${CHANNEL}) ; Save WebRTC channel for AMI redirect
 same => n,Dial(PJSIP/${EXTEN},30)
 same => n,NoOp(Call ended)
 same => n,Hangup()


;===================================================ZOIPER========================================
[for-zoiper]
exten => +911135401895,1,NoOp(Incoming DID to Zoiper 11001)
 same => n,Dial(PJSIP/11001,30)
 same => n,Hangup()

exten => 1134501895,1,NoOp(Incoming DID to Zoiper 11001 Without +91)
 same => n,Dial(PJSIP/11001,30)

; =============================
; Context: From Agents (dial-in/out)
; =============================
[from-agents]

; --- WebRTC Agents ---
exten => agent1,1,NoOp(WebRTC Agent 1)
 same => n,Dial(PJSIP/agent1,30)
 same => n,Hangup()

; --- Outbound Calls via Numeric Pattern ---
; Prefix with 9 to dial external number
;exten => _9X.,1,NoOp(Outbound dial)
; same => n,Dial(PJSIP/${EXTEN:1}@tata-endpoint,60)
; same => n,Hangup()

; =============================
; Context: From Agents (dial-in/out)
; =============================
[from-agents]

; --- Agent Extensions ---
exten => 1009,1,NoOp(Agent 1009)
 same => n,Dial(PJSIP/1009,30)
 same => n,Hangup()

; =============================
; Context: Internal Calls
; =============================
[internal]

; WebRTC agents
exten => 1001,1,Dial(PJSIP/agent1,30)

; WebRTC agents
exten => agent1,1,Dial(PJSIP/agent1,30)

; Agent extensions
exten => 1009,1,Dial(PJSIP/1009,30)

; Forward GSM call to another WebRTC agent
exten => forward,1,NoOp("Forward request received")
 same => n,Verbose(1,"Triggering Local channel to bridge GSM leg to agent2")
 same => n,Set(GSM_LEG=${CHANNEL(name)})  ; store the GSM leg
 same => n,Set(FWD_ID=${STRFTIME(${EPOCH},,%H%M%S)}-${RAND(1000,9999)})
 same => n,System(echo "${GSM_LEG}" > /tmp/forward-${FWD_ID}.txt)
 same => n,Originate(Local/forward-to-agent2@forwarding-${FWD_ID}/n,,) ; originate **from Asterisk**, not from bridged channel
 same => n,Hangup()

exten => agent2,1,NoOp(Forwarded call to WebRTC agent2)
 same => n,Dial(PJSIP/agent2,30,tT)
 same => n,Hangup()

; Echo Test (SIP2SIP)
exten => 3333,1,NoOp(SIP2SIP Echo Test)
 same => n,Dial(PJSIP/3333@sip2sip,30)
 same => n,Hangup()

exten => 4444,1,NoOp(SIP2SIP Echo Test)
 same => n,Dial(PJSIP/4444@sip2sip,30)
 same => n,Hangup()

exten => 6000,1,Answer()
 same => n,Echo()
 same => n,Hangup()



;================================================ AI OUTDIALER ===============================

; Route the specific 7000 to the MINDSERV AI Bot
exten => 7000,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 same => n,Answer()
 same => n,Wait(1)
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_mindserv.agi)
 same => n,Hangup()


; [ai-bot-outdial]
exten => 7001,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_outdial.agi)
 same => n,Hangup()

; [ai-bot-pre-outdial]
exten => 7002,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_pre_outdial.agi)
 same => n,Hangup()

; [ai-bot-around-outdial]
exten => 7003,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_around_outdial.agi)
 same => n,Hangup()

; [ai-bot-sales-outdial]
exten => 7004,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_sales_outdial.agi)
 same => n,Hangup()

; [ai-bot-cart-outdial]
exten => 7005,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_cart.agi)
 same => n,Hangup()



exten => *43,1,Answer()
    same => n,Wait(1)
    same => n,Echo()
    same => n,Hangup()


[ai-agent]
exten => s,1,NoOp(AI Agent Call Answered)
 same => n,Answer()
 same => n,AGI(ai_agent.php)   ; hand off to PHP AGI script
 ; same => n,Hangup()

[ai]
exten => _X.,1,NoOp(GSM Outbound)
 same => n,Set(CALLEDNUM=${EXTEN_TO_DIAL})
 same => n,Set(MONITOR_FILE=/var/www/html/recordings/${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${CALLERID(num)}-to-${CALLEDNUM}.wav)
 same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
 same => n,MixMonitor(${MONITOR_FILE})
 same => n,Dial(PJSIP/${CALLEDNUM}@trunk,,tT)
 same => n,NoOp(Call completed)
 same => n,Hangup()

[ivr-auto]
exten => _X.,1,NoOp(IVR Auto Call to ${EXTEN})
 same => n,Set(IVR_NUMBER=${EXTEN})
 same => n,Set(PROMPT_LIST=ivr_welcome&ivr_offer)
 same => n,Verbose(1, "PROMPT_LIST is ${PROMPT_LIST}")
 same => n,Dial(Local/play-prompts@play-prompts-dial,,tTg)
 same => n,Hangup()

[play-prompts-dial]
exten => play-prompts,1,NoOp(Playing prompts for callee)
 same => n,While($["${PROMPT_LIST}" != ""])
     same => n,Set(CUR_PROMPT=${CUT(PROMPT_LIST,&,1)})
     same => n,Set(PROMPT_LIST=${CUT(PROMPT_LIST,&,2-)})
     same => n,Playback(${CUR_PROMPT})
 same => n,EndWhile()
 same => n,Return()

; =============================
; Context: Transfer (SIP REFER / AMI)
; =============================
[transfer]
; Any numeric or agent UA transfer
exten => _X.,1,NoOp(Transfer to ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN},30)
 same => n,Hangup()

[from-trunk]
; Pilot number handling
exten => 1135401890,1,NoOp(Incoming call to Pilot)
 same => n,Dial(PJSIP/agent1)    ; send to agent/queue
 same => n,Hangup()

; DID range handling
; exten => _98XXXXXXX,1,NoOp(Incoming call to DID ${EXTEN})
 ; same => n,Dial(PJSIP/agent1)
 ; same => n,Hangup()

exten => _X.,1,NoOp(Incoming call from SIP trunk)
 same => n,Dial(PJSIP/agent1)   ; Ring internal agent
 same => n,Hangup()
 
[from-gsm]
 exten => s,1,NoOp(Incoming call from trunk GSM)
 same => n,Answer()
 same => n,Playback(hello-world)  ; replace with real logic
 same => n,Hangup()

[logdial]
exten => s,1,NoOp(Running logDialStatus.php after hangup)
 same => n,NoOp(DEBUG: Executing logDialStatus.php with args: ${CHANNEL} ${DIALSTATUS} ${MONITOR_FILE} ${CALLERID(num)} ${CALLEDNUM})
 same => n,System(/usr/bin/php /var/www/html/logDialStatus.php "${CHANNEL}" "${DIALSTATUS}" "${RECORDING_FILE}" "${CALLERID(num)}" "${CALLEDNUM}")
 same => n,Return()

; Forwarding context
[forwarding]
; Triggered from WebRTC Forward button
exten => forward-to-agent2,1,NoOp(Forward request received)
 same => n,Verbose(1, "Spawning Local channel to bridge GSM leg to agent2")
 same => n,Set(FWD_ID=${STRFTIME(${EPOCH},,%H%M%S)}-${RAND(1000,9999)})

 ; Save the GSM leg into a temp file for bridging
 same => n,System(echo "${ARG1}" > /tmp/forward-${FWD_ID}.txt)

 ; Originate Local channel from Asterisk core (not bridged channel)
 same => n,Originate(Local/bridge-forward-${FWD_ID}@forwarding,n,,)
 same => n,Hangup()

; Bridge the saved GSM leg to agent2
exten => bridge-forward-.,1,NoOp(Bridging saved GSM leg to agent2)
 same => n,Set(FWD_ID=${EXTEN:15}) ; extract FWD_ID
 same => n,Set(GSM_CHANNEL=${FILE(/tmp/forward-${FWD_ID}.txt)}) ; read GSM leg
 same => n,Verbose(1, "Bridging ${GSM_CHANNEL} to agent2")
 same => n,Bridge(${GSM_CHANNEL},PJSIP/agent2)
 same => n,Hangup()


; =============================
; Context: INCOMING CALL HANDING 
; =============================

[ivr-out]
exten => s,1,NoOp(IVR Outdialer Started for ${ARG1})
 same => n,Answer()
 same => n,NoOp(Playing prompt: ${PROMPT})   ; prints to CLI
 same => n,Playback(${PROMPT})   ; Play the dynamic file passed from PHP
 same => n,Hangup()


[ivr-out-multi]
exten => s,1,NoOp(IVR Outdialer Started for ${ARG1})
 same => n,Answer()
 same => n,NoOp(PROMPTS passed: ${PROMPTS})

 ; --- Play multiple prompts ---
 same => n,Set(PROMPT_ARRAY=${PROMPTS})
 same => n,While($["${PROMPT_ARRAY}" != ""])
    same => n,Set(PROMPT_FILE=${CUT(PROMPT_ARRAY,,1)})
    same => n,NoOp(Playing prompt: ${PROMPT_FILE})
    same => n,Playback(${PROMPT_FILE})
    same => n,Set(PROMPT_ARRAY=${CUT(PROMPT_ARRAY,,2-)})
 same => n,EndWhile()

 ; --- Wait for DTMF ---
 same => n,NoOp(Waiting for user input (DTMF))
 same => n,Read(USERCHOICE,beep,1,,,5)  ; 1 digit, 5 sec timeout

 same => n,NoOp(User pressed: ${USERCHOICE})

 ; --- Route based on DTMF ---
 same => n,GotoIf($["${USERCHOICE}"="1"]?banking)
 same => n,GotoIf($["${USERCHOICE}"="2"]?finance)
 same => n,GotoIf($["${USERCHOICE}"="3"]?sales)

 same => n,Playback(vm-goodbye)   ; Invalid input
 same => n,Hangup()

 ; --- Banking Option ---
 same => n(banking),Playback(custom/banking_prompt)
 same => n,Hangup()

 ; --- Finance Option ---
 same => n(finance),Playback(custom/finance_prompt)
 same => n,Hangup()

 ; --- Sales Option ---
 same => n(sales),Playback(custom/sales_prompt)
 same => n,Hangup()


; =========================================================== SIP TRUNK =======================================================================
; =============================
; Context: OUTGOING CALL HANDING 
; =============================
[trunk-out-241225]
 ; Mobiles (prefix 9)
 exten => _90[6-9]XXXXXXXXX,1,NoOp(Mobile via TRUNK)
  same => n,Set(CALLEDNUM=${EXTEN})
  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=+911135401891|+911135401892|+911135401893|+911135401894|+911135401895)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${RANDOM_CID})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})
  ; ---------------------------------------------------------
  same => n,Set(MONITOR_FILE=/var/www/html/recordings/${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${CALLERID(num)}-to-${CALLEDNUM}.wav)
; Save filename in channel variable
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE}) 
  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)
  same => n,Dial(PJSIP/${EXTEN:1}@trunk,,tT)
  ;same => n,Dial(PJSIP/${EXTEN:1}@100.64.24.17,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})
 ; same => n,Hangup()

; Echo Testing
exten => 6000,1,Answer()
 same => n,Echo()
 same => n,Hangup()


[trunk-out-100126]
exten => _90[6-9]XXXXXXXXX,1,NoOp(Mobile via TRUNK)
 same => n,Set(CALLEDNUM=${EXTEN})
 ; --------------------------------
 ; READ CLI FROM UI (X-CLI HEADER)
 ; --------------------------------
 same => n,Set(UI_CLI=${PJSIP_HEADER(read,X-CLI)})
 same => n,NoOp(UI CLI from X-CLI header: ${UI_CLI})
  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=+911135401891|+911135401892|+911135401893|+911135401894|+911135401895)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${UI_CLI})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})
  ; ---------------------------------------------------------
  same => n,Set(MONITOR_FILE=/var/www/html/recordings/${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${CALLERID(num)}-to-${CALLEDNUM}.wav)
; Save filename in channel variable
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE}) 
  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)
  same => n,Dial(PJSIP/${EXTEN:1}@trunk,,tT)
  ;same => n,Dial(PJSIP/${EXTEN:1}@100.64.24.17,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})
 ; same => n,Hangup()


[trunk-out-140126]
exten => _90[6-9]XXXXXXXXX,1,NoOp(Mobile via TRUNK)
 same => n,Set(CALLEDNUM=${EXTEN})
 ; --------------------------------
 ; READ CLI FROM UI (X-CLI HEADER)
 ; --------------------------------
 same => n,Set(UI_CLI=${PJSIP_HEADER(read,X-CLI)})
 same => n,NoOp(UI CLI from X-CLI header: ${UI_CLI})
  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=+911135401891|+911135401892|+911135401893|+911135401894|+911135401895)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${UI_CLI})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})
  ; ---------------------------------------------------------
  same => n,Set(MONITOR_FILE=/var/www/html/recordings/${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${CALLERID(num)}-to-${CALLEDNUM}.wav)
; Save filename in channel variable
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE}) 
  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)
  same => n,Dial(PJSIP/${EXTEN:1}@trunk,,tT)
  ;same => n,Dial(PJSIP/${EXTEN:1}@100.64.24.17,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})
 ; same => n,Hangup()


[trunk-out-200526]
exten => _90[6-9]XXXXXXXXX,1,NoOp(Mobile via TRUNK)
 same => n,Set(CALLEDNUM=${EXTEN})
 ; --------------------------------
 ; READ CLI FROM UI (X-CLI HEADER)
 ; --------------------------------
 same => n,Set(UI_CLI=${PJSIP_HEADER(read,X-CLI)})
 same => n,NoOp(UI CLI from X-CLI header: ${UI_CLI})

;same => n,Set(CDR_ID=${CUT(FROM_USER,__,2)})
;same => n,Set(LEAD_ID=${CUT(FROM_USER,__,3)})
;same => n,NoOp(FROM ? CDR=${CDR_ID} | LEAD=${LEAD_ID})

same => n,Set(CDR_ID=${PJSIP_HEADER(read,X-CDR-ID)})
same => n,Set(LEAD_ID=0)
same => n,NoOp(FROM X-CDR-ID header: CDR=${CDR_ID} | LEAD=${LEAD_ID})


  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=+911135401891|+911135401892|+911135401893|+911135401894|+911135401895)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${UI_CLI})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})

; -----------------------------------
; Generate recording filename
; -----------------------------------
  same => n,Set(RECORD_BASE=/var/www/html/recordings)
  same => n,Set(RECORD_TS=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
  same => n,Set(LAST10=${CALLEDNUM:-10})
  same => n,Set(RECORD_FILE=call_${LEAD_ID}_${LAST10}_${CDR_ID}.wav)
  same => n,Set(MONITOR_FILE=${RECORD_BASE}/${RECORD_FILE})
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE},ab)

  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)
  same => n,Dial(PJSIP/${EXTEN:1}@trunk,,tT)
  ;same => n,Dial(PJSIP/${EXTEN:1}@100.64.24.17,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})
 ; same => n,Hangup()


[trunk-out]
exten => _90[6-9]XXXXXXXXX,1,NoOp(Mobile via TRUNK)
 same => n,Set(CALLEDNUM=${EXTEN})
 ; --------------------------------
 ; READ CLI FROM UI (X-CLI HEADER)
 ; --------------------------------
 same => n,Set(UI_CLI=${PJSIP_HEADER(read,X-CLI)})
 same => n,NoOp(UI CLI from X-CLI header: ${UI_CLI})

;same => n,Set(CDR_ID=${CUT(FROM_USER,__,2)})
;same => n,Set(LEAD_ID=${CUT(FROM_USER,__,3)})
;same => n,NoOp(FROM ? CDR=${CDR_ID} | LEAD=${LEAD_ID})

same => n,Set(CDR_ID=${PJSIP_HEADER(read,X-CDR-ID)})
same => n,Set(LEAD_ID=0)
same => n,NoOp(FROM X-CDR-ID header: CDR=${CDR_ID} | LEAD=${LEAD_ID})


  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=+911135401891|+911135401892|+911135401893|+911135401894|+911135401895)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${UI_CLI})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})

; -----------------------------------
; Generate recording filename
; -----------------------------------
  same => n,Set(RECORD_BASE=/var/www/html/recordings)
  same => n,Set(RECORD_TS=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
  same => n,Set(LAST10=${CALLEDNUM:-10})
  same => n,Set(RECORD_FILE=call_${LEAD_ID}_${LAST10}_${CDR_ID}.wav)
  same => n,Set(MONITOR_FILE=${RECORD_BASE}/${RECORD_FILE})
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE},ab)

  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)

 ; ---------------------------------------------------------
 ; CRITICAL: open early-media path BEFORE Dial so the agent
 ; hears the carrier's real ringback / busy / SIT tone instead
 ; of silence (or Asterisk's synthetic ringback).
 ; ---------------------------------------------------------
  same => n,Progress()

  same => n,Dial(PJSIP/${EXTEN:1}@trunk,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})

; Carrier responses ? user-perceptible tones (only fire if Dial returned without bridging)
 same => n,Goto(handler-${DIALSTATUS},1)

; ----- Per-status handlers (so agent hears the right thing on failure) -----
exten => handler-BUSY,1,NoOp(Far end was busy)
 same => n,Busy(8)
 same => n,Hangup()

exten => handler-CONGESTION,1,NoOp(Network congestion)
 same => n,Congestion(8)
 same => n,Hangup()

exten => handler-CHANUNAVAIL,1,NoOp(Channel unavailable)
 same => n,Congestion(8)
 same => n,Hangup()

exten => handler-NOANSWER,1,NoOp(No answer)
 same => n,Hangup()

exten => handler-CANCEL,1,NoOp(Call cancelled by caller)
 same => n,Hangup()

exten => handler-ANSWER,1,NoOp(Call answered and completed normally)
 same => n,Hangup()

; Default catch-all
exten => _handler-.,1,NoOp(Unhandled DIALSTATUS: ${EXTEN})
 same => n,Hangup()


; ============================= ISHAN ===========================================================
exten => _80[6-9]XXXXXXXXX,1,NoOp(Mobile via ISHAN TRUNK)
 same => n,Set(CALLEDNUM=${EXTEN})
 ; --------------------------------
 ; READ CLI FROM UI (X-CLI HEADER)
 ; --------------------------------
 same => n,Set(UI_CLI=${PJSIP_HEADER(read,X-CLI)})
 same => n,NoOp(UI CLI from X-CLI header: ${UI_CLI})

same => n,Set(CDR_ID=${PJSIP_HEADER(read,X-CDR-ID)})
same => n,Set(LEAD_ID=0)
same => n,NoOp(FROM X-CDR-ID header: CDR=${CDR_ID} | LEAD=${LEAD_ID})

  ; ------------------------------
  ; Random Caller ID from list
  ; ------------------------------
  same => n,Set(CALLERID_LIST=00917969812707|00917969812708|00917969812709|00917969812710|00917969812711)
  same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
  same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
  same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
  same => n,Set(CALLERID(num)=${UI_CLI})
  same => n,NoOp(Selected random CLI: ${CALLERID(num)})

; -----------------------------------
; Generate recording filename
; -----------------------------------
  same => n,Set(RECORD_BASE=/var/www/html/recordings)
  same => n,Set(RECORD_TS=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
  same => n,Set(LAST10=${CALLEDNUM:-10})
  same => n,Set(RECORD_FILE=call_${LEAD_ID}_${LAST10}_${CDR_ID}.wav)
  same => n,Set(MONITOR_FILE=${RECORD_BASE}/${RECORD_FILE})
  same => n,Set(__RECORDING_FILE=${MONITOR_FILE})
  same => n,MixMonitor(${MONITOR_FILE},ab)

  same => n,Set(CHANNEL(hangup_handler_push)=logdial,s,1)

 ; ---------------------------------------------------------
 ; CRITICAL: open early-media path BEFORE Dial so the agent
 ; hears the carrier's real ringback / busy / SIT tone instead
 ; of silence (or Asterisk's synthetic ringback).
 ; ---------------------------------------------------------
  same => n,Progress()

  same => n,Dial(PJSIP/${EXTEN:1}@trunk2,,tT)
  same => n,Set(DIALSTATUS_LOG=${DIALSTATUS})
  same => n,NoOp(Call completed with status: ${DIALSTATUS})

; Carrier responses -> user-perceptible tones (only fire if Dial returned without bridging)
 same => n,Goto(handler-${DIALSTATUS},1)

; ================================================ END ISHAN ===========================================================

; WebRTC agents
exten => 11001,1,Dial(PJSIP/11001,30)

; WebRTC agents
exten => 11002,1,Dial(PJSIP/11002,30)

; WebRTC agents
exten => 11003,1,Dial(PJSIP/11003,30)


; WebRTC agents
exten => 1001,1,Dial(PJSIP/agent1,30)

; WebRTC agents
exten => agent1,1,Dial(PJSIP/agent1,30)

; Agent extensions
exten => 1009,1,Dial(PJSIP/1009,30)

; Echo Testing
exten => 6000,1,Answer()
 same => n,Echo()
 same => n,Hangup()


; Route the specific 7000 to the MINDSERV AI Bot
; exten => 7000,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 ; same => n,Answer()
 ; same => n,Wait(1)
 ; same => n,Set(CHANNEL(language)=hi)
 ; same => n,EAGI(ai_bot_mindserv.agi)
 ; same => n,Hangup()

exten => 7000,1,NoOp(Inbound AI Call)
 same => n,Answer()
 ; Force the audio formats before starting the script
 same => n,Set(STREAM_FORMATS=slin)
 same => n,Set(CHANNEL(read_format)=slin)
 same => n,Set(CHANNEL(write_format)=slin)
 same => n,EAGI(ai_bot_mindserv.agi)
 same => n,Hangup()

; Route the specific 7000 to the MINDSERV AI Bot
exten => 8000,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 same => n,Answer()
 same => n,Wait(1)
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(test.agi)
 same => n,Hangup()


; Route the specific 9000 to the MINDSERV AI Bot
exten => 9000,1,NoOp(Starting Hinglish AI Voicebot)
 same => n,Answer()
 same => n,Stasis(voicebot_app)
 ; Add a NoOp or a small Wait here to ensure Asterisk doesn't 
 ; instantly drop to Hangup if the bridge is 50ms late.
 same => n,NoOp(Stasis Session Ended) 
 same => n,Hangup()


; Route the specific 9000 to the MINDSERV AI Bot
exten => 9001,1,NoOp(Starting Hinglish AI Voicebot)
 same => n,Answer()
 same => n,Stasis(voicebot_debt)
 same => n,NoOp(Stasis Session Ended) 
 same => n,Hangup()


; Route the specific 9000 to the MINDSERV AI Bot
exten => 9002,1,NoOp(Starting Hinglish AI Voicebot)
 same => n,Answer()
 same => n,Stasis(voicebot_mindserv)
 same => n,NoOp(Stasis Session Ended) 
 same => n,Hangup()


[trunk-out-test]
exten => _0X.,1,NoOp(Outbound call to ${EXTEN})
 same => n,Set(CALLERID(num)=+911135401890)
 same => n,Dial(PJSIP/${EXTEN}@trunk,60)
 same => n,Hangup()


[trunk-in-191125]
exten => _+X.,1,NoOp(INCOMING CALL - Incoming call with + prefix: ${EXTEN})
 same => n,Set(CALLER=${CALLERID(num)})
 same => n,Set(DID=${EXTEN})
 ; Write to separate incoming log file
 same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 same => n,NoOp(Written to incoming_calls.log)
 same => n,AGI(incoming_assign_agent.agi,${CALLER},${DID})
 same => n,Hangup()


[setheaderincoming-191125]
exten => s,1,NoOp(Injecting all headers to agent side)
 same => n,Set(PJSIP_HEADER(add,X-Caller-ID)=${X_CALLER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Lead-ID)=${X_LEAD_ID})
 same => n,Set(PJSIP_HEADER(add,X-Campaign-ID)=${X_CAMPAIGN_ID})
 same => n,Set(PJSIP_HEADER(add,X-Customer-ID)=${X_CUSTOMER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Cdr-ID)=${X_CDR_ID})
 same => n,Return()



[trunk-in]
; -------- ZOIPER DID OVERRIDE FIRST (TOP RULE) --------
;exten => +911135401895,1,NoOp(DIRECT ZOIPER ROUTE CALL FROM +911135401895 TO 11002)
 ;same => n,Dial(PJSIP/11002,30)
 ;same => n,Hangup()

exten => +917965238701,1,NoOp(Incoming Debt AI)
 same => n,Answer()
 same => n,Set(X_LEAD_ID=1)      ; which lead to load from MySQL (optional; defaults to 19635)
 same => n,Stasis(voicebot_debt)
 same => n,Hangup()

; exten => +917965238702,1,Goto(ai-debt-realtime,s,1)

exten => +917965238701,1,NoOp(Inbound Debt Recovery AI call from ${CALLERID(num)})
 same => n,Answer()
 same => n,Wait(1)
 same => n,Set(CHANNEL(language)=hi)
 same => n,Set(CHANNEL(readformat)=slin)
 same => n,Set(CHANNEL(writeformat)=slin)
 same => n,MixMonitor(/tmp/debt_${UNIQUEID}.wav,b)
 same => n,EAGI(ai_bot_debt_outdial.agi)
 same => n,Hangup()

exten => +917965238700,1,NoOp(Incoming call on DID +917965238700)
 same => n,Answer()
 same => n,Playback(custom/mindserv_welcome)   ; play prompt (no extension)
 same => n,Wait(1)
 same => n,Set(__CUSTOMER_CHANNEL=${CHANNEL})
 same => n,Set(LOG_DATE=${STRFTIME(${EPOCH},,%Y-%m-%d)})
 same => n,Set(LOG_FILE=/var/log/asterisk/Mindserv_incoming_${LOG_DATE}.log)
 same => n,System(echo "${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} | DID=+911135402000 | CALLER=${CALLERID(num)} | AGENT=09818711711" >> ${LOG_FILE})
 same => n,Set(CALLERID(num)=+917965238700)
 same => n,Set(CALLERID(name)=Mindserv)
 same => n,Dial(PJSIP/09818711711@trunk,60)
 same => n,NoOp(DIALSTATUS=${DIALSTATUS})
 same => n,NoOp(HANGUPCAUSE=${HANGUPCAUSE})
 same => n,Hangup()

; exten => +917965238702,1,NoOp(DIRECT TATA ROUTE CALL FROM +917965238702 TO agent2)
 ;same => n,Set(CALLER=${CALLERID(num)})
 ;same => n,Set(DID=${EXTEN})
 ;same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 ;same => n,NoOp(Written to incoming_calls.log)
 ; same => n,Dial(PJSIP/agent2,30)
 ; same => n,Hangup()


; Route the specific DID +917965238703 to the MINDSERV AI Bot
;exten => +917965238703,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 ;same => n,Answer()
 ; 'voice_bot_app' is the name of the websocket app the vendor will connect to
 ;same => n,Stasis(demo_app)
 ;same => n,Hangup()


; Route the specific DID +917965238701 to the MINDSERV AI Bot
exten => +917965238700,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 same => n,Answer()
 same => n,Wait(1)
 ; same => n,Set(__X_LEAD_ID=${CALLERID(num)})
 ; same => n,Set(__X_LOG_ID=INB_${STRFTIME(${EPOCH},,%Y%m%d%H%M%S)})
 ; same => n,Set(__X_CAMPAIGN_ID=18) ; 18 is your Sales/Mindserv campaign
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_mindserv.agi)
 same => n,NoOp(AI Script finished, DialStatus was ${DIALSTATUS})
 same => n,Hangup()


exten => 00917969812701,1,NoOp(DIRECT ISHAN ROUTE CALL FROM +917969812701 TO agent2)
 same => n,Set(CALLER=${CALLERID(num)})
 same => n,Set(DID=${EXTEN})
 same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 same => n,NoOp(Written to incoming_calls.log)
 ; same => n,Dial(PJSIP/agent2,30)
 ; same => n,Hangup()


; Route the specific DID +917969812700 to the MINDSERV AI Bot
exten => 00917969812700,1,NoOp(Inbound AI Call from ${CALLERID(num)})
 same => n,Answer()
 same => n,Wait(1)
 ; same => n,Set(__X_LEAD_ID=${CALLERID(num)})
 ; same => n,Set(__X_LOG_ID=INB_${STRFTIME(${EPOCH},,%Y%m%d%H%M%S)})
 ; same => n,Set(__X_CAMPAIGN_ID=18) ; 18 is your Sales/Mindserv campaign
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_mindserv.agi)
 same => n,NoOp(AI Script finished, DialStatus was ${DIALSTATUS})
 same => n,Hangup()



exten => +911135401891,1,NoOp(=== ZOIPER INCOMING CALL FROM CUSTOMER: ${CALLERID(num)} === +911135401891 TO 11001)
    same => n,Ringing()
    same => n,Answer()
    same => n,Set(__CUSTOMER_CHANNEL=${CHANNEL})
    same => n,NoOp(Calling agent SIP/11001 ...)
    same => n,Set(LOG_DATE=${STRFTIME(${EPOCH},,%Y-%m-%d)})
    same => n,Set(LOG_FILE=/var/log/asterisk/zoiper_incoming_${LOG_DATE}.log)
    same => n,System(echo "${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} | DID=+911135401891 | CALLER=${CALLERID(num)} | AGENT=11001" >> ${LOG_FILE})
    same => n,Dial(PJSIP/11001,45)
    ;same => n,Voicemail(11001@default,u)
    same => n,Hangup()

exten => +911135401892,1,NoOp(=== ZOIPER INCOMING CALL FROM CUSTOMER: ${CALLERID(num)} === +911135401892 TO 11002)
    same => n,Ringing()
    same => n,Answer()
    same => n,Set(__CUSTOMER_CHANNEL=${CHANNEL})
    same => n,NoOp(Calling agent SIP/11002 ...)
    same => n,Set(LOG_DATE=${STRFTIME(${EPOCH},,%Y-%m-%d)})
    same => n,Set(LOG_FILE=/var/log/asterisk/zoiper_incoming_${LOG_DATE}.log)
    same => n,System(echo "${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} | DID=+911135401892 | CALLER=${CALLERID(num)} | AGENT=11002" >> ${LOG_FILE})
    same => n,Dial(PJSIP/11002,45)
    ;same => n,Voicemail(11002@default,u)
    same => n,Hangup()

exten => +911135401893,1,NoOp(=== ZOIPER INCOMING CALL FROM CUSTOMER: ${CALLERID(num)} === +911135401893 TO 11003)
    same => n,Ringing()
    same => n,Answer()
    same => n,Set(__CUSTOMER_CHANNEL=${CHANNEL})
    same => n,NoOp(Calling agent SIP/11003 ...)
    same => n,Set(LOG_DATE=${STRFTIME(${EPOCH},,%Y-%m-%d)})
    same => n,Set(LOG_FILE=/var/log/asterisk/zoiper_incoming_${LOG_DATE}.log)
    same => n,System(echo "${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} | DID=+911135401893 | CALLER=${CALLERID(num)} | AGENT=11003" >> ${LOG_FILE})
    same => n,Dial(PJSIP/11003,45)
    ;same => n,Voicemail(11003@default,u)
    same => n,Hangup()

; -------- ZOIPER DID OVERRIDE (END) --------

; -- Inbound ? Payment Reminder (Priya) ---------------------------
exten => _+917965238703,1,NoOp(Inbound reminder: ${EXTEN})
 same => n,Answer()
 same => n,Set(X_PHONE=${CALLERID(num)})
 same => n,Stasis(voicebot_reminder)
 same => n,Hangup()

; -- Inbound ? Generic data-driven agent --------------------------
;    set X_AGENT_ID to the agent id you want this DID to use
exten => _+917965238704,1,NoOp(Inbound generic agent: ${EXTEN})
 same => n,Answer()
 same => n,Set(X_PHONE=${CALLERID(num)})
 same => n,Set(X_AGENT_ID=4)
 same => n,Stasis(voicebot_survey)
 same => n,Hangup()

; -- Inbound ? Customer Support (Ishita) --------------------------
exten => _+917965238705,1,NoOp(Inbound support: ${EXTEN})
 same => n,Answer()
 same => n,Set(X_PHONE=${CALLERID(num)})
 same => n,Stasis(voicebot_support)
 same => n,Hangup()

; -- Inbound ? Debt Recovery --------------------------------------
exten => _+917965238702,1,NoOp(Inbound debt: ${EXTEN})
 same => n,Answer()
 same => n,Set(X_PHONE=${CALLERID(num)})
 same => n,Stasis(voicebot_debt)
 same => n,Hangup()

; ============== TESTING FOR VENDOR ===========================
exten => _+917965238716,1,NoOp(VENDOR REQUEST - Inbound call for 652387 series: ${EXTEN})
 same => n,Stasis(demo_app,inbound)
 same => n,Hangup()

exten => _+917965238708,1,NoOp(VENDOR REQUEST - Inbound call for 652387 series: ${EXTEN})
 same => n,Stasis(demo_app,inbound)
 same => n,Hangup()

exten => _+917965238706,1,NoOp(VENDOR REQUEST - Inbound call for 652387 series: ${EXTEN})
 same => n,Stasis(demo_app,inbound)
 same => n,Hangup()
; ============== TESTING FOR VENDOR ===========================

; --- VENDOR + IVR Builder check for +9179652387 series --------------
exten => _+91796523870.,1,NoOp(VENDOR + IVR check for ${EXTEN})
 same => n,Answer()
 same => n,AGI(ivr-inbound.php)
 same => n,GotoIf($["${IVR_HANDLED}" = "1"]?ivr_done)

 ; No IVR campaign � fall through to your vendor Stasis route as before.
 same => n,NoOp(IVR did not match � handing to vendor app)
 same => n,Stasis(demo_app,inbound)
 same => n,Hangup()

 same => n(ivr_done),NoOp(Call handled by IVR Builder)
 same => n,Hangup()
;
; include => from-pstn
;
; =============================== HANDLE INCOMING VENDORS ============================
; 
exten => _+9179652387.,1,NoOp(VENDOR REQUEST - Inbound call for 652387 series: ${EXTEN})
 same => n,Stasis(demo_app,inbound)
 same => n,Hangup()

; =============================== HANDLE INCOMING VENDORS ============================
; 


exten => _+X.,1,NoOp(INCOMING CALL - Incoming call with + prefix: ${EXTEN})
 same => n,Set(CALLER=${CALLERID(num)})
 same => n,Set(DID=${EXTEN})
 same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 same => n,NoOp(Written to incoming_calls.log)

 ; Call AGI
 same => n,AGI(incoming_assign_agent.agi,${CALLER},${DID})

 ; --- FIX START: CHECK IF AGENT IS SET ---
 ; If AGENT is empty or API_RESULT was set to REJECT, go to the handle_error label
 same => n,GotoIf($[${LEN(${AGENT})} = 0]?handle_error)
 same => n,GotoIf($["${API_RESULT}" = "REJECT"]?handle_error)
 ; --- FIX END ---

 ; Dial agent with header injection
 same => n,Dial(PJSIP/${AGENT},,b(setheaderincoming^s^1))
 same => n,Hangup()

 ; --- NEW ERROR HANDLING SECTION ---
 same => n(handle_error),NoOp(API Error or No Agent - Disconnecting Call)
 same => n,Answer()       ; Answer the signaling so the carrier stops retrying
 same => n,Wait(1)         ; Brief pause for stability
 same => n,Playback(ss-noservice) ; Optional: Plays "The number you have dialed is not in service"
 same => n,Hangup()        ; Properly sends the "BYE" signal to the carrier




exten => _+X.,1,NoOp(INCOMING CALL - Incoming call with + prefix: ${EXTEN})
 same => n,Set(CALLER=${CALLERID(num)})
 same => n,Set(DID=${EXTEN})
 same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 same => n,NoOp(Written to incoming_calls.log)

 ; Call AGI
 same => n,AGI(incoming_assign_agent.agi,${CALLER},${DID})

 ; Dial agent with header injection
 same => n,Dial(PJSIP/${AGENT},,b(setheaderincoming^s^1))
 same => n,Hangup()

[setheaderincoming]
exten => s,1,NoOp(Injecting all headers to agent side)
 same => n,Set(PJSIP_HEADER(add,X-Caller-ID)=${X_CALLER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Lead-ID)=${X_LEAD_ID})
 same => n,Set(PJSIP_HEADER(add,X-Campaign-ID)=${X_CAMPAIGN_ID})
 same => n,Set(PJSIP_HEADER(add,X-Customer-ID)=${X_CUSTOMER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Cdr-ID)=${X_CDR_ID})
 same => n,Return()


; =============================
; Context: INCOMING CALL HANDING 
; =============================
[trunk-in-WORKING-2011]
; Match numbers starting with + or digits
exten => _+X.,1,NoOp(Incoming call with + prefix: ${EXTEN})
 same => n,Set(CALLER=${CALLERID(num)})
 same => n,Set(DID=${EXTEN})
 ; Write to separate incoming log file
 same => n,System(echo "$(date '+%Y-%m-%d %H:%M:%S') | DID=${DID} | CALLER=${CALLER}" >> /var/log/asterisk/incoming_calls.log)
 same => n,NoOp(Written to incoming_calls.log)
 same => n,Goto(agent1-route,1)

; Incoming call for 1001
exten => 1001,1,NoOp(Incoming call for 1001)
 ; same => n,Goto(incoming-trunk,agent1-route,1)
 same => n,Goto(agent1-route,1)

; Incoming call for 1001
exten => 1001,1,NoOp(Incoming call for 1001)
 ; same => n,Goto(incoming-trunk,agent1-route,1)
 same => n,Goto(agent1-route,1)

; Add more agents if needed
; exten => 1002,1,NoOp(Incoming call for agent2)
;  same => n,Goto(agent2-route,1)

; Catch-all for any other number
exten => _X.,1,NoOp(Catch-all incoming call)
 ; same => n,Goto(incoming-trunk,agent1-route,1)
 same => n,Hangup()

;-------------------------------
; Invalid extension handler
;-------------------------------
exten => i,1,NoOp(Invalid extension dialed: ${EXTEN} from ${CALLERID(num)})
 same => n,Hangup()

; Local �extension� to dial agent1
exten => agent1-route,1,NoOp(Routing call to WebRTC agent1)
 ; same => n,Answer()
 same => n,Dial(PJSIP/agent1,30)
 same => n,Hangup()

[dialer-context]
; Step 1: Outgoing predictive call to customer
exten => customer-bridge,1,NoOp(Predictive dialing to ${CALLEDNUM} by ${CALLERID(num)})
 same => n,Set(__LEAD_ID=${LEAD_ID})
 same => n,Set(__CUSTOMER_ID=${CUSTOMER_ID})
 same => n,Dial(PJSIP/${CALLEDNUM}@trunk,25)
 same => n,NoOp(Customer did not answer or call ended)
 same => n,Hangup()


; Step 2: When customer answers
exten => answer,1,NoOp(Customer answered � initiating agent bridge)
 same => n,AGI(/var/lib/asterisk/agi-bin/bridge_agent.agi,${CUSTOMER_ID},${LEAD_ID})
 same => n,Hangup()


[predictive-dialer-old]
exten => _X.,1,NoOp(Predictive Dialer calling ${EXTEN})
 same => n,Set(CALLERID(num)=911135401890)
 ; same => n,Dial(PJSIP/${EXTEN}@trunk,20,U(predictive-handler))
 same => n,Dial(PJSIP/${EXTEN}@trunk,30)
 same => n,Hangup()

[predictive-dialer-291025]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(CALLERID(num)=911135401890)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Playback(hello)             ; optional test
 same => n,Dial(PJSIP/agent1)          ; change to your agent endpoint (e.g. PJSIP/agent1)
 same => n,Hangup()

[predictive-dialer-Working-291025]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})
 same => n,Set(CALLERID(num)=911135401890)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Playback(hello)             ; optional test
 same => n,Set(__X_CALLER_ID=${LEADNUMBER})       ; pass variable to child
 same => n,Dial(PJSIP/agent1,,b(setheader^s^1))   ; inject header before call connects
 same => n,Hangup()

[predictive-dialer-Working-291025-1809]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})
; same => n,Set(CALLERID(num)=911135401890)
; ----- RANDOM CLI SELECTION -----
 same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
 same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
 same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
 same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
 same => n,Set(CALLERID(num)=${RANDOM_CID})
 same => n,NoOp(Selected random CLI: ${CALLERID(num)})
;
 same => n,Answer()
 same => n,Wait(1)
 same => n,Playback(hello)             ; optional test
 same => n,Set(__X_CALLER_ID=${LEADNUMBER})       ; pass variable to child
 ; same => n,Dial(PJSIP/agent1,,b(setheader^s^1))   ; inject header before call connects
 same => n,AGI(predictive_bridge.agi)
 same => n,Hangup()


[predictive-dialer-301025]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})
; same => n,Set(CALLERID(num)=911135401890)
; ----- RANDOM CLI SELECTION -----
 same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
 same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
 same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
 same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
 same => n,Set(CALLERID(num)=${RANDOM_CID})
 same => n,NoOp(Selected random CLI: ${CALLERID(num)})
;
 same => n,Answer()
 same => n,Wait(1)
 same => n,Playback(hello)             ; optional test
;
same => n,NoOp(Lead ID: ${LEAD_ID}, Campaign: ${CAMPAIGN_ID}, Customer: ${CUSTOMER_ID})
;
; ---- Set headers to pass to WebRTC agent ----
 same => n,Set(PJSIP_HEADER(add,X-Called-NUMBER)=${LEADNUMBER})
 same => n,Set(PJSIP_HEADER(add,X-Lead-ID)=${LEAD_ID})
 same => n,Set(PJSIP_HEADER(add,X-Campaign-ID)=${CAMPAIGN_ID})
 same => n,Set(PJSIP_HEADER(add,X-Customer-ID)=${CUSTOMER_ID})

 same => n,Set(__X_CALLER_ID=${LEADNUMBER})       ; pass variable to child
 ; same => n,Dial(PJSIP/agent1,,b(setheader^s^1))   ; inject header before call connects
 same => n,AGI(predictive_bridge.agi)
 same => n,Hangup()


[setheader-301025]
exten => s,1,NoOp(Injecting X-Caller-ID to agent side)
 same => n,Set(PJSIP_HEADER(add,X-Caller-ID)=${X_CALLER_ID})
 same => n,Return()


[predictive-dialer-101135-Working]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})

 ; ----- RANDOM CLI SELECTION -----
 same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
 same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
 same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
 same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
 same => n,Set(CALLERID(num)=${RANDOM_CID})
 same => n,NoOp(Selected random CLI: ${CALLERID(num)})

 same => n,Answer()
 same => n,Wait(1)
 same => n,Playback(hello)             ; optional test

 ; ---- Custom variables for lead ----
 same => n,NoOp(Lead ID: ${LEAD_ID}, Campaign: ${CAMPAIGN_ID}, Customer: ${CUSTOMER_ID})
 same => n,Set(__X_CALLER_ID=${LEADNUMBER})
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_CUSTOMER_ID=${CUSTOMER_ID})

 ; ---- Call the PHP bridge ----
 same => n,AGI(predictive_bridge.agi)
 same => n,Set(LAST_STATUS=${DIALSTATUS})             ; fallback for failed calls
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,Hangup()

exten => h,1,NoOp(--- Handling hangup for Lead ${X_LEAD_ID} ---)
 same => n,Set(HANGUP_REASON=${HANGUPCAUSE})
 same => n,Set(HANGUP_TEXT=${HANGUPCAUSE_STRING})
 same => n,Set(LAST_STATUS=${DIALSTATUS})      ; <-- This is where you capture it
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,NoOp(Call ended with cause ${HANGUP_REASON} (${HANGUP_TEXT}), DIALSTATUS=${LAST_STATUS})
 same => n,AGI(hangup.agi,${X_LEAD_ID},${X_CAMPAIGN_ID},${X_CUSTOMER_ID},${X_CALLER_ID},${LAST_STATUS},${HANGUP_REASON},${HANGUP_TEXT})



[predictive-dialer-251125]
exten => _X.,1,NoOp(--- Predictive Dialer started for ${EXTEN} ---)
    same => n,Set(LEADNUMBER=${EXTEN})

    ; -------- RANDOM CLI SELECTION --------
    same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
    same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
    same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
    same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
    same => n,Set(CALLERID(num)=${RANDOM_CID})
    same => n,NoOp(Selected CLI: ${CALLERID(num)})

    ; -------- VARIABLES FROM AMI --------
    same => n,NoOp(LeadID:${LEAD_ID}, Camp:${CAMPAIGN_ID}, Cust:${CUSTOMER_ID}, Cdr:${CDR_ID})
    same => n,Set(__X_LEAD_ID=${LEAD_ID})
    same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
    same => n,Set(__X_CUSTOMER_ID=${CUSTOMER_ID})
    same => n,Set(__X_CALLER_ID=${LEADNUMBER})
    same => n,Set(__X_CDR_ID=${CDR_ID})

    ; -------- DO NOT ANSWER THE CALL HERE --------
    ; The called party will answer. Only then AGI will run.

    same => n,NoOp(Waiting for answer from callee ${EXTEN})

    ; If the call answers ? run your AGI bridge
    same => n,AGI(predictive_bridge.agi)

    ; -------- SAVE FINAL DIALSTATUS --------
    same => n,Set(LAST_STATUS=${DIALSTATUS})
    same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
    same => n,NoOp(Final DialStatus = ${LAST_STATUS})

    same => n,Hangup()

; -------- HANGUP CONTEXT --------
exten => h,1,NoOp(--- HANGUP for ${X_LEAD_ID} ---)
    same => n,Set(HANGUP_REASON=${HANGUPCAUSE})
    same => n,Set(HANGUP_TEXT=${HANGUPCAUSE_STRING})
    same => n,Set(LAST_STATUS=${DIALSTATUS})
    same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
    same => n,NoOp(Call ended Cause=${HANGUP_REASON} (${HANGUP_TEXT})  DIALSTATUS=${LAST_STATUS})
    same => n,AGI(hangup.agi,${X_LEAD_ID},${X_CAMPAIGN_ID},${X_CUSTOMER_ID},${X_CDR_ID},${X_CALLER_ID},${LAST_STATUS},${HANGUP_REASON},${HANGUP_TEXT})


[predictive-dialer-ishan]
exten => _X.,1,NoOp(Predictive Dialer ISHAN started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})
 same => n,Set(ORIGINAL_CID=${CALLERID(num)})
 same => n,NoOp(The Caller ID sent from PHP is: ${ORIGINAL_CID})

 ; ----- RANDOM CLI SELECTION -----
 same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
 same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
 same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
 same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
 same => n,Set(CALLERID(num)=${RANDOM_CID})
 same => n,NoOp(Selected random CLI: ${CALLERID(num)})

 ; ---- Custom variables for lead ----
 same => n,NoOp(Lead ID: ${LEAD_ID}, Campaign: ${CAMPAIGN_ID}, Customer: ${CUSTOMER_ID}, , Cdr: ${CDR_ID})
 same => n,Set(__X_CALLER_ID=${LEADNUMBER})
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_CUSTOMER_ID=${CUSTOMER_ID})
 same => n,Set(__X_CDR_ID=${CDR_ID})

; ---- Generate recording filename ----
 same => n,Set(RECORD_BASE=/var/www/html/recordings)
 same => n,Set(RECORD_TS=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
 same => n,Set(LAST10=${LEADNUMBER:-10})
 same => n,Set(RECORD_FILE=D_call_${LEAD_ID}_${LAST10}_${CDR_ID}.wav)


 same => n,Answer()
 same => n,Wait(1)
 ; ---- START RECORDING (BOTH SIDES) ----
 same => n,MixMonitor(${RECORD_BASE}/${RECORD_FILE},ab)
 same => n,Playback(hello)             ; optional test

 ; ---- Call the PHP bridge (answered calls) ----
 same => n,AGI(predictive_bridge.agi)

 ; ---- Ensure LAST_STATUS is always set, even for failed calls ----
 same => n,Set(LAST_STATUS=${DIALSTATUS})
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,Hangup()

; ---- Handle hangup after channel termination (all calls) ----
exten => h,1,NoOp(--- Handling hangup for Lead ${X_LEAD_ID} ---)
 same => n,Set(HANGUP_REASON=${HANGUPCAUSE})
 same => n,Set(HANGUP_TEXT=${HANGUPCAUSE_STRING})
 same => n,Set(LAST_STATUS=${DIALSTATUS})
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,NoOp(Call ended with cause ${HANGUP_REASON} (${HANGUP_TEXT}), DIALSTATUS=${LAST_STATUS})

 ; ---- Stop recording explicitly (safe) ----
 same => n,StopMixMonitor()

 same => n,AGI(hangup.agi,${X_LEAD_ID},${X_CAMPAIGN_ID},${X_CUSTOMER_ID},${X_CDR_ID},${X_CALLER_ID},${LAST_STATUS},${HANGUP_REASON},${HANGUP_TEXT})



[predictive-dialer]
exten => _X.,1,NoOp(Predictive Dialer started for ${EXTEN})
 same => n,Set(LEADNUMBER=${EXTEN})
 same => n,Set(ORIGINAL_CID=${CALLERID(num)})
 same => n,NoOp(The Caller ID sent is: ${ORIGINAL_CID})

 ; ----- RANDOM CLI SELECTION -----
 ; same => n,Set(CALLERID_LIST=+911135401893|+911135401894|+911135401895)
 ; same => n,Set(COUNT=$[${FIELDQTY(CALLERID_LIST,|)}])
 ; same => n,Set(RAND_INDEX=$[${RAND(1,${COUNT})}])
 ; same => n,Set(RANDOM_CID=${CUT(CALLERID_LIST,|,${RAND_INDEX})})
 ; same => n,Set(CALLERID(num)=${RANDOM_CID})
 ; same => n,NoOp(Selected random CLI: ${CALLERID(num)})

 ; ---- Custom variables for lead ----
 same => n,NoOp(Lead ID: ${LEAD_ID}, Campaign: ${CAMPAIGN_ID}, Customer: ${CUSTOMER_ID}, , Cdr: ${CDR_ID})
 same => n,Set(__X_CALLER_ID=${LEADNUMBER})
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_CUSTOMER_ID=${CUSTOMER_ID})
 same => n,Set(__X_CDR_ID=${CDR_ID})

; ---- Generate recording filename ----
 same => n,Set(RECORD_BASE=/var/www/html/recordings)
 same => n,Set(RECORD_TS=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
 same => n,Set(LAST10=${LEADNUMBER:-10})
 same => n,Set(RECORD_FILE=D_call_${LEAD_ID}_${LAST10}_${CDR_ID}.wav)


 same => n,Answer()
 same => n,Wait(1)
 ; ---- START RECORDING (BOTH SIDES) ----
 same => n,MixMonitor(${RECORD_BASE}/${RECORD_FILE},ab)
 same => n,Playback(hello)             ; optional test

 ; ---- Call the PHP bridge (answered calls) ----
 same => n,AGI(predictive_bridge.agi)

 ; ---- Ensure LAST_STATUS is always set, even for failed calls ----
 same => n,Set(LAST_STATUS=${DIALSTATUS})
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,Hangup()

; ---- Handle hangup after channel termination (all calls) ----
exten => h,1,NoOp(--- Handling hangup for Lead ${X_LEAD_ID} ---)
 same => n,Set(HANGUP_REASON=${HANGUPCAUSE})
 same => n,Set(HANGUP_TEXT=${HANGUPCAUSE_STRING})
 same => n,Set(LAST_STATUS=${DIALSTATUS})
 same => n,ExecIf($["${LAST_STATUS}"=""]?Set(LAST_STATUS=FAILED))
 same => n,NoOp(Call ended with cause ${HANGUP_REASON} (${HANGUP_TEXT}), DIALSTATUS=${LAST_STATUS})

 ; ---- Stop recording explicitly (safe) ----
 same => n,StopMixMonitor()

 same => n,AGI(hangup.agi,${X_LEAD_ID},${X_CAMPAIGN_ID},${X_CUSTOMER_ID},${X_CDR_ID},${X_CALLER_ID},${LAST_STATUS},${HANGUP_REASON},${HANGUP_TEXT})



[setheader]
exten => s,1,NoOp(Injecting all headers to agent side)
 same => n,Set(PJSIP_HEADER(add,X-Caller-ID)=${X_CALLER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Lead-ID)=${X_LEAD_ID})
 same => n,Set(PJSIP_HEADER(add,X-Campaign-ID)=${X_CAMPAIGN_ID})
 same => n,Set(PJSIP_HEADER(add,X-Customer-ID)=${X_CUSTOMER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Cdr-ID)=${X_CDR_ID})
 same => n,Return()



[macro-predictive-handler]
exten => s,1,NoOp(Predictive Handler Started for ${CALLERID(num)})
 same => n,AGI(/var/lib/asterisk/agi-bin/predictive_bridge.php)
 same => n,Return()

[test-agi]
exten => 600,1,NoOp(Testing PHPAGI Script)
 same => n,AGI(test.agi)
 same => n,Hangup()



; ================================================================
; CLICK-TO-CALL TWO GSM BRIDGE with RANDOM CLI
; ================================================================

;--------------------------------------------
; Context: click2call-bridge
;--------------------------------------------
[click2call-bridge-old]
exten => s,1,NoOp(Click2Call bridge start NUM1=${NUM1} NUM2=${NUM2})
 same => n,Set(ARG2=${NUM2})
 same => n,Set(CMD=U(call-second^s^1^${NUM2}))
 same => n,NoOp(Passing CMD=${CMD})
 same => n,Dial(PJSIP/${NUM1}@trunk,45,${CMD})
 same => n,Hangup()

[call-second-old]
exten => s,1,NoOp(=== GSM1 ANSWERED, calling GSM2: ${ARG3} ===)
 same => n,Dial(PJSIP/${ARG3}@trunk,45,g)   ; ? add 'g' to stay in bridge
 same => n,Return()


[click2call-bridge]
exten => s,1,NoOp(=== Click2Call INIT: ${NUM1} <-> ${NUM2} ===)
 same => n,Set(__NUM1=${NUM1})
 same => n,Set(__NUM2=${NUM2})
 same => n,Set(__OUTCALLERID=${OUTCALLERID})
 same => n,Dial(Local/${NUM1}@outgoing-leg,45,U(callsecond^s^1))
 same => n,Hangup()

[callsecond]
exten => s,1,NoOp(=== First leg answered, calling second leg: ${NUM2} ===)
 same => n,Dial(Local/${NUM2}@outgoing-leg,45)
 same => n,Return()

[outgoing-leg]
exten => _X.,1,NoOp(Dialing outbound via trunk to ${EXTEN} using CLI ${OUTCALLERID})
 same => n,Set(CALLERID(num)=${OUTCALLERID})
 same => n,Set(PJSIP_HEADER(add,P-Asserted-Identity)="<sip:${OUTCALLERID}@ai.mindserv.co>")
 same => n,Set(PJSIP_HEADER(add,Remote-Party-ID)="${OUTCALLERID}")
 same => n,Dial(PJSIP/${EXTEN}@trunk,45)
 same => n,Hangup()


;======================================================== SUPERVISOR TRANSFER BERGE-IN ==================================
[agent-transfer]
exten => _X.,1,NoOp(Transferring to agent ${EXTEN})
  same => n,Dial(PJSIP/${EXTEN},30)
  same => n,Hangup()

[spy-context]
exten => s,1,NoOp(=== WebRTC Supervisor Spy ===)
 same => n,Answer()
 same => n,Set(CHANNEL(musicclass)=none)

 same => n,NoOp(Original Target = ${SPY_TARGET})
 same => n,NoOp(Mode = ${SPY_MODE})

 same => n,GotoIf($["${SPY_TARGET}"=""]?hangup)
 same => n,GotoIf($["${SPY_MODE}"=""]?hangup)

 ; ====================================================
 ; Detect correct channel to spy
 ; For BARGE ? spy BRIDGE PEER if exists
 ; ====================================================

 same => n,Set(SPY_CHAN=${SPY_TARGET})

 ; If channel is bridged, spy the bridge peer
 same => n,ExecIf($["${BRIDGEPEER(${SPY_TARGET})}"!=""]?Set(SPY_CHAN=${BRIDGEPEER(${SPY_TARGET})}))

 same => n,NoOp(Resolved Spy Channel = ${SPY_CHAN})

 ; ====================================================
 ; Mode routing
 ; ====================================================

 same => n,GotoIf($["${SPY_MODE}"="listen"]?listen)
 same => n,GotoIf($["${SPY_MODE}"="whisper"]?whisper)
 same => n,GotoIf($["${SPY_MODE}"="barge"]?barge)

 same => n(hangup),Hangup()

; ================= LISTEN =================

same => n(listen),NoOp(Listen Mode)
 same => n,ChanSpy(${SPY_CHAN},qE)
 same => n,Hangup()

; ================= WHISPER =================

same => n(whisper),NoOp(Whisper Mode)
 same => n,ChanSpy(${SPY_CHAN},qwE)
 same => n,Hangup()

; ================= BARGE =================

same => n(barge),NoOp(Barge Mode)
 same => n,ChanSpy(${SPY_CHAN},qwbBE)
 same => n,Hangup()


[spy-context-working-110126]
exten => s,1,NoOp(=== WebRTC Supervisor Spy ===)
 same => n,Answer()
 same => n,Set(CHANNEL(musicclass)=none)
 same => n,NoOp(Target=${SPY_TARGET})
 same => n,NoOp(Mode=${SPY_MODE})
 same => n,GotoIf($["${SPY_TARGET}"=""]?hangup)
 same => n,GotoIf($["${SPY_MODE}"=""]?hangup)

 ; LISTEN
 same => n,GotoIf($["${SPY_MODE}"="listen"]?listen)
 ; WHISPER
 same => n,GotoIf($["${SPY_MODE}"="whisper"]?whisper)
 ; BARGE
 same => n,GotoIf($["${SPY_MODE}"="barge"]?barge)
 same => n(hangup),Hangup()

same => n(listen),NoOp(Listen Mode)
 same => n,ChanSpy(${SPY_TARGET},lqE)       ; listen only, quiet, exit
 same => n,Hangup()

same => n(whisper),NoOp(Whisper Mode)
 same => n,ChanSpy(${SPY_TARGET},wqE)       ; whisper, quiet, exit
 same => n,Hangup()

same => n(barge),NoOp(Barge Mode)
 same => n,ChanSpy(${SPY_TARGET},qwbE)       ; barge, quiet, exit
 same => n,Hangup()


[spy-context-old]
exten => s,1,NoOp(=== WebRTC Supervisor Spy ===)
 same => n,Answer()
 same => n,Set(CHANNEL(musicclass)=none)
 same => n,NoOp(Target=${SPY_TARGET})
 same => n,NoOp(Mode=${SPY_MODE})
 ; fail fast if missing
 same => n,GotoIf($["${SPY_TARGET}"=""]?hangup)
 same => n,GotoIf($["${SPY_MODE}"=""]?hangup)

 ; LISTEN
 same => n,GotoIf($["${SPY_MODE}"="listen"]?listen)
 ; WHISPER
 same => n,GotoIf($["${SPY_MODE}"="whisper"]?whisper)
 ; BARGE
 same => n,GotoIf($["${SPY_MODE}"="barge"]?barge)
 same => n(hangup),Hangup()

 same => n(listen),NoOp(Listen Mode)
 same => n,ChanSpy(${SPY_TARGET},lq)
 same => n,Hangup()

 same => n(whisper),NoOp(Whisper Mode)
 same => n,ChanSpy(${SPY_TARGET},wq)
 same => n,Hangup()

 same => n(barge),NoOp(Barge Mode)
 same => n,ChanSpy(${SPY_TARGET},bq)
 same => n,Hangup()


[spy-context-WORKING WITH CHANSPY AGENT-SUPERVISOR.PHP AND supervisor-1.php]
exten => s,1,NoOp(=== WebRTC Supervisor Spy ===)
 same => n,Answer()
 same => n,Set(CHANNEL(musicclass)=none)
 same => n,NoOp(Target=${SPY_TARGET})
 same => n,NoOp(Mode=${SPY_MODE})
 ; hard fail if missing
 same => n,GotoIf($["${SPY_TARGET}"=""]?hangup)
 same => n,GotoIf($["${SPY_MODE}"=""]?hangup)
 ; LISTEN
 same => n,ExecIf($["${SPY_MODE}"="listen"]?ChanSpy(${SPY_TARGET},qE))
 ; WHISPER
 same => n,ExecIf($["${SPY_MODE}"="whisper"]?ChanSpy(${SPY_TARGET},qwE))
 ; BARGE
 same => n,ExecIf($["${SPY_MODE}"="barge"]?ChanSpy(${SPY_TARGET},qBE))
 same => n(hangup),Hangup()


[spy-context-040126]
; SPY_MODE = listen / whisper / barge
exten => s,1,Answer()
 same => n,NoOp(Supervisor joining ${SPY_TARGET} in mode ${SPY_MODE})
 same => n,ExecIf($["${SPY_MODE}"="listen"]?ChanSpy(${SPY_TARGET},l))
 same => n,ExecIf($["${SPY_MODE}"="whisper"]?ChanSpy(${SPY_TARGET},w))
 same => n,ExecIf($["${SPY_MODE}"="barge"]?ChanSpy(${SPY_TARGET},b))
 same => n,Hangup()

[from-internal]
exten => spy,1,Answer()
 same => n,ChanSpy(${ARG1},${ARG2})
 same => n,Hangup()

exten => _agent.,1,NoOp(Dialing ${EXTEN} FROM SUPERVISOR TRASNFER)
 same => n,Dial(PJSIP/${EXTEN},30)
 same => n,Hangup()

exten => agent1,1,Dial(PJSIP/agent1,30)
exten => agent2,1,Dial(PJSIP/agent2,30)
exten => agent3,1,Dial(PJSIP/agent3,30)


; ===============================
; SUPERVISOR TRANSFER HANDLER
; ===============================
[transfer-handler]
exten => s,1,NoOp(=== SUPERVISOR TRANSFER START ===)

 ; ---- DEBUG ----
 same => n,NoOp(CallerID: ${CALLERID(all)})
 same => n,NoOp(Target Agent: ${TARGET_AGENT})

 ; ---- INHERITED METADATA ----
 same => n,Set(__X_CALLER_ID=${CALLERID(num)})
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_CUSTOMER_ID=${CUSTOMER_ID})
 same => n,Set(__X_CDR_ID=${CDR_ID})

 ; ---- CLEAN OLD HEADERS (IMPORTANT) ----
 same => n,Set(PJSIP_HEADER(remove,X-Caller-ID)=)
 same => n,Set(PJSIP_HEADER(remove,X-Lead-ID)=)
 same => n,Set(PJSIP_HEADER(remove,X-Campaign-ID)=)
 same => n,Set(PJSIP_HEADER(remove,X-Customer-ID)=)
 same => n,Set(PJSIP_HEADER(remove,X-Cdr-ID)=)

 ; ---- ADD SIP HEADERS ----
 same => n,Set(PJSIP_HEADER(add,X-Caller-ID)=${X_CALLER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Lead-ID)=${X_LEAD_ID})
 same => n,Set(PJSIP_HEADER(add,X-Campaign-ID)=${X_CAMPAIGN_ID})
 same => n,Set(PJSIP_HEADER(add,X-Customer-ID)=${X_CUSTOMER_ID})
 same => n,Set(PJSIP_HEADER(add,X-Cdr-ID)=${X_CDR_ID})

 ; ---- DIAL NEW AGENT ----
 same => n,Dial(PJSIP/${TARGET_AGENT},30)
 same => n,Hangup()


exten => _agent.,1,NoOp(Dialing ${EXTEN} FROM SUPERVISOR TRASNFER)
 same => n,Dial(PJSIP/${EXTEN},30)
 same => n,Hangup()


;============================================================ END ============================================
;======================================= AI IMPLEMENTATION =================================

[outbound-ai-old]
exten => s,1,NoOp(Starting AI Outdial)
 same => n,Answer()
 ;same => n,AGI(ai_conversation.agi)
 same => n,AGI(ai_conversation_stream.agi)
 same => n,Hangup()

;========================================= BARGE-IN=========================================
; ========================================
;  MONITOR ACTIONS (BARGE-IN SYSTEM)
; ========================================
[monitor-actions]
; --------------------------
; LISTEN ONLY (No audio to agent or customer)
; --------------------------
exten => listen-_,1,NoOp(Listen-only monitor for ${EXTEN:7})
 same => n,ChanSpy(PJSIP/${EXTEN:7},q)
 same => n,Hangup()
; --------------------------
; WHISPER TO AGENT (Customer cannot hear supervisor)
; --------------------------
exten => whisper-_,1,NoOp(Whisper-to-agent for ${EXTEN:8})
 same => n,ChanSpy(PJSIP/${EXTEN:8},Whq)
 same => n,Hangup()
; --------------------------
; FULL BARGE-IN (Supervisor joins 2-way)
; --------------------------
exten => barge-_,1,NoOp(Full Barge-In for ${EXTEN:6})
 same => n,ChanSpy(PJSIP/${EXTEN:6},bq)
 same => n,Hangup()
; --------------------------
; TAKEOVER (Supervisor joins and agent is dropped)
; --------------------------
exten => takeover-_,1,NoOp(Takeover call for ${EXTEN:9})
 same => n,ChanSpy(PJSIP/${EXTEN:9},Bq)
 same => n,Hangup()
; --------------------------
; DISCONNECT AGENT CALL
; --------------------------
exten => disconnect-_,1,NoOp(Disconnect agent call for ${EXTEN:11})
 ; Force hangup on agent's PJSIP channel
 same => n,System(/usr/sbin/asterisk -rx "channel request hangup PJSIP/${EXTEN:11}")
 same => n,Hangup()

;=========================================TRANSFER =========================================

[internal-transfer]
exten => _X.,1,NoOp(Blind Transfer to Agent ${EXTEN})
    same => n,Dial(PJSIP/${EXTEN},30)
    same => n,Hangup()

[attended-transfer]
exten => _X.,1,NoOp(Attended Transfer to Agent ${EXTEN})
    same => n,Dial(PJSIP/${EXTEN},30,Ttr)
    same => n,Hangup()

[transfer-context]
exten => _X.,1,NoOp(TRANSFER from predictive dialer to agent ${EXTEN})
 same => n,Set(__TRANSFERRED_FROM=${CHANNEL})
 same => n,Dial(PJSIP/agent${EXTEN},30)
 same => n,Hangup()

;======================================== IVR CALLING WITH TTS =============================

[ivr-entry]
exten => s,1,Answer()
 same => n,NoOp(Campaign=${CAMPAIGN_ID} Attempt=${ATTEMPT})
 same => n,Set(IVR_PROMPT=${BASE64_DECODE(${IVR_PROMPT_B64})})
 same => n,NoOp(Prompt=${IVR_PROMPT})
 same => n,NoOp(Language=${TTS_LANG})

 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT}",${TTS_LANG})

 same => n,Read(DIGIT,,1,,3,5)
 same => n,NoOp(User pressed ${DIGIT})

 ;same => n,GotoIf($["${DIGIT}"="1"]?opt1)
 ;same => n,GotoIf($["${DIGIT}"="2"]?opt2)
 same => n,GotoIf($[${DIGIT}=1]?ivr-entry,opt1,1)
 same => n,GotoIf($[${DIGIT}=2]?ivr-entry,opt2,1)

 same => n,AGI(elevenlabs_tts.agi,"Galat vikalp chuna gaya hai. Dhanyavaad.",${TTS_LANG})
 same => n,Hangup()

exten => opt1,1,NoOp(Customer pressed 1)
 same => n,Set(IVR_PROMPT_1_B64=${BASE64_DECODE(${IVR_PROMPT_1_B64})})
 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT_1_B64}",${TTS_LANG})
 same => n,Hangup()

exten => opt2,1,NoOp(Customer pressed 2)
 same => n,Set(IVR_PROMPT_2_B64=${BASE64_DECODE(${IVR_PROMPT_2_B64})})
 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT_2_B64}",${TTS_LANG})
 same => n,Hangup()


[ivr-asr]
exten => s,1,Answer()
 same => n,NoOp(Campaign=${CAMPAIGN_ID} Attempt=${ATTEMPT})

 same => n,Set(IVR_PROMPT=${BASE64_DECODE(${IVR_PROMPT_B64})})
 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT}",${TTS_LANG})

 same => n,Record(/tmp/asr_${UNIQUEID}.wav,5,2,k)
 same => n,AGI(asr_whisper.agi,/tmp/asr_${UNIQUEID}.wav)

 ; ?? NORMALIZE CORRECTLY (ORDER MATTERS)
 same => n,Set(ASR_TEXT=${TRIM(${ASR_TEXT})})
 same => n,Set(ASR_TEXT=${TOLOWER(${ASR_TEXT})})

 same => n,NoOp(ASR RESULT="${ASR_TEXT}")

 ; Silence retry
 same => n,GotoIf($[${LEN(${ASR_TEXT})}=0]?retry)

 ; ? FLEXIBLE INTENT MATCHING (NO ^ )
 same => n,GotoIf($[${REGEX("(haan|ha|haanji|yes|pay|karunga|karo)" ${ASR_TEXT})}]?pay)
 same => n,GotoIf($[${REGEX("(nahi|nahin|no|later|abhi nahi)" ${ASR_TEXT})}]?nopay)

 same => n,AGI(elevenlabs_tts.agi,"Aapka jawab samajh nahi aaya. Kripya baad mein koshish karein.",hi)
 same => n,Hangup()


; ---------- RETRY ----------
exten => retry,1,NoOp(ASR retry)
 same => n,AGI(elevenlabs_tts.agi,"Kripya haan ya nahi mein jawab dein.",hi)

 same => n,Record(/tmp/asr_${UNIQUEID}_retry.wav,4,2)
 same => n,AGI(asr_whisper.agi,/tmp/asr_${UNIQUEID}_retry.wav)

 same => n,Set(ASR_TEXT=${TRIM(${ASR_TEXT})})
 same => n,Set(ASR_TEXT=${TOLOWER(${ASR_TEXT})})
 same => n,NoOp(ASR RETRY RESULT="${ASR_TEXT}")

 same => n,GotoIf($[${REGEX("(haan|yes|pay)" ${ASR_TEXT})}]?pay)
 same => n,GotoIf($[${REGEX("(nahi|no|later)" ${ASR_TEXT})}]?nopay)

 same => n,AGI(elevenlabs_tts.agi,"Dhanyavaad. Aap baad mein jama kar sakte hain.",hi)
 same => n,Hangup()


; ---------- PAY ----------
exten => pay,1,Set(IVR_PROMPT=${BASE64_DECODE(${IVR_PROMPT_1_B64})})
 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT}",hi)
 same => n,Set(ASR_DECISION=PAY)
 same => n,Goto(log)


; ---------- NO PAY ----------
exten => nopay,1,Set(IVR_PROMPT=${BASE64_DECODE(${IVR_PROMPT_2_B64})})
 same => n,AGI(elevenlabs_tts.agi,"${IVR_PROMPT}",hi)
 same => n,Set(ASR_DECISION=NO_PAY)
 same => n,Goto(log)


; -------- LOG ----------
exten => log,1,NoOp(Logging ASR decision=${ASR_DECISION} Text="${ASR_TEXT}")
 same => n,Hangup()

;======================================================================================================

[globals]
AI_AGI=/var/lib/asterisk/agi-bin/ai_bot.agi

[outbound-ai-old]
exten => _X.,1,NoOp(Starting AI outbound call to ${EXTEN})
 same => n,Set(CALLERID(num)=+911135401895)
 same => n,Dial(PJSIP/${EXTEN}@trunk,30,b)
 same => n,NoOp(Call answered � launching AI bot)
 same => n,Answer()
 same => n,AGI(ai_bot.agi,start)
 same => n,Hangup()

[outbound-ai]
exten => _X.,1,NoOp(Starting AI outbound call to ${EXTEN})
 same => n,Set(CALLERID(num)=+911135401895)
 ; Dial and RUN AI after answer
 same => n,Dial(PJSIP/${EXTEN}@trunk,30,U(ai-start))
 same => n,Hangup()

; This runs ONLY AFTER the callee answers
[ai-start]
exten => s,1,NoOp(AI BOT ANSWERED)
 same => n,Answer()
 same => n,AGI(ai_bot.agi,start)
 same => n,Hangup()


[start-ai]
exten => s,1,Answer()
 same => n,TalkDetect(set)
 same => n,Set(AI_SESSION=${UNIQUEID})
 same => n,AGI(${AI_AGI},start)
 same => n,WaitExten(3600)

[talk-detect]
exten => s,1,AGI(${AI_AGI},bargein)
 same => n,Return()

[hangup-ai]
exten => s,1,AGI(${AI_AGI},hangup)
 same => n,Return()

[ai-outbound]
exten => start,1,NoOp(AI Outbound Call to ${CUSTOMER_NUMBER})
 same => n,Set(CALLERID(num)=+911135401895)

 ; Raw audio
 same => n,Set(CHANNEL(readformat)=slin)
 same => n,Set(CHANNEL(writeformat)=slin)

 ; ?? Start audiohook
 same => n,MixMonitor(/tmp/ai_${UNIQUEID}.wav,b)

 ; ?? Allow child channels (U/EAGI) to receive audio
 same => n,Set(AUDIOHOOK_INHERIT(MixMonitor)=yes)

 ; Outbound
 same => n,Dial(PJSIP/${CUSTOMER_NUMBER}@trunk,30,U(ai-bot^start^1))

 same => n,Hangup()

[ai-bot]
exten => start,1,NoOp(AI Bot Call Started)
 same => n,Set(CHANNEL(language)=hi)

 ; (optional but safe)
 same => n,Set(CHANNEL(readformat)=slin)
 same => n,Set(CHANNEL(writeformat)=slin)

 same => n,EAGI(ai_bot.agi)
 same => n,Hangup()

;================================================ AI OUTDIALER ===============================
[ai-bot-outdial]
exten => start,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_outdial.agi)
 same => n,Hangup()

[ai-bot-pre-outdial]
exten => start,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_pre_outdial.agi)
 same => n,Hangup()

[ai-bot-around-outdial]
exten => start,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_around_outdial.agi)
 same => n,Hangup()

[ai-bot-sales-outdial]
exten => start,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_sales_outdial.agi)
 same => n,Hangup()

[ai-bot-cart-outdial]
exten => start,1,NoOp(AI Bot Outbound Call Started)
 same => n,Answer()
 same => n,Set(__X_LEAD_ID=${LEAD_ID})
 same => n,Set(__X_VOICE_ID=${VOICE_ID})
 same => n,Set(__X_CAMPAIGN_ID=${CAMPAIGN_ID})
 same => n,Set(__X_LOG_ID=${LOG_ID})
 same => n,Set(CHANNEL(language)=hi)
 same => n,EAGI(ai_bot_cart.agi)
 same => n,Hangup()


;============================================= BROADCAST ===================================
[broadcast-1]
exten => s,1,NoOp(BROADCAST IVR Outdialer Started for ${ARG1})
 same => n,Answer()
 same => n,NoOp(Playing prompt: ${PROMPT})   ; prints to CLI
 same => n,Playback(${PROMPT})   ; Play the dynamic file passed from PHP
 same => n,Hangup()

[broadcast]
exten => s,1,NoOp(BROADCAST IVR Outdialer - Processing Log ID: ${CAMPAIGN_LOG_ID})
 same => n,Set(call_id=${CAMPAIGN_LOG_ID})
 same => n,Set(call_log_id=${CALL_LOG_ID})
 ; Update DB to 'connected' via a small local php trigger
 same => n,Set(res=${SHELL(php /var/www/html/dialserv/broadcast/update_call.php ${call_id} connected ${call_log_id)})
 same => n,Answer()
 same => n,Playback(${PROMPT})
 same => n,Hangup()

; This catches if the user hangs up or the call finishes
exten => h,1,Set(res=${SHELL(php /var/www/html/dialserv/broadcast/update_call.php ${CAMPAIGN_LOG_ID} disconnected)})


; --- Context for Interactive DTMF Campaigns ---
[broadcast-dtmf]
exten => s,1,NoOp(DTMF Broadcast - Campaign: ${CAMPAIGN_ID} - Log: ${CAMPAIGN_LOG_ID})
 ; 1. Initialize IDs
 same => n,Set(call_id=${CAMPAIGN_LOG_ID})
 same => n,Set(call_log_id=${CALL_LOG_ID})
 
 same => n,Answer()

 ; 2. Play greeting and wait for 1 digit
 ; FIXED: Added the full path to the prompt so Read can find the file and won't hangup
 ; same => n,Read(DIGIT,/var/www/html/dialserv/broadcast/uploads/${CUSTOMER_ID}/${PROMPT},1,,1,5)
 same => n,Read(DIGIT,${PROMPT},1,,1,5)

 
 ; 3. Branching logic
 ; If Read failed (file missing) or timed out without a keypress, it goes to hangup
 same => n,GotoIf($[${LEN(${DIGIT})} > 0]?process_digit:hangup_logic)
 
 ; 4. Dynamic Response Mapping
 same => n(process_digit),NoOp(User Pressed Key: ${DIGIT})
 same => n,Set(RESPONSE_FILE=${digit_${DIGIT}})
 
 ; 5. Check if a prompt exists for that specific key
 same => n,GotoIf($["${RESPONSE_FILE}" = ""]?hangup_logic)
 
 ; 6. Play the specific response audio using the full path
 same => n,Playback(/var/www/html/dialserv/broadcast/uploads/${CUSTOMER_ID}/${RESPONSE_FILE})
 
 same => n(hangup_logic),NoOp(Ending DTMF call session)
 same => n,Hangup()

; 7. Hangup Handler
exten => h,1,NoOp(Call Finished - Key Pressed: ${DIGIT})


[broadcast-ivr]
exten => s,1,NoOp(IVR Runtime)

; This dumps all channel variables, caller ID info, and states to the CLI console
exten => s,n,NoOp(Testing IVR Flow ID: ${IVR_FLOW_ID})
; exten => s,n,DumpChan()
; Print Caller ID (Name and Number)
exten => s,n,NoOp(IVR Caller ID Number: ${CALLERID(num)})
exten => s,n,NoOp(IVR Called Number: ${IVR_CALLED_NUMBER})
exten => s,n,NoOp(IVR Campaign ID: ${IVR_CAMPAIGN_ID})
exten => s,n,NoOp(IVR Contact ID: ${IVR_CONTACT_ID})

exten => s,n,AGI(ivr-runtime.php,${IVR_FLOW_ID})
exten => s,n,Hangup()

[from-pstn]
; Catch every DID arriving on the trunk and hand it to ivr-inbound.php.
; The handler resolves the DID -> campaign -> flow_id and then exec-AGIs
; ivr-runtime.php in the same channel.
exten => _X.,1,NoOp(Inbound to ${EXTEN} from ${CALLERID(num)})
exten => _X.,n,Answer()
exten => _X.,n,Set(CHANNEL(language)=en)
exten => _X.,n,AGI(ivr-inbound.php)
exten => _X.,n,Hangup()


;============================================= END =========================================

[ai-debt-realtime]
exten => s,1,NoOp(Realtime Ritu via AudioSocket -> Gemini Live)
 same => n,Answer()
 same => n,Wait(1)
 same => n,Set(ASID=${SHELL(uuidgen | tr -d '\n')})
 same => n,System(/bin/echo '{"phone":"${CALLERID(num)}"}' > /tmp/as_${ASID}.json)
 same => n,AudioSocket(${ASID},127.0.0.1:8090)
 same => n,Hangup()

;============================================= VENDOR MGT ==================================
[demo]
exten => _X.,1,NoOp(Handing Call to Vendor Voice Bot)
 same => n,Answer()
 ; 'voice_bot_app' is the name of the websocket app the vendor will connect to
 same => n,Stasis(demo_app)
 same => n,Hangup()

;============================================ SIP LINK =====================================
[from-acme-240626]
exten => _X.,1,NoOp(Acme outbound to ${EXTEN})
 same => n,Set(GROUP()=acme)
 same => n,GotoIf($[${GROUP_COUNT(acme)} > 3]?limit)
 same => n,Set(CALLERID(num)=00917969812705)        ; DID presented to the carrier
 same => n,Set(CALLERID(name)=00917969812705)
 same => n,Dial(PJSIP/0${EXTEN}@trunk2)              ; 0-prefix destination
 same => n,Set(CDR(userfield)=${DIALSTATUS}/${HANGUPCAUSE})   ; <-- reason
 same => n,Hangup()
 same => n(limit),NoOp(Acme limit reached)
 same => n,Hangup(503)

; =========================================================
; extensions.conf � VENDOR: Acme Telecom
; Outbound: only assigned CallerIDs may dial out (else Hangup 21).
; Inbound : assigned DIDs route to endpoint acme_trunk.
; Paste into extensions.conf, then: asterisk -rx "dialplan reload"
; =========================================================

; ----- Acme Telecom outbound -----
[from-acme-250626]
exten => _X.,1,NoOp(Acme outbound CID=${CALLERID(num)} -> ${EXTEN})
 ;exten => _X.,1,NoOp(Acme Telecom outbound to ${EXTEN})
 ;
 ; --- THIS LINE PRINTS THE DID & DESTINATION IN ALL CASES ---
 same => n,NoOp(--> [ALL CASES LOG] Received Call | Outbound DID/CID: ${CALLERID(num)} | Dialed Destination: ${EXTEN})
 ;same => n,Set(CALLERID(num)=00917969812704)        ; DID presented to the carrier
 ;same => n,Set(CALLERID(name)=00917969812704)
 same => n,NoOp(--> [ALL CASES LOG] Received Call | Outbound DID/CID: ${CALLERID(num)} | Dialed Destination: ${EXTEN})
 same => n,GotoIf($["${DIALPLAN_EXISTS(from-acme-dids,${CALLERID(num)},1)}"="0"]?badcid)

 ; --- THIS LINE PRINTS THE SELECTED/AUTHORIZED DID ---
 same => n,NoOp(--> MATCH SUCCESS: Allowed DID [${CALLERID(num)}] is routing this call)
 same => n,Set(GROUP()=acme_trunk)
 same => n,GotoIf($[${GROUP_COUNT(acme_trunk)} > 2]?limit)
 same => n,Dial(PJSIP/0${EXTEN}@trunk2)
 same => n,Set(CDR(userfield)=${DIALSTATUS}/${HANGUPCAUSE})
 same => n,Hangup()
 same => n(badcid),NoOp(Rejected: CallerID ${CALLERID(num)} is not a Acme Telecom DID)
 same => n,Hangup(21)
 same => n(limit),NoOp(Acme Telecom channel limit (2) reached)
 same => n,Hangup(503)

; ----- allowed outbound CallerIDs (generated) -----
[from-acme-dids]
exten => 00917969812704,1,Return()
exten => 00917969812705,1,Return()

[from-acme]
exten => _X.,1,Set(DID=${CALLERID(num)})                         ; canonical DID from the vendor (via PAI)
 same => n,Set(DID=${IF($["${DID:0:2}"="00"]?${DID:2}:${DID})})  ; defensive: strip a stray leading 00
 same => n,Set(CALLERID(num)=00${DID})                           ; <-- carrier prefix, MindServ side ONLY
 same => n,Set(CALLERID(name)=${CALLERID(num)})
 same => n,NoOp(Acme presenting CID=${CALLERID(num)} -> ${EXTEN})
 same => n,GotoIf($["${DIALPLAN_EXISTS(from-acme-dids,${CALLERID(num)},1)}"="0"]?badcid)
 same => n,Set(GROUP()=acme_trunk)
 same => n,GotoIf($[${GROUP_COUNT(acme_trunk)} > 2]?limit)
 same => n,Dial(PJSIP/0${EXTEN}@trunk2)
 same => n,Set(CDR(userfield)=${DIALSTATUS}/${HANGUPCAUSE})
 same => n,Hangup()
 same => n(badcid),NoOp(Rejected: CallerID ${CALLERID(num)} is not an Acme DID)
 same => n,Hangup(21)
 same => n(limit),NoOp(Acme Telecom channel limit (2) reached)
 same => n,Hangup(503)


; ----- inbound DID routing -----
; Add these lines to your SHARED [from-carrier] context (the context= set on
; the trunk2 trunk endpoint in pjsip.conf). DIDs are globally unique, so each
; vendor's lines simply append to the same context.
[from-carrier]
exten => 917969812805,1,Dial(PJSIP/acme_trunk)
exten => 917969812806,1,Dial(PJSIP/acme_trunk)
; Add ONCE, as the LAST line of [from-carrier]:
; exten => _X.,1,Hangup(1)   ; DID not assigned to any vendor
;
;============================================ SIP LINK =====================================

