You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.8 KiB

title date layout permalink
Asterisk: Call Queues & Agents (Building a call-in show) 2021-12-18 06:33:28 page /pbx/2021/DEC/18-callqueus.php

Quite some time ago I started playing with AsteriskPBX. I havent yet written a detailed account of how this came to be, so let me sum it up. I originally wanted to convert my house phones in to a hybrid VOIP setup. I only have an analog (FXS) port coming from my fiber terminal, despite the fact its technically a VoIP service. I also didnt want to spend hundreds of dollars on cordless handsets to replace the existing collection of handsets; but I wanted the ability to be called down in the shack over ethernet since cordless DECT doesnt reach there very well.

What transpired was a series of tangents as I needed to learn Asterisk, buy some hardware, figure out the flaws in the setup, and getting distracted doing other things. I still have a couple of ATAs down here and Ive yet to get the house fully switched over. But in the meantime I decided to do another stupid trick and attempt to do a livestream call-in show using my deskphone to control the queue as an agent, a softphone “barging” in to the agents channel, and three days of trying to make it all work.

The main problem I had was the documentation I could find for anything related to this subject was based on older versions of Asterisk. You literally cannot rely on documentation for older revisions when doing Asterisk; there have been some massive changes in how things work. I spent three days trying to get this working; every time I felt like I kept running through a loop. To make a long story short, if youre running Asterisk 18, as I am; then searching online for things related to asterisk call queue agent will be a bit of a waste of time. It probably didnt help that my Asterisk configuration didnt load every single module; and several I needed hadnt been loaded.

Anyway…after three days of stumbling around search results, reading older revision documentation in order to catch up, and pulling my hair out. Turned out reading the page about changes from chan_agent to app_agent_pool was the lone doc that made everything fall in place.

Asterisk Modules

I did not keep track of what modules I had to activate in my barebones install; I know I activated voicemail and while applications a while back; I dont remember if db was in the minimal config. I dont think queue requires any of those, so its probably a good bet these are all youll need to put in modules.conf:


load = app_queue.so
load = app_agent_pool.so
load = func_channel.so
load = chan_local.so

I seem to remember in reading the documentation, you needed to make sure that app_queue as loaded after pbx_config.so; I did this moving them to the very end of the modules.conf file. But in writing this I noticed a duplicate entry for app_queue.so at the top of the list; so Im not sure anymore and Im writing this on my birthday…so I dont feel like looking it up again. I dont think itll hurt to move pbx_config to the end of the load with app_queue coming right after. For all I know the first one could be erroring out. Loading chan_local.so seemed to be a big key as to why nothing was working for me.

Call Agents

I wanted the call agent function simply so I could use * and # on the deskphone in a way that felt “more radio” than punching them on a softphone. I mostly just didnt want to have to switch apps to switch calls. This is what gave me the most trouble, mostly due to the lack of chan_local and knowing that I had to specify the agent as local…and…wow. The way agents are created is also VASTLY different than the old software. You no longer have a single [agents] context that specifies agents; each agent ID gets its own context.


[501]

ackcall=yes
acceptdtmf=#
autologoff=0
musiconhold=silence

This is my absolute minimal configuration for Agent 501. The options themselves are documented. In fact it was pretty easy for me to tell from the same conf how this worked before I read the CHANGELOG that mentioned it.

Call Queue Setup

I was so close on this one. In fact the only thing I had wrong was how I was adding agents as members. Most docs and examples made it look as easy as:


member = Agent/501

But no, thats wrong; and it was only after reading about the change to app_agent_pool and a forum post that I figured out how to actually specify an agent as a member:


member => Local/501@agents,,Jay,Agent:501

This is why not having chan_local loaded screwed me over. I didnt click that the agent would use a local channel. It makes total sense now, sure. But after getting chan_local loaded and converting to that line; it all started working.

Dialplans

Most everything I read said I could just simply call AgentLogin() and it would ask for the ID and pin; then I realized the new system didnt use a pin for an agent….then it turns out it doesnt work that way at all. All you simply do now is pass AgentLogin the agent ID. Wheres the authentication? You now do that somewhere in the dialplan. Since Im the only one using this PBX, I just set up an extenstion to do AgentLogin(501) and Im off to the races, right?

No.

When I did this, I could get calls by pressing #, but I couldnt hang-up. Thats because pressing * to hang up is handled by the internal functions. So I have to set the dtmf_features for the channel. This is fine and all, except AgentLogin doesnt do that; these are usually passed in the Dial() app. This is when I found out I needed func_channel.so loaded; because trying the obvious thing of using Set(CHANNEL() didnt work at first.


same = n,Set(CHANNEL(dtmf_features)=H)

The other part of this I hadnt mentioned was using AgentRequest.


[agents]
exten = _XXX,1,NoOp()
same = n,AgentRequest(${EXTEN})
same = n,Congestion()

I already had this in the dialplan when I tested; as during my digging I came across this somewhere. So my full dialplan for agents looks like this:


[agent_login]
exten = _XXX,1,Set(CHANNEL(dtmf_features)=H)

same = n,Answer()
same = n,AgentLogin(${EXTEN})
same = n,Hangup()


[agents]
exten = _XXX,1,NoOp()
same = n,AgentRequest(${EXTEN})
same = n,Congestion()

This line in my deskphones context logs it in as an agent:


exten = 6000,1,Goto(agent_login,501,1)

So at this point, everything was working. I call my DID from my cell phone, get in to the queue, I hear the deskphone beep, # to take call, and * to hangup. Excellent.

Lets Build The Call In Show

Obviously if I want to have callers on a live-stream, I need to get the channel audio in to OBS and get my audio in to the softphone. Thankfully, shared mode means the softphone and OBS can share the mic. I dont get the highly compressed audio out to the caller, but I dont think thats important. I originally did this to avoid latency, but I dont think that would have mattered much either. Its just easier to not worry about routing the calls around too much. I largely just have to configure the softphone to use my normal desktop audio (which is captured by OBS) and to share the mic. Easy peasy. Now….how do we get this thing to “barge” in on the agents channel?

To do this, we can use ExtenSpy. It will require you to have app_chanspy.so loaded. But other than that, we just have to tell it which extension we want to “barge” in to; barge being the option that allows the spy to hear and talk in to the channel.


exten = 777,1,ExtenSpy(501@agents,Bq)
same = n,Hangup()

It is literally that easy. Our agents extension is 501@agents, the B option activates barge, q means it doesnt beep all the time. So all I have to do is dial 777 on the softphone. At that point, I can hear and talk to whatever call the agent is on. Mute the deskphone, punch keys on it.

Switching All The Lines Over

I have 3 DID (Direct Inward Dial) numbers assigned to my SIP trunk. By default, all of these drop you in to my ivr of madness. I have my deskphone setup to mimic lines; extensions basically have a pre-configured outgoing caller-id. There is some logic in the trunks context to send each DID to a specific extension; but the IVR itself does this based on an extension variable set in the trunks context. This means I can just redirect everyone to the queue by modifying just my IVR dialplan; but it also makes it stupid easy to automate this by phone so I dont have to manually update files and reload the dialplan.

I had hoped to make everything 100% automated; and its possible I can still do that…just using external bash scripts. In the meantime, why cant I just punch a number to flip everything to the queue and another number to return it to “normal”.


exten = 1000,1,Set(DB(callin/stream)=1)
same = n,Playback(hello)
same = n,Playback(goodbye)
same = n,Hangup()

exten = 1001,1,Set(DB_DELETE(callin/stream)=0)
same = n,Playback(goodbye)
same = n,Playback(hello)
same = n,Hangup()

Im going to make use of Asterisks internal database because its easiest. When I dial 1000 from the spys extension; I create a key called stream under the callin family. I dont care about the value; Im going to work on the existence of the key, deleting it to turn the callin queue off.


same = n,GoToIf($["${DB_EXISTS(callin/stream)}" = "1"]?inbound-ivr,[callin],1:normal)
same = n(normal),Background(if-u-know-ext-dial)

exten = [callin],1,Playback(phoneintro)
same = n,Queue(callin,h)

* I have substituted [callin] for the actual extension, suckers.


If the key exists, then we basically jump to the extension for the callin queue. For the time being, I just have this mapped to an actual ivr extension on my DIDs; which allows me to pre-queue people before flipping the lines over.