Добавляем в CRM выбор лучшего времени общения с клиентом

от автора

Привет, Хабр. На связи Екатерина Саяпина, Product Owner платформы МТС Exolve. В этом материале я расскажу, как автоматически добавить в CRM оптимальное время для общения с клиентами. В рамках этого кейса я покажу настройку нашей платформы и получение информации из нее в Битрикс24.

Подготовка МТС Exolve

Подключение

Этот процесс описан в одном из наших предыдущих материалов: нужно завести аккаунт, подписать договор, создать приложение и получить API-ключ. В списке приложений выберите нужное:

Затем перейдите в «Настройки» на панели слева:

Пролистайте до конца вниз и нажмите на кнопку «Подключить» в блоке «Умная проверка номеров»:

Так как это платная услуга, то нужно будет ознакомиться с условиями и тарифами и подтвердить ваше согласие.

Теперь нам доступна работа с Number Lookup API — набором методов, с помощью которых можно получить оптимальное время для звонка. Мы используем GetBestCallTime. Он покажет, когда лучше всего позвонить абоненту. Надо учитывать, что этот метод работает только с клиентами МТС.

Применение

Используем POST-запрос https://api.exolve.ru/hlr/v1/GetBestCallTime. Для авторизации берем Bearer-токен из нашего приложения МТС Exolve, а в тело запроса в JSON-формате добавляем проверяемый телефон. Отправку запроса и преобразование ответа к нужному виду делаем так:

def get_time(time_string: str):    hstr = time_string.split(':')[0]    return time(hour=int(hstr))   def get_time_range(recepient: str):    payload = {'number': recipient}    r = requests.post(r'https://api.exolve.ru/hlr/v1/GetBestCallTime', headers={'Authorization': 'Bearer '+sms_api_key}, data=json.dumps(payload))    print(r.text)    if r.status_code == 200:        ans = json.loads(r.text)        text = ans['result'].split(',')        since, till = [get_time(t) for t in text]        ans.update({'since': since, 'till': till})        return ans    return None

Функция get_time_range отправляет запрос и проверяет успешность передачи информации. В последнем случае при помощи get_time получаем время в часах, соответствующее началу и концу периода, наиболее благоприятного для звонка этому абоненту.

Готовим приложение

Импортируем все необходимые библиотеки, в том числе для работы с CRM Битрикс24, потому что именно в ней будет все храниться:

from flask import Flask import requests import json from dataclasses import dataclass import pandas as pd from fast_bitrix24 import Bitrix from datetime import time from datetime import datetime as dt

База данных

Для синхронизации используем функции получения записей о клиентах и номерах телефонов по их ID. Работу с ними мы описывали в одном из прошлых материалов. Номера, а вместе с ними начало и конец подходящих для звонка интервалов храним в pandas DataFrame. Эта библиотека используется для автоматизации обработки данных, а объекты класса DataFrame можно представить как таблицу с индексацией по строкам и столбцам.

Создадим класс для управления базой данных и инициализируем ее поля:

@dataclass class DB:    UTC_plus = 3    phones_and_clients_table = pd.DataFrame(columns=['phone', 'client_id', 'call_since', 'call_till']).set_index(['phone'])

Они содержат сдвиг в часах вашей временной зоны относительно UTC+0, а также pandas DataFrame с информацией о номерах телефонов.

Синхронизация с CRM

Сначала функция add_client_phones получает номера клиента из Битрикс24 и передает их в локальную базу. Затем push_phone нормализует номер телефона, проверяет его наличие в базе и при необходимости добавляет. У каждого номера должен быть свой уникальный client_id. Если его нет, то он присваивается автоматически.

   def add_client_phones(self, client_full_record):        contact_phones = [ph.get('VALUE', '') for ph in client_full_record.get('PHONE', [{}])]        if len(contact_phones) == 0:            return 0        for c in contact_phones:            self.push_phone(c, client_ID=[client_full_record.get('ID')])        return 1  def push_phone(self, phone, client_ID=None, since=None, till=None, replace=False):    phone_str = str(phone).replace(' ', '').replace('(', '').replace(')', '').replace('-', '').replace('+', '')    if not replace and phone_str in self.phones_and_clients_table.index:        return    if client_ID is None:        client_ID = self.phones_and_clients_table.shape[0]    self.phones_and_clients_table.loc[phone_str, 'client_id'] = client_ID    self.phones_and_clients_table.loc[phone_str, 'call_since'] = since + self.UTC_plus    self.phones_and_clients_table.loc[phone_str, 'call_till'] = till + self.UTC_plus

Получение списка телефонов

При синхронизации с Битрикс24 для новых клиентов устанавливаются значения границ интервалов доступности — когда стоит звонить. Для уже внесенных в базу номеров данные не обновляются повторно — это позволяет избежать лишних запросов к API, если не установлен флаг update=True.

def add_times(self, phone, update=False):    if phone not in self.phones_and_clients_table.index: return    if self.phones_and_clients_table.loc[phone, 'call_since'] and self.phones_and_clients_table.loc[phone, 'call_till'] and not update:        return    ans = get_time_range(phone)    if ans is None: return    self.phones_and_clients_table.loc[phone, 'call_since'] = ans['since']    self.phones_and_clients_table.loc[phone, 'call_till'] = ans['till']

Функция add_times_for_db запускает обновление интервалов для всех телефонов в базе:

def add_times_for_db(self, update=False):    for ph in self.phones_and_clients_table.index:        self.add_times(ph, update)

Функция select_for_call возвращает список номеров, которые подходят для звонка в ближайшее время:

def select_for_call(self, hour=None):    if hour is None: hour = dt.now().hour    since_indexes = self.phones_and_clients_table['call_since'] <= hour + 1    till_indexes = self.phones_and_clients_table['call_till'] >= hour    indexes = since_indexes * till_indexes    temp_table = self.phones_and_clients_table.loc[indexes].sort_values('call_till')    return temp_table.index.to_list()

По умолчанию используется текущий час. Выбираются номера:

  • чей интервал еще не истек (call_till ≥ hour);

  • интервал уже идет или начнется в течение следующего часа (call_since ≤ hour + 1).

Затем список сортируется по времени окончания интервала (call_till) — в приоритете те, у кого он скоро закончится. Номера, которые пока недоступны, в выборку не попадают.

Для экспорта базы телефонов в Excel используем эту функцию:

def dump(self):    self.phones_and_clients_table.to_excel('Dump.xlsx')

Как работает серверная часть

Создаем сервер на фреймворке Flask. Нам достаточно одной функции, возвращающей JSON-объект. Flask автоматически преобразует словарь в объект, который можно передать по сети.

@app.route('/get_phones', methods=['GET']) def get_phones():    ans = crm_pipeline()    return json.dumps(ans)   def main():    app.run(host='0.0.0.0', port=5000)

В результате приложение возвращает JSON, содержащий только номера, упорядоченные в рекомендуемом порядке:

[  "79101????24",  "79101????09",  "79101????32", ]

Можно получить сведения о доступности клиентов, выгрузив JSON для последующей программной обработки. Для этого отправляем запрос по адресу https://<your_server>/get_phones_list и вызываем функцию:

@app.route('/get_phones_list', methods=['GET']) def get_phones_list():    return db.phone_table.to_json(None, 'index')

Она возвращает JSON вида:

{  "79101????00": {    "client_id": 0,    "call_since": 16,    "call_till": 17  },  "79101????01": {    "client_id": 1,    "call_since": 8,    "call_till": 9  },  "79101????03": {    "client_id": 2,    "call_since": 11,    "call_till": 12  } }

В таком виде результат подходит для обработки другими приложениями. Его можно сделать удобным и для чтения человеком. Используем функции экспорта базы данных в Excel и передачи файла по HTTP-запросу:

@app.route('/get_phones_table', methods=['GET']) def get_phones_table():    db.dump()    try:        return send_file('Dump.xlsx', mimetype='application/octet-stream', as_attachment=True)    except Exception as e:        return str(e), 405

Так будет выглядеть полученная таблица:

Отметим, что читать и записывать Excel-файлы умеет много кто: pandas и polars на Python, MatLab, readxl на R

Отметим, что читать и записывать Excel-файлы умеет много кто: pandas и polars на Python, MatLab, readxl на R

Получение номеров телефонов клиентов, заполнение границ интервалов для звонков и создание упорядоченного списка задаются одной небольшой функцией, которая вызывается при каждом запросе определения доступности:

def crm_pipeline():    clients = get_contacts()    for c in clients:        db.add_client_phones(c)    db.add_times_for_db()    ans = db.select_for_call()    return ans

Это решение можно развернуть в докер-контейнере, в том числе на бесплатном хостинге, как мы описывали в другом нашем материале. Информация от МТС Exolve будет обновляться только для номеров, которые добавляются в базу при синхронизации с CRM, выполняемой с каждым запросом.


Мы собрали рабочее решение для автоматического определения лучшего времени звонка и интегрировали его с CRM Битрикс24. В основе — метод одиночной проверки GetBestCallTime, который показывает, когда абонент чаще всего на связи. Для работы с большими списками номеров есть отдельный метод GenerateBestCallTimeReport.


ссылка на оригинал статьи https://habr.com/ru/articles/927506/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *