Asterisk tips voicemail live
Created by: JustRumours,Last modification on Tue 15 of Jul, 2008 [08:03 UTC] by admin
Voicemail Live
Answering machine mimic: Listen while caller is leaving voicemail for you; with pick-up optionPosted by Philipp von Klitzing in Dec. 2005
Idea
You'd like to have the PBX voicemail act just like your answering machine at home? It can be done!First of all you'd probably want to listen in on the new call while the caller dictates her message to the system. Then, if you wish, press # to stop the recording and connect to the caller. All this is most useful if your phone comes with auto-answer/intercom support and a decent speaker.
Concept
- ideally you have a multi-line speaker phone that allows to configure one of the lines for auto-answer/intercom (SNOM or the like)
- place caller, voicemail and speaker phone into a first dynamic MeetMe conference with the help of AGI-generated .call files
- dissolve that first MeetMe upon a # key press by the callee on the speaker phone
- now engage in a second dynamic MeetMe to begin a conversation; we allow for a maximum of two simultaneous instances of this second MeetMe
Potential enhancements
Note: For testing purposes the freely available (for private use) SNOM softphone proved to be a useful tool- Use app_bridge instead of 2nd MeetMe for duplex talking - bug/patch 5841
- Consider ChanSpy instead of the 1st MeetMe (for listening in)
- Insert an astdb (DBGet/Put) switch for "Voicemail Live" on/off
- Use 'dE' instead of just 'd' to create a free dynamic conference and then do away with MeetMeCount
- Find out why we sometimes get the VM prompts rather late?! Possible cause is CPU load, should be low (check with 'top')
- Dedicate a SNOM button of type "DTMF" to issue the # and label it "voicemail pick-up"
- Use the SNOM "URL Action" for Off-Hook to talk to the vm caller by simply lifting the handset (no need to press #)
- Rewrite the dialplan part without priority jumping
- Play the VM intro _before_ entering the MeetMe room so that the listener (=VM box owner) does not always have to listen to it
- Fix me: If the caller hangs up before having entered MeetMe, then the listener and voicemail will remain in MeetMe until time out . Cause: the x option of MeetMe first of all needs to marked user to enter before it gets a chance to close the conference. Solutions:
- use ChanSpy instead of MeetMe (but then owner can't exit by pressing #), or
- let a 4th fake & marked user (option A) quickly enter and leave the MeetMe room after caller hangup (this caller's presence must then overlap with the entry of the caller into MeetMe; we would need to stay below vm config for minimum message duration), or
- set a channel variable as flag, and then use the h extension to call MeetMeAdmin on hangup and kick those that remain in MeetMe (downsides: a channel variable is probably already destroyed when we reach the h extension, but maybe we can address this with a global variable? Also employing the h extension might mess up our CDRs a little, and this h extension will be called by any closing call, not just our VM calls)
- consider to introduce the 'w' flag for the listener and set up special music-on-hold that plays pick-up instructions for the owner/listener
- check if $agi["callerid"] gives us trouble when a Caller ID Name is present and we thus have a space in the filename
extensions.conf
Note: You'll most probably need priorityjumping=yes in the [general] section if you are using Asterik v1.2.x. If you don't like that then you'll need to rework the parts where jumpts to n+101 are performed, e.g. look for Dial() and ChanIsAvail()
[vm-meet-listener]
; We need this (and the Local channel) in order to set the Caller ID correctly
; Used by macro-vm-meetme (initiated by macro-vm-listen.agi)
exten => listen,1,NoOp ; Maybe set a timeout here > vm max. message length
; if you have a SNOM then EITHER set the header below, OR configure the phone's line to auto-answer
;exten => listen,2,SIPAddHeader(Call-Info: <sip:domain>\;answer-after=0) ; try SNOM auto-answer, add your domain
exten => listen,2,NoOp
exten => listen,3,Set(CALLERID(Name)=${ORIGCIDNAME})
exten => listen,4,Set(CALLERID(Num)=${ORIGCIDNUM})
exten => listen,5,Dial(${TARGET},5) ; give intercom/auto-answer 5 sec to answer
exten => t,1,NoOp(=== The auto-answer setup of ${TARGET} might not be correctly configured ===)
exten => t,2,HangUp
[vm-meet-join]
; -- Dialplan logic for the callee (aka 'silent listener' aka 'vm-owner') --
; Press # to exit the voicemail meetme room and kick the calling user
; OPTIONAL: Dedicate a "DTMF" type SNOM button to send #
; REQUIRES: Language directory "mm" with silenced "conf-kicked.gsm" soundfile (short!)
; e.g. ~np~[root@sounds]~/np~# cp silence/1.gsm mm/conf-kicked.gsm
; REQUIRES: vm-instructions-short.gsm (2nd half of vm-instructions.gsm)
; NOTE: The two bugs/patches mentioned below are not necessary for Asterisk v1.2.1 or later
; NOTE: Use [http://bugs.digium.com/view.php?id=5773|bug/patch 5773] to fix MeetMe X option in Asterisk v1.2.0 (ref. bug 5631)
; NOTE: Use [http://bugs.digium.com/view.php?id=5810|bug/patch 5810] to pass variables from .call files to Local channels in Asterisk v1.2.0
; NOTE: We can't transfer from within 2nd MeetMe
exten => join,1,Set(TIMEOUT(absolute)=300)
; Possible ToDo here: Check with MeetMeCount if 99${ROOMNO} is empty = caller already hung up
exten => join,2,Playback(vm-instructions-short) ; play "#-pickup" instructions
exten => join,3,Wait(.5)
exten => join,4,NoOp
exten => join,5,MeetMe(99${ROOMNO}|dmqpx) ; due to playback we enter after caller
exten => join,6,MeetMeAdmin(99${ROOMNO},K) ; Kick all users after exiting MeetMe by pressing #
exten => join,7,MeetMeCount(98${ROOMNO}|mcount)
exten => join,8,GotoIf($[${mcount} > 1]?13)
exten => join,9,Set(ROOMSELECT=98${ROOMNO}) ; remember the chosen room
exten => join,10,MeetMe(98${ROOMNO}|dqpx) ; TODO for future: Use app_bridge instead!
exten => join,11,MeetMeAdmin(98${ROOMNO},K) ; Kick with # to make sure its closed
exten => join,12,HangUp
exten => join,13,MeetMeCount(97${ROOMNO}|mcount)
exten => join,14,GotoIf($[${mcount} > 1]?18)
exten => join,15,Set(ROOMSELECT=97${ROOMNO})
exten => join,16,MeetMe(97${ROOMNO}|dqpx) ; TODO for future: Use app_bridge instead!
exten => join,17,MeetMeAdmin(97${ROOMNO},K) ; Kick with # to make sure its closed
exten => join,18,HangUp ; we dont allow more than 2 vm callers
exten => h,1,GotoIf($["${ROOMSELECT}" = ""]?3)
exten => h,2,MeetMeAdmin(${ROOMSELECT},K) ; in case we went on-hook before caller
exten => h,3,NoOp(ROOMSELECT=${ROOMSELECT})
[vm-meet-exit]
; -- Exit context for the VM caller (for pressing #) --
; This way we can distinguish between own # and being kicked by MeetMeAdmin!
exten => #,1,NoOp(===== VM caller pressed: # =====)
exten => #,2,HangUp
[macro-vm-meetme]
; -- This is the main macro for 'voicemail live' handling the caller --
; ${ARG1} - Device(s) to ring (TARGET)
; ${ARG2} - Extension
; ${ARG3} - Mailbox
;
; Required contexts: [vm-meet-listener] and [vm-meet-join] and [vm-meet-exit]
;
; -- we are unavailable; don't go here if we are busy! --
exten => s,1,Set(TIMEOUT(absolute)=300) ; need to synchronize this with max vm rec
exten => s,2,Set(TARGET=${ARG1}) ; we read this in AGI
exten => s,3,ChanIsAvail(${ARG1},j) ; bug: only works for SIP peers?!
exten => s,4,Set(ORIGLANG=LANGUAGE) ; preserve the original language setting
exten => s,5,MeetMeCount((99${ARG2}|mcount)
exten => s,6,GotoIf($[${mcount} > 0]?101) ; do we already have someone on VM recording?
exten => s,7,Agi(macro-vm-listen.agi) ; put the VM owner into MeetMe for Intercom
exten => s,8,Wait(.1)
exten => s,9,Agi(macro-vm-record.agi) ; put the VM record app into MeetMe
exten => s,10,Playback(beep) ; play short sound so caller knows we answered
; at this point we sometimes get a delay with silence due to slow call-setup work of Asterisk
exten => s,11,Set(LANGUAGE()=mm) ; we have to avoid "You have been kicked..."!
; replace conf-kicked.gsm with plain silence
exten => s,12,Set(MEETME_EXIT_CONTEXT=vm-meet-exit)
; now put the caller into MeetMe together with voicemail and the silent listener
exten => s,13,MeetMe(99${ARG2},dAXq) ; Option X works after applying patch 5773
exten => s,14,MeetMeCount((98${ARG2}|mcount) ; we arrive here after 1) kick or 2) # press
exten => s,15,GotoIf($[${mcount} > 1]?18)
exten => s,16,MeetMe(98${ARG2}|dAM)
exten => s,17,HangUp
exten => s,18,MeetMeCount((97${ARG2}|mcount)
exten => s,19,GotoIf($[${mcount} > 1]?101) ; we tried twice, so no meetme, now pure vm
exten => s,20,MeetMe(97${ARG2}|dAM)
exten => s,21,HangUp
exten => s,101,Voicemail(u${ARG3}) ; we already have another VM-MeetMe active
exten => s,102,HangUp
exten => s,104,Goto(101) ; ChanIsAvail gave a negative result
[default]
; === Join voicemail to meetme (for macro-vm-meetme) ===
; NOTE: Adjust macro-vm-record.agi accordingly if you change the '8600' prefix here
exten => _8600X.,1,Set(CALLERID(name)=${ORIGCIDNAME}) ; needed for the e-mail notification
exten => _8600X.,2,Set(CALLERID(number)=${ORIGCIDNUM})
exten => _8600X.,3,VoiceMail(u${EXTEN:4})
exten => _8600X.,4,HangUp
; here's the extension for our own phone (SIP/myPeer); send caller to voicemail if we are not available
exten => 1234,1,Dial(SIP/myPeer,20,t)
exten => 1234,2,Macro(vm-meetme,SIP/myPeer,1234,881234) ; we are unavailable
exten => 1234,3,HangUp
exten => 1234,102,Voicemail(b881234)
exten => 1234,103,HangUp
AGI scripts (PHP)
On Debian you'll need 'php-cli' installed for this. Of course any other language can do the job as well, you are not bound to PHP.macro-vm-listen.agi
#!/usr/bin/php -q
<?php
ob_implicit_flush(true);
set_time_limit(5);
$in = fopen("php://stdin","r");
// toggle debugging output (more verbose)
$debug = false;
//$debug = true;
function __read__() {
global $in, $debug;
$input = str_replace("\n", "", fgets($in, 4096));
if ($debug) echo "VERBOSE \"read: $input\"\n";
return $input;
}
function __write__($line) {
global $debug;
if ($debug) echo "VERBOSE \"write: $line\"\n";
print $line."\n";
}
//read the standard agi variables
while (!feof($in)) {
$temp = str_replace("\n","",fgets($in,4096));
$s = split(":",$temp);
$agi[str_replace("agi_","",$s[0])] = trim($s[1]);
if (($temp == "") || ($temp == "\n")) {
break;
}
}
//get the variables and strip off all the extra stuff around
__write__~/np~("GET VARIABLE TARGET");
$res = substr(strrchr(~np~__read__(),"("),1,-1);
__write__~/np~("GET VARIABLE ARG2");
$arg2 = substr(strrchr(~np~__read__(),"("),1,-1);
$cf = fopen("/tmp/cb".$agi["callerid"],"w+");
fputs($cf,"Channel: Local/listen@vm-meet-listener/n\n");
fputs($cf,"Set: _ORIGCIDNAME=".$agi["calleridname"]."\n");
fputs($cf,"Set: _ORIGCIDNUM=".$agi["callerid"]."\n");
fputs($cf,"Set: CALLERID(name)=".$agi["calleridname"]."\n");
fputs($cf,"Set: CALLERID(number)=".$agi["callerid"]."\n");
fputs($cf,"Set: _TARGET=".$res."\n");
fputs($cf,"Set: _ROOMNO=".$arg2."\n");
fputs($cf,"MaxRetries: 0\n");
fputs($cf,"RetryTime: 10\n");
// --- We are the first so we create the dynamic conference ---
fputs($cf,"Context: vm-meet-join\n");
fputs($cf,"Extension: join\n");
fputs($cf,"Priority: 1\n");
//Now move (!) the file to the outgoing dir AFTER we closed it
fclose($cf);
exec("mv /tmp/cb".$agi["callerid"]." /var/spool/asterisk/outgoing");
fclose($in);
?>
macro-vm-record.agi
#!/usr/bin/php -q
<?php
ob_implicit_flush(true);
set_time_limit(5);
$in = fopen("php://stdin","r");
// toggle debugging output (more verbose)
$debug = false;
//$debug = true;
function __read__() {
global $in, $debug;
$input = str_replace("\n", "", fgets($in, 4096));
if ($debug) echo "VERBOSE \"read: $input\"\n";
return $input;
}
function __write__($line) {
global $debug;
if ($debug) echo "VERBOSE \"write: $line\"\n";
print $line."\n";
}
//read the standard agi variables
while (!feof($in)) {
$temp = str_replace("\n","",fgets($in,4096));
$s = split(":",$temp);
$agi[str_replace("agi_","",$s[0])] = trim($s[1]);
if $temp == "") {
break;
}
}
//get the variables and strip off all the extra stuff around
__write__("GET VARIABLE ARG2");
$arg2 = substr(strrchr(__read__(),"("),1,-1);
__write__("GET VARIABLE ARG3");
$arg3 = substr(strrchr(__read__(),"("),1,-1);
$cf = fopen("/tmp/cb2_".$agi["callerid"],"w+");
fputs($cf,"Channel: Local/8600".$arg3."@default/n\n");
fputs($cf,"Set: CALLERID(name)=".$agi["calleridname"]."\n");
fputs($cf,"Set: CALLERID(number)=".$agi["callerid"]."\n");
fputs($cf,"Set: _ORIGCIDNAME=".$agi["calleridname"]."\n");
fputs($cf,"Set: _ORIGCIDNUM=".$agi["callerid"]."\n");
fputs($cf,"MaxRetries: 0\n");
fputs($cf,"RetryTime: 10\n");
fputs($cf,"Application: MeetMe\n");
fputs($cf,"Data: 99".$arg2."|dpqx\n");
//Now move (!) the file to the outgoing dir AFTER we closed it
fclose($cf);
exec("mv /tmp/cb2_".$agi["callerid"]." /var/spool/asterisk/outgoing/");
fclose($in);
?>
See also
Go back to Asterisk tips and tricks
Comments
333
333
I think this is a great feature for a business environment, especially for those people who need to screen their calls. I've noticed that it leaves you hangin if the calling party hangs up. I think this is probably a limitation of MeetMe, and with the enhancements suggested this would no longer be a limitation.
333
333