How to make automatic dialing has already been written a lot, including on this site. Flexibility
asterisk'a has no boundaries. Written a huge number of articles on the implementation of the simplest actions with built-in tools, or using third-party products and solutions. Therefore, in my opinion, the most interesting solution would be not a standard task, for which it was necessary to completely develop and implement a system from scratch, given compatibility with the current call center scheme.
Prehistory
At the moment, more than a year has passed since its introduction. About 90-95% of the total program code of the system has been rewritten and I clearly understand how the system is developing and how it should be developed. But at that moment when the task was set before me, I had a vague idea of ​​how the code should look like judging by the TK, and there wasn’t any experience in dealing with asterisk. I’ll say right away that the main idea is not mine, my task was to implement or even rather portray what was painted in the context of the task. But at the same time, the most important thing was that I was virtually unlimited in choosing technologies and solutions - which in my opinion allowed me to bring the whole scheme to the form I wanted.
At that time, the company already had a working call center. About 10 queues, from 4 to 20 operators in each queue and about 12-15 thousand calls around the clock. 5 providers for local, long-distance and international calls. For a long time, the call center was overgrown with a large number of different functional and own developments. The main software platform is an asterisk server, a database with call statistics and business logic on
MySQL , as well as an
AGI script binding.
Task
From time to time there is a need to ring up clients for various types of questions. This may be needed periodically, for example, once a month (debt, notifications for promotions or advertising), as well as occasionally (unsolved problems, technical issues). To date, several people are selected from the line-up who ring up such clients and form a report on their work, which is then transmitted to each other. If there is a large queue of calls - the operators return to incoming calls and they help to eliminate the load, then come back again. As a result, there are many problems as a human factor, a person can make a mistake in the room, forget one of the clients, or in the administration department - you need to monitor the queue and transfer agents from telephone calls to incoming calls and back.
Therefore, you need a machine that will ring up the right customers, while taking into account the load in the queue, so that for simple questions, you can call around only with a minimum load, and for more complex and important - even over incoming calls. The machine should directly connect the operator and the client, so that the client immediately began a conversation with a live person, without going through the
IVR or waiting in the queue. The operator of which was chosen for the call - must remain in the queue and after the end of the conversation without any action could take a normal incoming call or another call. After connecting with the operator, you need a timeout in order for the operator to grasp the essence of the call - to understand why he is calling the client, and after the call is completed, regardless of the result, the timeout is for commenting out the task. The planned load of 2000 calls per month and then in the future up to 10 000 - 15 000.
')
Implementation plan
Judging by the description of the conditions of the problem we form the technical model:
- Task scheduler required. It is necessary to form the tasks themselves, to issue them both at any time and at a certain time (no one will need information on the balance at 3 am). It is necessary to monitor the queue load so that the ringing tasks do not clog all the slots and do not interfere with normal incoming calls.
- Need a mechanism to directly connect the operator and client. First of all, choosing a free operator, the operator’s shoulder rises, and only then the client’s shoulder. At the time of the call, the entire queue processing logic should be preserved. The operator must be busy and the call must be counted in the statistics.
- If possible, the minimum participation of the operator. Here, in this case, I mean maximum automation by the operator. For him, an incoming call must come, the operator answers it and the system makes the client’s call, choosing both the direction and the status of the task itself, regardless of whether we called the client or not. I will explain in more detail below.
- It requires balancing and reservation of both outgoing directions and the asterisk servers themselves. Also, when choosing an outgoing provider, you need to consider that in addition to belonging to the zone, there is also a different call cost.
- Be sure to backup call tracking logic. If the operator does not answer the call, the computer freezes up or changes his mind, the call should not just fall off and be lost. If you receive an “AgentRingNoAnswer” event during a normal incoming call, we can simply choose another operator, in this case we get a bottleneck in the interval between the operator’s response and dialing the client.
Life time
Planning
Now, after the passage of time, it is difficult to recall the chronology of execution, but the first thing I immediately decided was that all planning should be in billing. Tasks will be formed on the basis of an existing client and its attributes: phone numbers tied to it, balance, equipment tied to it, business statistics, and so on. Initially, it was clear what wide prospects such automation provides for communication with the client. We can request all - who connected to any service - does not use it. When a new firmware is released, call the customers to suggest upgrades if there is no
auto provisioning . Warn customers about planned work, or push forward to additional company services. But such a task from the technical side of telephony just looks like a phone number and the reason for which we are calling.
We can call for a banal problem, but we may need a more urgent connection with the client - for example, during a call, the call was interrupted. We need to contact the client as soon as possible and solve his question. Therefore, we need such a parameter as a priority. You can call him at any time and first of all.
Each task has its own solution. If we have a question of the administrative plan, we solve it through the subscriber department. The technical issue is solved through technical support. Accordingly, when creating a task, you need to choose which service will receive the task.
As a result, a billing view was made through the internal logic of the billing, from which you can get a task in the form of: task id, phone number, priority and queue for which the created task. For my part, a demon was written that constantly monitors the queue loading - takes the current number of calls waiting for an answer to the maximum allowed in percent, as well as the number of free operators. For example, for tasks with low priority, 0% queue loading and at least 1 free agent are required. If the priority is higher then the load is not more than 70%. For higher priority loading is not equal to 100%. Under favorable conditions, the demon receiving the task, changes its status to
"executed" and throws the call into the queue. The task number is recorded in the local cache of the daemon, by which the task is monitored in the dictionary table in our database of the form:
task id -> dialstatus . If the status has appeared, then we change the status of the task in the billing system and delete the task from the dictionary and local cache. Accordingly, when the daemon starts, all tasks with the status
“run” are collected from the billing to monitor their status.
Work with the operator
Developing the scheme of work with the operator - I highlighted the main characteristics:
- The process of selection and connection with the operator should be as fast as possible. Any busy operator should be checked for idleness and suitability for a call in no more than 3-5 seconds.
- If fail occurred and the operator did not answer the call or the computer freezes. It is necessary to escort the call back to the queue, and withdraw the problem operator from the queue.
- It is necessary to track the status of the call so that upon completion of the call, set the status of the task either completed or not.
When I began to look for a solution, I immediately realized that the current statistics did not correctly see and call off the call to the operator as successful, besides, it was not clear how to pull the operator to receive such special calls, therefore attempts were made to make crutches such as dialing to the operator and parrying it with the shoulder of the client, as well as various frauds with queue slots. The collected schemes were working, but extremely unstable and time consuming when collecting statistics. As a result, I refused to try to adapt to the current scheme and decided to write an independent part of the logic for handling incoming calls of a new type. The mechanism for working with the operator was selected. Many people oppose the
call file, the technology is certainly cool, and for some kind of tasks it has the right to exist, but it wouldn’t work for my task: to connect with the operator, an instant response is required, whether or not the operator can accept the call, in the case of a file, you need to monitor the file itself that from my point of view is not convenient and not rational. I just fulfill the ami request, wait about 5 seconds, if the operator picked up the phone, then the call goes to the next stage, if not, then return the call to the queue and look for the next operator. Also, files are characterized by local work on an asterisk, for which it is formed. That is, we will need a local script on each server, which will locally generate the file, simultaneously finding out who of the server's colleagues is the least loaded now. Complete decentralization of management. In my case, I simply request the load on all servers and selecting the least loaded one sends him an ami request. For the work was used library
Asterisk :: AMI .
After the operator answers the call, in the formed call through local variables, the
“exten” is selected for the call, where the timeout through
“Wait” before the
“Dial” is set . The main problem was that if the operator deliberates at the time of the timeout or the call and hangs up - then the call disappears. Therefore, when a dialstatus
“Cancel” is received
, the call is considered not processed and returns to the queue. We deactivate the operator from the queue. Next on dialplan, there is a
Dial with an additional parameter
“g” to continue execution after the end of the conversation. Upon completion of the timeout after the conversation through the
“hangup” extension, an entry is added to the dictionary - job ID and dialstatus,
insert by request. Thus, having a working machine and a client sip on it, enabling the
“auto answer” function — the operator only comments on the tasks in the interface, all call processing takes place behind it. Maybe someone will say that this is a bad idea, but this was done for several reasons. If we want our customers to try guaranteed to get a phone call for 30 seconds, then we simply set a timeout for the dial command, and not wait for any action from the operator. Again, we get a scheme in which monotonous work for people is reduced. It so happened that at the very beginning I worked in the same company as a technical support operator and I do not know by hearsay how tedious the monotonous actions are, by the customers themselves.
Balancing and reservation
If you search the Internet for information on how to reserve outgoing calls when one of the providers is unavailable - most often you will find a recommendation of the type to make several Dials in diaplan one after the other. For my part, I wanted to do something more flexible and dynamic. The main problem was that we used a lot of different glands and before forming a call there is no time to bypass them in search of the least loaded and accessible trunk. The main idea was to work with aggregated information. To do this, a separate daemon was written, whose tasks were as follows: collect load from TDM, TDM difference between free slots, accessibility through
sip peers from voip providers, and choose asterisks based on current channels from asterisks. But besides the choice of direction, it is necessary to take into account that any of the trunks can fall, so you need to choose an alternative. In my case, these were voip providers, but they also have their own rates, so balancing should choose from the cheapest to the most expensive ones (they are absolutely equivalent in quality). For such a sample, I made my own weight or value for each provider. Thus, we have a list of keys and values ​​of this type:
- Local zone => 10
- Voip provider A => 20
- Voip provider B => 30
- Voip provider C => 40
So if one of the providers is not available - we are looking for a replacement from lower to higher, or more expensive.
But in addition to this, there is also a distribution depending on the number of the room. That is, for example, you should call Moscow numbers through your local provider, to Siberia or other countries through yours, if you have one, or through a long-distance and international route. Here there is a need to determine to which region the number belongs. For this, I wrote a
registry parser to
connect to the database. Looks like:
mysql> select local.get_region(903599****); +
The general scheme of work and various nuances
- For each task at the current time, you can set the number of attempts, which is decremented by each status “not executed” . After a timeout, the task will again return to the output.
- Just recently, it became possible to prioritize dialing through a given provider, if it is not overloaded and accessible.
- The daemon for load aggregation by voip providers is prefixed in the name of the sip channels. One could use for example groups , but for now everything works fine.
- He periodically pings both Voip providers and all the glands on the way to trunks, if which of them is not available, the provider is removed from balancing.
- After selecting a free operator - it is marked as busy and the following logic occurs:
- We request all asterisk servers that are in balance.
- Calling parameters are requested for the current queue. This is the waiting time for a response from the operator and the client, the “source” number that the client sees when an incoming call is received - for each service it is different.
- According to the client number, we choose the direction through the table with the register of numbers.
- Select the uplink for the call. If you set 0 - uplink will not participate in the selection, respectively, you can balance by shifting the priority to providers. The selected trunk is checked for availability and then for download. If conditions are not met, uplink is skipped and we move on to the next in weight.
- Next, we select the asterisk server with the least number of channels. Since in Redis we have all the channels from all servers, we just get the total through the built-in function hlen . Choosing a server, we are trying to connect to it - if the server did not respond, we take the next one, again with the lowest load.
- Lastly, for the variable parameter, an array of service variables is formed, on the basis of which both the call accounting and the direction selection are performed. Also, there is used the binding of the current call of the operator to the task for which calls are made.
Conclusion
Despite the stated load, the figure has now reached about 30,785 calls in the last month. Initially, the scheme planned only for a narrow circle of tasks has expanded to a service capable of solving a wide range of tasks and will cope with a large load. At the moment, work on it has not stopped - I constantly add something and expand the functionality, and also plan to conduct another refactoring.