Автоматична система обдзвону клієнтів

Передісторія

На даний момент з моменту впровадження минуло вже понад рік. Зараз переписано близько 90-95% всього програмного коду системи і я чітко уявляю, як розвивається і як повинна розвиватися система. Але на той момент коли переді мною поставили завдання, у мене було розпливчасте уявлення як повинен виглядати код судячи з ТЗ, а досвіду у спілкуванні з asterisk'ом не було в принципі. Відразу скажу, що основна ідея не моя, моє завдання було реалізувати або навіть швидше зобразити, що було розписано в умовах завдання. Але при цьому, що найважливіше, я був практично нічим не обмежений у виборі технологій і способів вирішення — що, на мій погляд, дозволило мені довести всю схему до виду, який я хотів.

На той момент у компанії вже був робітничий кол-центр. Близько 10 черг, від 4 до 20 операторів у кожній черзі та близько 12-15 тисяч дзвінків цілодобово. 5 провайдерів для місцевих, міжміських та міжнародних викликів. За довгий час кол-центр обріс великою кількістю різного функціоналу та власних розробок. Основний софт платформою є сервери на астерисках, база зі статистикою дзвінків і бізнес логікою на MySQL, а також обв'язування зі скриптів на AGI.

План реалізації

Втілення у життя

Планування
Робота з оператором
Балансування та резервування

Якщо пошукати в інтернеті інформацію на тему як резервувати вихідні дзвінки за недоступності одного з провайдерів — найчастіше ви знайдете рекомендацію типу зробити в діаплані кілька Dial'ів один за одним. Мені ж зі свого боку хотілося зробити щось більш гнучке і динамічне. Основна проблема була в тому, що у нас використовується багато різних залізок і перед формуванням дзвінка немає часу обходити їх у пошуках найменш навантаженого ідоступного транку. Основна ідея була в тому, щоб працювати вже з агрегованою інформацією. Для цього був написаний окремий демон, завдання якого зводилися до наступного: збирати з різного типу обладнання навантаження, з TDM різниця зайнятих слотів до вільних, з провайдерів voip доступність через sip peers і на основі поточних каналів з астерісків вибирати хто більше з них використовується. Але крім вибору напрямку треба враховувати, що будь-який з транків може впасти, тому треба вибрати альтернативу. У моєму випадку це були провайдери voip, але у них так само існують свої розцінки, тому балансування має вибирати з найбільш дешевих до дорогих (за якістю вони абсолютно рівнозначні). Для такої вибірки я зробив для кожного провайдера свою вагу або вартість. Таким чином вийшов список із ключів та значень такого виду:

  • Місцева зона => 10
  • Voip провайдер А => 20
  • Voip провайдер B => 30
  • Voip провайдер C => 40
Так якщо один із провайдерів не доступний — ми шукаємо йому заміну з нижчого до вищого чи дорогого.

Але повз це існує ще й розподіл залежно від належності номера. Тобто на номери Москви, наприклад, слід дзвонити через свого місцевого провайдера, до Сибіру або інших країн через свого, якщо такий є, або через міжміський і міжнародний напрямок. Тут виникає потреба визначити, до якого регіону належить номер. Для цього я написав парсер реєстру розв'язку в базу. Виглядає як:

Загальна схема роботи та різні нюанси

  • Для кожного завдання на поточний момент можна задати кількість спроб, що декрементується кожним статусом«не виконано». Через знову таки задаємо тайм завдання зновуповернеться на видачу.
  • Також недавно з'явилася можливість пріоритетно зробити додзвон через заданого провайдера, якщо він не перевантажений і доступний.
  • Демон для агрегації навантаження по voip провайдерам орієнтується префіксом в назві sip каналів. Можна було б використовувати наприклад групи, але поки що все чудово працює.
  • Він же періодично пінгає як Voip провайдери так і всі залізниці на шляху до транків, якщо якась із них не доступна — провайдер виводиться з балансування.
  • Після вибору вільного оператора він позначається як зайнятий і відбувається наступна логіка:
  1. Ми запитуємо всі asterіsk сервери, що знаходяться в балансуванні.
  2. Для поточної черги запитуються параметри дзвінків. Це час очікування відповіді від оператора та клієнта,«source»номер який побачить клієнт при вхідному дзвінку — для кожних служб він різний.
  3. За номером клієнта вибираємо напрямок через таблицю з реєстром номерів.
  4. Вибираємо аплінк для виклику. Якщо виставити 0 - аплінк не братиме участі у виборі, відповідно можна балансувати зрушуючи пріоритет провайдерам. Вибраний транк перевіряться на доступність, а потім на завантаження. Якщо умов не дотримано, аплінк пропускається і ми переходимо до наступного за вагою.
  5. Далі ми вибираємо asterіsk сервер з найменшою кількістю каналів. Оскільки в Redis'e у нас зберігаються всі канали з усіх серверів, ми просто отримуємо загальну кількість через вбудовану функцію hlen. Вибравши сервер, ми намагаємося до нього підключиться - якщо сервер не відповів, ми беремо наступний знову з найменшим навантаженням.
  6. Наостанок для параметра «variable» формується масив із службових змінних, на основі яких відбувається як облік дзвінка, так і вибір напрямку. Так само тамвикористовується прив'язка поточного дзвінка оператора до завдання, за яким відбувається обдзвон.