Automatic customer call system

How to make automatic dialing has already been written a lot, including on this site. The flexibility of asterisk has no limits. A huge number of articles have been written on the implementation of simple actions by built-in tools, or using third-party products and solutions. Therefore, in my opinion, it will be most interesting to solve a non-standard task, for which I had to completely develop and implement a system from scratch, given compatibility with the current call center scheme.

Background


At the moment, more than a year has passed since the introduction. Now about 90-95% of the entire program code of the system has been rewritten, and I clearly imagine how the system is developing and how the system should develop. But at that moment when the task was set for me, I had a vague idea of ​​what the code should look like, judging by the TOR, and there was basically no experience in communicating with asterisk. I must say right away that the main idea is not mine, my task was to realize or even rather depict what was painted in the conditions of the task. But at the same time that the most important thing was that I was practically unlimited in the choice of technologies and solutions - which, in my opinion, allowed me to bring the whole scheme to the form that I wanted.

At that time, the company already had a working call center. About 10 lines, from 4 to 20 operators in each line 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 has overgrown with a large number of different functionalities and own developments. The main software platform are asterisk servers, a database with call statistics and business logic in MySQL , as well as binding from scripts to AGI .

Task


From time to time there is a need to call customers on various kinds of issues. This may be necessary both periodically, for example once a month (debt, notifications on promotions or advertising), as well as occasionally (unresolved problems, technical issues). Currently, several people are being selected from the queue who are phoning such clients and compiling a report on their work, which is then transferred to each other. If there is a large queue of calls, the operators are returned to incoming calls and they help to eliminate the load, then they come back again. As a result, many problems arise both of the human factor, a person can make a mistake in the number, forget one of the clients, or in the administrative department - it is necessary to monitor the queue and transfer operators from dialing to incoming calls and back.
Therefore, some kind of machine is required that will ring up the right customers by itself, taking into account the load in the queue, so that for simple questions, you can ring only at the minimum load, and for more complex and important ones, it is a priority even over incoming calls. The machine must directly connect the operator and the client, so that the client immediately begins a conversation with a living person, without passing IVR'aor waiting in line. The operator who was chosen to call - must remain in the queue and after the end of the conversation without any action could take a regular incoming call or another call. After connecting with the operator, a timeout is needed in order for the operator to understand 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 to comment on the task. The planned load of 2,000 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:
  • A task scheduler is required. It is necessary to formulate the tasks themselves, to issue them both at any time and at a specific time (no one will need information on the balance at 3 a.m.). It is necessary to monitor the load of the queue so that dialing tasks do not clog all the slots and do not interfere with regular incoming calls.
  • Need a mechanism for direct connection of the operator and the 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 agent must be busy and the call must be counted in statistics.
  • If possible, minimal operator involvement is required. Here, in this case, I mean the maximum automation on the part of the operator. An incoming call should come for him, the operator will answer it, and the system will make the client’s call itself, choosing both the direction and changing the status of the task, regardless of whether we got through to the client. I will explain in more detail below.
  • Balancing and redundancy of both outgoing directions and the asterisk servers themselves is required. Also, when choosing an outgoing provider, you need to consider that in addition to belonging to the zone, there is also a different cost of the call.
  • Be sure to reserve a call tracking logic. If the operator does not answer the call, the computer freezes or hesitates, the call should not simply fall off and be lost. If during a normal incoming call, we receive the “AgentRingNoAnswer” event , we can simply select another operator, then in this case we get a bottleneck between the operator’s response and the client’s dial-up.

Realization


Planning

Now, over time, it is difficult to recall the execution chronology, but the first thing I decided right away was that all planning should be in billing. Tasks will be formed on the basis of the existing client and its features: phone numbers tied to it, balance, status of equipment tied to it, business statistics, etc. Initially, it was clear what broad prospects such automation provides for communication with the client. We can ask everyone - who having connected any service - does not use it. When a new firmware is released, call customers to offer updates if there is no auto provisioning. Warn customers about planned activities, or intrusively promote additional company services. But such a task from the technical side of telephony simply looks like a phone number and the reason we are calling.
We can call on a trivial problem, but we may need a more urgent connection with the client - for example, during a conversation 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 priority. You can call him at any time and in the first place.
Each task has its own solution. If we have a question of an administrative plan, we solve it through the subscription department. The technical issue is resolved through technical support. Accordingly, when forming a task, you need to choose which service will receive the task.

As a result, a view was made through the internal logic of the billing, from which the task can be obtained in the form of: task id, phone number, priority, and the queue for which the task was formed. For my part, a daemon was written that constantly monitors the queue load - it takes the current number of calls waiting for a response to the maximum allowable percentage, as well as the number of free operators. For example, for tasks with low priority, 0% queue loading and at least 1 free operator are required. If the priority is higher then the load is not more than 70%. For a higher priority, the load is not equal to 100%. Under favorable conditions, the demon, having received the task, changes its status to “in progress”and throws the call in line. The task number is recorded in the local daemon cache, by which the task is monitored in the dictionary table in our database of the form: task id -> dialstatus . If a status has appeared, then we change the status of the task in billing and delete the task from the dictionary and local cache. Accordingly, at the start of the daemon, all tasks with the status “are executed” are collected from billing to monitor their status.

Work with the operator

Developing a scheme for working with an operator, I highlighted the main characteristics:
  • The process of selecting and connecting with the operator should be as quick as possible. Any busy operator should be checked for unemployment and suitability for a call in no more than 3-5 seconds.
  • If a failure occurs and the operator does not answer the call or the computer freezes. It is necessary to escort the call back to the queue, and remove the problem operator from the queue.
  • It is necessary to monitor the status of the call so that at the end of the call to put the status of the task is either completed or not.

When I began to search for a solution, I immediately realized that the current statistics would not correctly see and not count the call to the operator as successful, besides it was not clear how to pull out the operator to receive such special calls, so attempts were made to make crutches like making a call to the operator and bridging it with the client’s shoulder, 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 processing incoming calls of a new type. The mechanism for working with the operator was selected Originate . Many put call file as opposed tothe technology is certainly cool, and it has a right to exist for some tasks, but it wouldn’t be suitable for my task: to connect to the operator, an instant answer is required, whether the operator can accept the call or not, in the case of a file you need to monitor the file itself, which from my point of view not convenient and not rational. I just do an ami request, wait about 5 seconds, if the operator picks up the phone, 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 the asterisk for which it is formed. That is, we will need a certain local script on each server that will form a file locally, simultaneously finding out which of the other server colleagues is currently the least loaded. Complete decentralization of management. In my case, I just request a load on all servers and choosing the least loaded I send him an ami request. The library was used for workAsterisk :: AMI .
After the operator answered the call, in the formed call through local variables, “exten” is selected for the call, where a timeout is set via “Wait” before “Dial” . The main problem was that if the operator thinks at the time of a timeout or call and hangs up, the call disappears. Therefore, when dialstatus'a “Cancel” arrives , the call is considered not processed and is returned to the queue. Deactivate the operator from the queue. Further on dialplan'y Dial with an additional parameter “g” occurs to continue execution after the end of the conversation. At the end of the timeout after talking through the "hangup"Exten is added an entry in the dictionary - job identifier and dialstatus, insert request. Thus, having a working machine and a client sip on it, turning on the “auto answer” function - the operator only comments on tasks in the interface, all call processing takes place for him. Maybe someone will say that this is a bad idea, but this is done for several reasons. If we want clients guaranteed to try to get through 30 seconds, then we just set the timeout for the dial command, and do 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 know firsthand how tedious the same actions are, besides 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 Dial'ov one after another in the diaplan. For my part, I wanted to do something more flexible and dynamic. The main problem was that we used a lot of different pieces of iron and before forming a call there was no time to go around them in search of the least loaded and available trunk. The main idea was to work with aggregated information already. To do this, a separate daemon was written, the tasks of which were as follows: to collect the load from different types of equipment, with TDM the difference between occupied slots to free, from voip providers accessibility via sip peersand based on the current channels from the asterisk, choose who is most used. But in addition to choosing the direction, one must 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 prices, so balancing should choose from the cheapest to the most expensive (they are absolutely equivalent in quality). For such a selection, I made for each provider my weight or my cost. Thus, a list of keys and values ​​of this type was obtained:

  • Local Area => 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 expensive.

But besides this, there is also a distribution depending on the room. That is, Moscow numbers, for example, should be called through your local provider, to Siberia or other countries through your own, if any, or through an intercity and international destination. Here there is a need to determine which region the number belongs to. For this, I wrote a parser of the communications registry in the database. Looks like:

mysql> select local.get_region(903599****);
+-------------------------------------+
| voicecon_new.get_region(903599****) |
+-------------------------------------+
| Moskva i Moskovskaya oblast         |
+-------------------------------------+


General scheme of work and various nuances


  • For each task at the moment, you can set the number of attempts, which is decremented by each status of "failed" . After the set timeout again, the task will again return to the issuance.
  • Also recently an opportunity has appeared to make priority dialing through a given provider, if it is not overloaded and available.
  • The daemon for load aggregation by voip providers is oriented by the prefix in the name of sip channels. One could use for example groups , but so far everything is working fine.
  • He periodically pings both Voip providers and all the glands on the way to trunks, if which one is not available - the provider is removed from the balance.
  • After selecting a free operator, it is marked as busy and the following logic occurs:

  1. We request all asterisk servers that are in balance.
  2. For the current queue, the call parameters are requested. This is the waiting time for a response from the operator and the client, the "source" number that the client will see with an incoming call - it is different for each service.
  3. By customer number, select the direction through the table with the register of numbers.
  4. Select the uplink to call. If you set 0, the uplink will not participate in the selection, so you can balance by shifting the priority to providers. The selected trunk will be checked for availability and then for download. If the conditions are not met, the uplink is skipped and we move on to the next one by weight.
  5. Next, we select the asterisk server with the fewest channels. Since Redis has all the channels from all servers, we just get the total through the built-in hlen function . Choosing a server, we try to connect to it - if the server does not respond, we take the next one, again with the least load.
  6. Lastly, for the “variable” parameter , an array of service variables is formed, on the basis of which both call accounting and direction selection are performed. It also uses the binding of the current operator’s call to the task for which the call is being made.


Conclusion


Despite the declared load, now the figure has reached about 30,785 calls in the last month. Initially, the scheme, planned only for a narrow range of tasks, has grown to a service capable of solving an extensive range of tasks and will cope with a large load. At the moment, work on it has not stopped - I’m constantly adding something and expanding the functionality, as well as planning to conduct another refactoring.

Also popular now: