login | register
Mon 13 of Oct, 2008 [05:33 UTC]

voip-info.org

History

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 option
Posted 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

  1. Use app_bridge instead of 2nd MeetMe for duplex talking - bug/patch 5841
  2. Consider ChanSpy instead of the 1st MeetMe (for listening in)
  3. Insert an astdb (DBGet/Put) switch for "Voicemail Live" on/off
  4. Use 'dE' instead of just 'd' to create a free dynamic conference and then do away with MeetMeCount
  5. Find out why we sometimes get the VM prompts rather late?! Possible cause is CPU load, should be low (check with 'top')
  6. Dedicate a SNOM button of type "DTMF" to issue the # and label it "voicemail pick-up"
  7. Use the SNOM "URL Action" for Off-Hook to talk to the vm caller by simply lifting the handset (no need to press #)
  8. Rewrite the dialplan part without priority jumping
  9. Play the VM intro _before_ entering the MeetMe room so that the listener (=VM box owner) does not always have to listen to it
  10. 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:
    1. use ChanSpy instead of MeetMe (but then owner can't exit by pressing #), or
    2. 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
    3. 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)
  11. 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
  12. 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

Comments Filter
222

333

by rushowr, Wednesday 20 of September, 2006 [13:17:06 UTC]
I'll be posting my results once I get to this :) I've got a HUGE amount of stuff to finalize and then I'll be starting on enhancements :)
222

333

by lacym, Wednesday 20 of September, 2006 [08:04:10 UTC]
I've been playing with this for several hours tonight (more like 6), and finally have it working with a Polycom IP601. There are a few issues that will keep it from being deployed in the environment I had planned to. I'll probably keep playing with it, especially now that I have figured out what it is doing.

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.
222

333

by Zion800, Thursday 10 of August, 2006 [21:44:02 UTC]
Anybody do any of enhancements that were recommended? Post!! :-)
222

333

by lacym, Thursday 06 of April, 2006 [16:07:09 UTC]
I'm having trouble following this. What is the SIP/myPeer? In this a context that needs to be added, or is this supposed to be replaced with something, or what? I've tried this, and it didn't seem to work, and the only thing is that I'm just not sure what the SIP/myPeer is doing. I did a saearch on mypeer and it looks to be a context in some installations, though what kind of context, I'm not sure.