1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. If you had a PIAF Forum account in the vBulletin days, log in with your old credentials. Otherwise, sign up again and we'll get you back in business as soon as we can.
  3. A serious FreePBX vulnerability has been reported. Update your Framework Module immediately. Click here for details.
  4. Critical FreePBX vulnerability! Update your server immediately. Details here.

PIONEERS Picking up a call on hold on another extension

Discussion in 'Developers' Corner' started by nicknomo, Mar 31, 2014.

  1. nicknomo Happy-IT-Guy

    UPDATE:
    Now with a Yealink APP:
    http://pbxinaflash.com/community/index.php?threads/yealink-app-for-picking-up-calls-on-hold.14759/

    I was wondering if anyone had any input on this approach. What I've been working on is being able to grab a call that is on hold on another extension in asterisk. This is usually needed when you didn't have the foresight to park a call, and you or someone else ends up wanting to pick it up on another extension.

    There doesn't appear to be any channel variables that indicate a call is on hold... there is only hold events, but you'd need to constantly monitor the system to keep track of them. Neither FreePBX nor Elastix currently have any methods for picking up a held call, and I could not find any built in application within asterisk that natively does this.. What I ended up noticing was that calls on hold has the write format of "slin", and I've leveraged that to isolate calls on hold.

    What I ended up doing was using "core show channels" and "core show channel" to see what was in use by the extension we want to steal the call from. I'd go down the list and look for channels that belong to SIP/{$extension}. I'd then look at who they are bridged with, and use "core show channel" on those bridged channels to see the "write format". If its playing a wav file (hence the signed linear write format), then I take the call.

    I'm curious to hear from the rest of you if you can see any problems with this approach? It appears to work OK in testing, but its with a limited number of calls.
    Last edited by nicknomo, Apr 8, 2014
  2. nicknomo Happy-IT-Guy

    Here are the files I'm using. I've placed them in /var/lib/asterisk/agi-bin . They will require execute privileges (chmod 755).

    There are two variants I made (files are attached to this post):
    1) holdsteal2.agi - made to get out of the script as soon as it finds a match.
    2) holdsteal.agi - made to get a full array of held calls, in the case that there may be more than one. This is slated for future development, and I want to try making an app for the T46G using this.

    As far as the dialplan work, I added this under [from-interal-custom] in extensions_custom.conf
    Code:
    [from-internal-custom]
    include => pickuphold
    
    and at the bottom of the file, I have this:
    Code:
    [pickuphold]
    exten => _#9X.,1,NoOP(PICK UP HELD CALL: ${EXTEN:2})
    exten => _#9X.,n,Set(ARG1=${EXTEN:2})
    exten => _#9X.,n,AGI(holdsteal2.agi,${ARG1})
    exten => _#9X.,n,NoOP(This was returned as the held call channel2: ${HELD_CHANNEL})
    exten => _#9X.,n,Bridge(${HELD_CHANNEL})
    exten => _#9X.,n,hangup()
    ;end of [pickuphold]
    
    I've elected to use #9 and then the extension number to perform the action (e.g. #9301 ).

    Limitations:
    1) You must use SIP extensions.
    2) You MUST have hold music
    3) This will NOT work with GSM hold music. Preferably the format should be WAV, but
    MP3 does work as well. Other formats are probably not supported.
    4) If you use elastix, you'll need to put a different password in the .agi file.

    Attached Files:

    Last edited by nicknomo, Jul 11, 2014
  3. atsak Guru

    What happens if the extension who had the call on hold tries to pick it up out of interest?
  4. nicknomo Happy-IT-Guy

    Just wanted to post this, in case anyone wanted to make the held call pickup the same as the current pickup feature code "**", you'd use the following dial plan code.

    Code:
    [pickuphold]
    include => app-pickup
    exten => _**X.,1,NoOP(PICK UP HELD CALL: ${EXTEN:2})
    exten => _**X.,n,Set(ARG1=${EXTEN:2})
    exten => _**X.,n,AGI(holdsteal2.agi,${ARG1})
    exten => _**X.,n,NoOP(This was returned as the held call channel2: ${HELD_CHANNEL})
    exten => _**X.,n,GotoIf($[ "{$HELD_CHANNEL}" != "" ] ?bridgeit:passit)
    exten => _**X.,n(bridgeit),Bridge(${HELD_CHANNEL})
    exten => _**X.,n(bridgeit),hangup()
    exten => _**X.,n(passit),NoOP(Passing it!!!!)
    exten => _**X.,n(passit),GOTO(from-internal-additional,${EXTEN},1)
    exten => _**X.,n(passit),hangup()
    ;end of [pickuphold]
    
    If it doesn't find a call on hold, it passes it onto the normal dial plan.
  5. nicknomo Happy-IT-Guy


    The line is basically disconnected on the original extension, so they don't have a chance to retrieve it back. Its a lot like if the call was transferred. Its similar to call parking, except it never leaves the extension it is on.

    The upside to doing this is, unlike a parked call, there isn't really a chance for the call to be orphaned. So, if the call isn't picked up, the person who has the call on hold still sees it and is aware of it. The down side to this is

    1) it could be a security issue, to have employees steal other calls
    2) It cannot handle more than one call on hold. It just takes the first one it found.

    #2 is customizable... I could potentially return the call that has been on hold the longest, or the shortest.
  6. kenn10 Guru ish

    I'd like to see something like this developed to act the way the old Rolm/Siemens PBX's worked. Any extension could place a call on hold and another user could dial an access code plus the extension number where the call was on hold and retrieve it. On the multi-line sets, the call would just drop off when picked up elsewhere.

    Likewise, you could dial a feature code and place a call on-hold at any extension in the system. On multiline phones, it would show up as a held call and on single line phones, it was retrieved with a single retrieve code.

    This is kind of like valet call parking on a per station basis where every extension supports a parking slot. This, and other multiline shared call functionality is sadly lacking in Asterisk.
  7. nicknomo Happy-IT-Guy


    If the access code was shared among all extensions, or large ranges of extensions, that would actually be very easily done in the dial plan. If you wanted the access code when you dial in, like #9 + <access code> + <extension> ... then the extension 300, with access code 8843, would look like this when dialed - #98843300 . It would only require a minor modification to my code above, like so:

    Code:
    [pickuphold]
    exten => _#98843X.,1,NoOP(PICK UP HELD CALL: ${EXTEN:6})
    exten => _#98843X.,n,Set(ARG1=${EXTEN:6})
    exten => _#98843X.,n,AGI(holdsteal2.agi,${ARG1})
    exten => _#98843X.,n,NoOP(This was returned as the held call channel2: ${HELD_CHANNEL})
    exten => _#98843X.,n,Bridge(${HELD_CHANNEL})
    exten => _#98843X.,n,hangup()
    ;end of [pickuphold]
    
    To use your own pin, you could sub out the 8843 from the code above with whatever 4 digit code you wanted. You could also change the #9 to whatever two digit feature code you wanted, provided its not in use already.

    If you want the system to prompt you and ask for a pin, then code like this should work
    Code:
    [pickuphold]
    exten => _#9X.,1,NoOP(PICK UP HELD CALL: ${EXTEN:2})
    exten => _#9X.,n,Set(ARG1=${EXTEN:2})
    exten => _#9X.,n,Authenticate(8843,)
    exten => _#9X.,n,AGI(holdsteal2.agi,${ARG1})
    exten => _#9X.,n,NoOP(This was returned as the held call channel2: ${HELD_CHANNEL})
    exten => _#9X.,n,Bridge(${HELD_CHANNEL})
    exten => _#9X.,n,hangup()
    ;end of [pickuphold]
    
    The line that says Authenticate() checks for the pin inside the brackets. You could change out the number as desired.

    Now, as far as putting calls on hold remotely, I don't know of any way to do that... From what I can tell, being "on hold" in asterisk doesn't involve asterisk doing much. Its really more of a function of the phone. The phone tells asterisk its putting the line on hold, stops sending audio data, and then the phone does whatever the manufacturer decides to do. Asterisk really only comes into play by saying "welp, I guess I should play some hold music for this guy". The channel is still technically open on both sides. Not much has really changed.

    Basically, what I think would stop me from forcing a hold is that I can't really make the phones do anything. Even if I could, it would likely be manufacture specific.

    What I *could* do is make a script to steal an ACTIVE call. I'd guess the only reason you'd want to put a call on hold is so you can take it from an extension that is off the hook, but no one is present. I'm guessing you'd put the call on hold, just to steal pick it up from hold? If so, stealing an active call would effectively do that. That would require a change to my AGI code, but it would be a relatively minor one.
    Last edited by nicknomo, Apr 1, 2014
  8. kenn10 Guru ish

    Great ideas. I'd be curious what others on this forum think of the ideas.
    Last edited by kenn10, Apr 5, 2014
  9. nicknomo Happy-IT-Guy

    I've updated this a little to reflect what was previously suggested.

    Here are the files I'm using. I've placed them in /var/lib/asterisk/agi-bin . They will require execute privileges (chmod 755).

    There are two variants I made (files are attached to this post):
    1) holdsteal2.agi - made to get out of the script as soon as it finds a match.
    2) callsteal.agi - This script takes a live/active call from another extension. Its ideal if you left a handset off the hook, and you want to pick it up.

    As far as the dialplan work, I added this under [from-interal-custom] in extensions_custom.conf
    Code:
    [from-internal-custom]
    include => pickuphold
    
    and at the bottom of the file, I have this:
    Code:
    [pickuphold]
    exten => _#99X.,1,NoOP(PICKING UP ACTIVE CALL: ${EXTEN:3})
    exten => _#99X.,n,Authenticate(31337,)
    exten => _#99X.,n,Set(ARG1=${EXTEN:3})
    exten => _#99X.,n,AGI(callsteal.agi,${ARG1})
    exten => _#99X.,n,NoOP(This was returned as the active call channel: ${HELD_CHANNEL})
    exten => _#99X.,n,GotoIf($[ "${HELD_CHANNEL}" != "" ] ?bridgeit1:passit1)
    exten => _#99X.,n(bridgeit1),Bridge(${HELD_CHANNEL})
    exten => _#99X.,n(bridgeit1),hangup()
    exten => _#99X.,n(passit1),NoOP(Passing it!!!!)
    exten => _#99X.,n(passit1),hangup()
     
    exten => _#9X.,1,NoOP(PICK UP HELD CALL: ${EXTEN:2})
    ;exten => _#9X.,n,Authenticate(31337,)
    exten => _#9X.,n,Set(ARG1=${EXTEN:2})
    exten => _#9X.,n,AGI(holdsteal2.agi,${ARG1})
    exten => _#9X.,n,NoOP(This was returned as the held call channel: ${HELD_CHANNEL})
    exten => _#9X.,n,GotoIf($[ "${HELD_CHANNEL}" != "" ] ?bridgeit2:passit2)
    exten => _#9X.,n(bridgeit2),Bridge(${HELD_CHANNEL})
    exten => _#9X.,n(bridgeit2),hangup()
    exten => _#9X.,n(passit2),NoOP(Passing it!!!!)
    exten => _#9X.,n(passit2),GOTO(from-internal-additional,${EXTEN},1)
    exten => _#9X.,n(passit2),hangup()
    ;end of pickup hold
    
    I've elected to use #99 and then the extension number to pickup live calls (e.g. #99301 ). This requires an authentication code, and as of now its set to 31337 .

    I've elected to use #9 and then the extension number to pickup held calls. (e.g. #9305 ). The authentication line is commented out, but its there in case someone wants to enable it..

    This has tested well so far. It proves to be more useful than park, since if you leave your desk with a call on hold, then you or anyone else can pick it up anywhere. Now, with the forceful active call steal, you don't even need that call to be on hold.

    It should cover the features provided by many legacy PBX's.

    Limitations:
    1) You must use SIP extensions.
    2) You MUST have hold music (or anything else playing on hold)
    3) This will NOT work with GSM hold music. Preferably the format should be WAV, but MP3 does work as well. Other formats are probably not supported.
    4) If you use elastix, you'll need to put a different password in the .agi file.
    5) If you have more than one call on hold, the hold steal will likely find the oldest one and steal that one.
    6) Using the #99 feature code to steal a live call prohibits you from using extentions 900-999 for the hold steal. Any extension that starts with 9 (e.g. 901) will trigger the live steal instead, since the sequence will read #9901 . Instead of picking up the held call on 901 (#9 + 901), it will try to pick up a live call on 01 (#99 + 01).

    Attached Files:

    Last edited by nicknomo, Jul 11, 2014
  10. phoneguy Guru

    We have a app coming out for this in the next release that does everything through AMI and does not have any of the requirements above and the potential issues of anyone can now pickup any held call and no control over which one. Also how do you verify its a call on hold with this setup versus my extension getting MoH for some other reason.

    http://wiki.freepbx.org/display/FCM/RESTful Phone Apps-Roadmap
  11. nicknomo Happy-IT-Guy


    This is done partially through AMI as well... but it using a very simple approach on sending console commands. What is your approach? I posted here looking for cleaner ways to do it, so I'm open to hearing suggestions. If there is more direct way to look up this information, I'd definitely consider a rewrite. I've yet to hear of another approach to this... in fact I've seen plenty of people say it couldn't be done to begin with. AFAIK there is no channel data, or other accessible variables that indicate a call is on hold.

    Security is definitely light in my work here (partially by design - that is how the legacy phone systems worked). I've added the option to authenticate, but I personally won't be using it... and I wrote it so I can use it.

    In theory you could easily add granular protections to the dial plan... but it would be a lot of manual work. If security information was added to the DB via the GUI, I could create generic code that makes it secure. Do you plan to put the entry for these controls in the main release of FreePBX or are they going to only be in a commercial module?

    As far as verifying the call is on hold, we can be reasonably certain they are not getting MoH for any other reason. Basically we are looking at the channels bridged with our target extension, and nothing else. If they are bridged, the remote channel is either talking or on hold, save for some uncommon scenarios (none that I've yet to run into).

    I'm planning to make a yealink app to use in conjunction with my logic here. The only thing I'm a little unsure of is how to get the call to the extension requesting it. I'd have to do something like park it first if I wanted the phone to dial in. I could transfer the call to the extension, but that feels too much like a hack. How do you think the calls should be bridged?
  12. phoneguy Guru

    As far as permissions this is all controlled through the FreePBX Class of Service module which yes is a commercial module.

Share This Page