globals { USER_FWD=; USER_VOIPJET=; USER_SIPPHONE=; USER_TELIAX=; USER_IAXTEL=; POTS_CID="" <>; LOCAL_AREACODE=; // 2002-2254 -- LAN softphones DEV_2005=SIP/2005; DEV_2073=SIP/2073; DEV_2077=SIP/2077; // 2300-2399 -- Analogue hardphones DEV_2300=SIP/2300; DEV_2301=SIP/2301; // 2400-2499 -- Digital hardphones // 25xx (Individuals) & 27xx (Groups) are time-specific // 2500-2599 -- Individuals DEV_2505=SIP/2300&SIP/2005; DEV_2507=SIP/2077&SIP/2073&SIP/2301; // 2700-2799 -- Groups DEV_2700=SIP/2077&SIP/2073&SIP/2005&SIP/2300&SIP/2301; EMERGENCY=; }; macro replace(s, find, replacement) { // replace arg2 with arg3 in arg1; store result in arg4 // NOTE: ARG2 and ARG3 *must* be exactly one byte! Set(Repl_dst=); // clear any old value for (Repl_field=1; ${LEN(${Repl_dst})} < ${LEN(${s})}; Repl_field=${Repl_field} + 1) { Set(Repl_new=${CUT(s,${find},${Repl_field})}); Set(Repl_dst=${Repl_dst}${replacement}${Repl_new}); }; Repl_var=REPLACED; if ("${ARG4}" != "") Set(Repl_var=${ARG4}); Set(${Repl_var}=${Repl_dst:${LEN(${replacement})}}); }; macro prepcall_beginmonitor(context, exten) { // arg1 = outgoing, incoming, or autodial // arg2 = exten if ("${CALLFILENAME}" == "") { &replace(${CALLERID(num)},/,~,MyCIDNUM); &replace(${MyCIDNUM},_,~,MyCIDNUM); &replace(${exten},_,~,MyEXTEN); &replace(${CHANNEL},/,~,MyCHANNEL); &replace(${MyCHANNEL},_,~,MyCHANNEL); Set(CALLFILENAME=${TIMESTAMP}_${context}_${MyCIDNUM}_${MyEXTEN}_${MyCHANNEL}_${UNIQUEID}); MONITOR_EXEC=soxmix; Monitor(wav,${CALLFILENAME},m); } else NoOp(Already recording to ${CALLFILENAME}!); }; macro prepcall(context, exten) { &prepcall_beginmonitor(${context},${exten}); SetMusicOnHold(default); }; macro iterate(IteratorHandle) { ${IteratorHandle}=${CUT(Iterator_${IteratorHandle}||1)}; // this needs SetVar so dashes aren't treated as subtraction! SetVar(Iterator_${IteratorHandle}=${CUT(Iterator_${IteratorHandle}||2-)}); }; macro iterator(OptList, IteratorHandle) { // this needs SetVar so dashes aren't treated as subtraction! SetVar(Iterator_${IteratorHandle}=${OptList}); &iterate(${IteratorHandle}); }; macro route-fwd(exten, IsPOTS, Timeout, DialOpts) { SetCallerID(${USER_FWD}); SetCIDName(${USER_FWD}); if (${IsPOTS}) { Dial(IAX2/${FWD_NUM}@fwd/\*${exten},${Timeout},${DialOpts}); } else { Dial(IAX2/${USER_FWD}@fwd/${exten},${Timeout},${DialOpts}); }; }; macro route-iconnecthere(exten, IsPOTS, Timeout, DialOpts) { SetCallerID(${POTS_CID}); Dial(SIP/${exten}@iconnecthere,${Timeout},L1800000:300000${DialOpts}); }; macro route-voipmich(exten, IsPOTS, Timeout, DialOpts) { Dial(SIP/${exten}@voipmich,${Timeout},${DialOpts}); }; macro route-voipjet(exten, IsPOTS, Timeout, DialOpts) { Dial(IAX2/${USER_VOIPJET}@voipjet/${exten},${Timeout},L(1800000:300000)${DialOpts}); }; macro route-voxee(exten, IsPOTS, Timeout, DialOpts) { Dial(IAX2/voxee/${exten},${Timeout},L(1800000:300000)${DialOpts}); }; macro route-myphonecompany(exten, IsPOTS, Timeout, DialOpts) { Dial(SIP/${exten}@sipmedia,${Timeout},L(300000:60000)${DialOpts}); }; macro route-sipphone(exten, IsPOTS, Timeout, DialOpts) { // TODO: if IsPOTS, use POTS CID etc SetCallerID(${USER_SIPPHONE}); Dial(SIP/${exten}@sipphone,${Timeout},${DialOpts}); }; macro route-teliax(exten, IsPOTS, Timeout, DialOpts) { Dial(IAX2/${USER_TELIAX}@teliax/${exten},${Timeout},${DialOpts}); }; macro route-iaxtel(exten, IsPOTS, Timeout, DialOpts) { Dial(IAX2/${USER_IAXTEL}@iaxtel.com/${exten}@iaxtel,${Timeout},${DialOpts}); }; macro route-enum(exten) { // TODO // Perhaps based on http://www.voip-info.org/wiki/view/RFC+Compliant+ENUM+Macro // Note we should follow 'tel:' redirects only via a subsequent enum lookup DIALSTATUS=NoENUM }; macro route-home-person(exten) { Dial(${DEV_${exten}},20,rt); switch(${DIALSTATUS}) { case BUSY: Voicemail(b${exten}@local); break; default: Voicemail(u${exten}@local); }; catch a { VoiceMailMain(${exten}); }; Hangup(); }; macro route-home-location(exten) { Dial(${DEV_${exten}},30,rt); Playback(vm-nobodyavail); Hangup(); }; macro route-home-conference(exten) { // TODO }; macro route-home-service(exten) { switch(${exten}) { case 2900: // TODO: Name directory break; case 2901: NoOp(TODO remove this-- it exists only to make the fallthru work); case 2957: VoicemailMain(); Hangup(); break; case 2997: jump s@incoming; break; }; }; macro route-home(exten) { switch(${exten}) { pattern 2[0-4]XX: type=location; break; pattern 2[57]XX: type=person; break; pattern 28XX: type=conference; break; pattern 29XX: type=service; break; }; &route-home-${type}(${exten}); }; macro TryRoutes(TRY, exten, IsPOTS, Timeout, DialOpts) { for (&iterator(${TRY},A); "${A}" != ""; &iterate(A)) { NoOP(Trying route ${A}); &route-${A}(${exten},${IsPOTS},${Timeout},${DialOpts}); switch(${DIALSTATUS}) { case BUSY: Busy(); Hangup(); // is this needed? break; default: NoOP(not busy\, so trying next route...); break; }; }; Congested(); }; macro dial-tollfree(exten, TrueIsPOTS, Timeout, DialOpts) { &TryRoutes(enum-voipmich-fwd-sipphone,${exten},1,${Timeout},${DialOpts}); }; macro dial-national(exten, TrueIsPOTS, Timeout, DialOpts) { &TryRoutes(enum-voipjet-voxee,${exten},1,${Timeout},${DialOpts}); }; macro dial-localarea(exten, TrueIsPOTS, Timeout, DialOpts) { // Input numbers: 367-5340, 913-367-5340, and 1-913-367-5340 (w/o -s, of course) // Note: This wraps to dial-national and does *NOT* filter to ensure a number is local switch(${exten}) { pattern NXXXXXX: &dial-localarea(${LOCAL_AREACODE}${exten},1,${Timeout},${DialOpts}); break; pattern NXXNXXXXXX: &dial-localarea(1${exten},1,${Timeout},${DialOpts}); break; pattern 1NXXNXXXXXX: &dial-national(${exten},1,${Timeout},${DialOpts}); break; }; }; context ext-tollfree { _1800NXXXXXX => { SetCallerID(${POTS_CID}); &dial-tollfree(${EXTEN},1,,Tr); }; _1866NXXXXXX => { SetCallerID(${POTS_CID}); &dial-tollfree(${EXTEN},1,,Tr); }; _1877NXXXXXX => { SetCallerID(${POTS_CID}); &dial-tollfree(${EXTEN},1,,Tr); }; _1888NXXXXXX => { SetCallerID(${POTS_CID}); &dial-tollfree(${EXTEN},1,,Tr); }; _1899NXXXXXX => { SetCallerID(${POTS_CID}); &dial-tollfree(${EXTEN},1,,Tr); }; }; context ext-localarea { 911 => { SetCallerID(${POTS_CID}); &dial-localarea(${EMERGENCY},1,,Tr); }; _NXXXXXX => { SetCallerID(${POTS_CID}); &dial-localarea(${EXTEN},1,,Tr); }; _NXXNXXXXXX => { SetCallerID(${POTS_CID}); &dial-localarea(${EXTEN},1,,Tr); }; }; context ext-national { _1NXXNXXXXXX => { SetCallerID(${POTS_CID}); &dial-national(${EXTEN},1,,Tr); }; }; context ext-USpots { includes { ext-tollfree; ext-localarea; ext-national; }; }; context ext-global { // Teliax has cheap India & Israel // TODO }; context ext-universal { // TODO }; context ext-iaxtelPotsEmu { // Must be included *before* ext-national _1700NXXXXXX => &route-iaxtel(${EXTEN},0,,Tr); }; context ext-sipphonePotsEmu { _1222. => &route-sipphone(${EXTEN},0,,Tr); // Conference _1747. => &route-sipphone(${EXTEN},0,,Tr); // SIPPhone }; context ext-fwdPotsEmu { _1393. => &route-fwd(${EXTEN:4},0,,Tr); // FWD }; context ext-potsAndEmu { includes { ext-USpots; ext-iaxtelPotsEmu; ext-sipphonePotsEmu; ext-fwdPotsEmu; }; }; context ext-home { _2[0-57-9]XX => &route-home(${EXTEN}); }; context ext-speeddial { 350 => &dial-national(,1,,Tr); 364 => &dial-national(,1,,Tr); 380 => { &dial-national(,1,,Tr); &dial-national(,1,,Tr); &dial-national(,1,,Tr); }; }; context home { _. => { &prepcall(home,${EXTEN}); SetCallerPres(allowed_passed_screen); jump ${EXTEN}@home-2; }; }; context home-2 { includes { ext-home; ext-speeddial; }; 911 => jump ${EXTEN}@ext-localarea; _1NXXNXXXXXX => jump ${EXTEN}@ext-potsAndEmu; _9. => jump ${EXTEN:1}@ext-potsAndEmu; 7 => { if (${CALLERIDNUM} = 2300) { jump 2301; } else { jump 2300; }; }; }; macro normalize_cid() { switch(${CALLERID(number)}) { pattern NXXNXXXXXX: CALLERID(number)=1${CALLERID(number)}; break; pattern 1NXXNXXXXXX: break; // Ideal match pattern +1NXXNXXXXXX: CALLERID(number)=${CALLERID(number):1}; break; }; }; macro determinecidname() { TXTCIDName(${CALLERID(number)}); if (${LEN(${TXTCIDName})} > 0) CALLERID(name)=${TXTCIDName}; }; context incoming-hangup { h => { NoOP(Hangup in incoming); Hangup(); }; }; context incoming-default { _. => { Ringing(); &normalize_cid(); &prepcall(incoming,${EXTEN}); Zapateller(nocallerid); PrivacyManager(); //&determinecidname(); NoOP(Caller ID is ${CALLERID(all)}); SetCallerPres(allowed_not_screened); jump 2700@ext-home; }; }; context incoming { includes { incoming-hangup; incoming-default; }; }; // TODO: dial by name directory & menu-invalid // TODO: autodial