- PVSM.RU - https://www.pvsm.ru -

CRMDANCER — Простая и бесплатная CRM на Flask и интеграцией с Asterisk

Все началось с того, что мои друзья попросили подобрать CRM-систему для менеджеров оптовой компании. Мы потратили уйму времени на поиски, но ничего подходящего не нашли. В итоге я предложил написать написать свою CRM. Удивительно, но готовый продукт внедрили сразу, без долгой «раскачки». Система работает и выложена в открытом доступе. В этой статье я расскажу, как ее сделал, — возможно, кому-нибудь пригодится.

Видео с демонстрацией возможностей

Для начала посмотрите ролик, мы сняли его для менеджеров. Показали, как работает CRM и какие функции в ней реализованы.

Инициация исходящего звонка

image

Исходящий звонок клиенту инициировали с помощью Python AMI Client [1] и
Asterisk Manager Interface (AMI) [2]
Что сделали:

  1. На Астериске прописали логин и пароль для AMI в файле /etc/asterisk/manager.conf
  2. Перезапустили Астериск и открыли на нем порт 5038 для соединения с нашим CRM-сайтом
  3. На сервере с CRM поставили AMI Client командой:
    pip install asterisk-ami

  4. Убедились, что все звонит с помощью этого кода:
    from asterisk.ami import SimpleAction
    client = AMIClient(address='your-remote.freepbx.net',port=5038)
    client.login(username='username',secret='password')
    action = SimpleAction(
        'Originate',
        Channel='SIP/974957777777',  # Внешний городской номер, на который надо позвонить
        Exten='2010',   # Инициатор вызова, внутренний номер на Астериске
        Priority=1,
        Context='default',
        CallerID='python',
    )
    client.send_action(action)
    

Затем прикрутили результат к нашему сайту на Flask, в итоге с сайта можно звонить нажатием кнопки на мышке. Работает так: менеджер кликает и у него звонит SIP-телефон. Когда он берет трубку — начинается вызов клиенту.
Астерисков у ребят два: один основной, другой — резервный, на случай выхода из строя основного. Поэтому в файле aster.py я прописал проверку на доступность с помощью socket и try.

Прием входящего звонка

image

Когда у менеджера звонит SIP-телефон, CRM показывает ему окно, как на скриншоте. Пробовали разные варианты, но написали в итоге на Perl, с помощью модуля Asterisk::AMI [3] и EV [4].

use EV;
use Asterisk::AMI;

#Create your connection
my $astman = Asterisk::AMI->new(
    PeerAddr => '127.0.0.1',
    PeerPort => '5038',
    Username => 'admin',
    Secret   => 'supersecret',
    Events   => 'on',
    Handlers => { default => &eventhandler }
);

#Alternatively you can set Blocking => 0, and set an on_error sub to catch connection errors
die "Unable to connect to asterisk" unless ($astman);

#Define the subroutines for events
sub eventhandler { my ( $ami, $event ) = @_; print 'Got Event: ', $event->{'Event'}, "rn"; }

#Define a subroutine for your action callback
sub actioncb { my ( $ami, $response ) = @_; print 'Got Action Reponse: ', $response->{'Response'}, "rn"; }

#Send an action
my $action = $astman->( { Action => 'Ping' }, &actioncb );

#Do all of you other eventy stuff here, or before all this stuff, whichever ..............

#Start our loop
EV::loop

Кому интересно, почитайте статью о событийно-ориентированном программировании [5] — мне она помогла.
Идем дальше. На гитхабе лежит файл с названием amievent.pl. С его помощью происходит отлов входящих звонков, занесение в memcached и в mysql базу.
На сервере у нас стоит Centos 7.
Мы демонизировали этот процесс с помощью systemd:


[Unit]
Description=Crm dancer event incomming call
After=mysql.service

[Service]
Type=simple
User=crmdancer
Group=crmdancer
Restart=always
RestartSec=30
ExecStart=/home/crmdancer/crmdancer/amievent.pl


[Install]
WantedBy=multi-user.target

Работа с таблицей клиентов

image

Для таблицы с клиентами использовали DataTables [6].
Этот jquery-плагин делает из обычной html-таблицы таблицу с поиском, сортировкой, фильтрами, разбиением больших таблиц на страницы и другими полезными функциями.
Это работало с небольшим количеством клиентов. Как только импортировали большую базу, увеличилось время открытия страницы, а общий размер html-таблицы составил 12 000 строк.

Загуглили и нашли в документации [7] следующее:

Обработка на стороне клиента с помощью DOM: ~5'000 строк
Обработка на стороне клиента с загрузкой данных через Ajax: ~50'000 строк
Обработка на стороне сервера — миллион строк

Переделали загрузку на Ajax. Загрузка страницы с 4-х секунд упала до >1 секунды.

Записи звонков

image

У нас не было технической возможности сжимать записи в mp3 на FreePBX.
Поэтому сделали rsync с FreePBX на сайт с CRM.
Принцип такой: файлы перебрасываются в wav формате, потом конвертируются в mp3. Конвертируем с помощью lame [8] и perl-скрипта convertor.pl — он находит wav файлы, которые еще не сконвертированны. Файл length.pl создает csv-файлы в формате «название mp3-файла — время звучания». Для каждого дня свой csv-файл.

Календарь событий

image

Чтобы менеджерам было удобно вести план работ, записывать предстоящие дела и события, мы установили jquery-плагин fullcalendar [9]. К нему дописали возможность хранения событий в mysql и цветовую идентификацию событий.
Как это реализовано можно посмотреть в коде на гитхабе.

Про темплейт

При верстке использовали замечательный темплейт админки:
https://github.com/puikinsh/gentelella [10]
Очень красивый и удобный. Здесь демо-версия:
https://colorlib.com/polygon/gentelella/index.html [11]
Единственный минус — меню слева по умолчанию развернуто. Чтобы получить дополнительное пространства, свернули вот так:
заменили на Других изменений не вносили, все работает из коробки.

CRMDANCER — установка и работа

  • Ставим mysql и memcached
  • Устанавливаем python 3.x
  • Ставим виртуальное окружение
  • Устанавливаем flask
  • Копируем код с гитхаба [12]
  • Ставим все необходимые модули
  • Создаем базу данных с готовой структурой, код sql на гитхабе
  • Правим конфиги и прописываем свои логины и пароли
  • Запускаем

P.S.

Бывает пишешь что-нибудь, используешь передовые технологии, а в итоге никто продуктом не пользуется. У нас были подобные истории, поэтому когда писали Crm Dancer, воспринимали его как прототип, не думали, что менеджерам понравится и они будут им пользоваться. Из-за этого использовали обычный ajax вместо websocket [13] и признаем — в других местах можно было бы написать лучше.
В остальном все в порядке, скачивайте и пользуйтесь.

Автор: crmdancer

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/246639

Ссылки в тексте:

[1] Python AMI Client: https://github.com/ettoreleandrotognoli/python-ami/tree/0.1.0/

[2] Asterisk Manager Interface (AMI): https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=4817239

[3] модуля Asterisk::AMI: http://search.cpan.org/~greenbean/Asterisk-AMI-v0.2.5/lib/Asterisk/AMI.pm

[4] EV: http://search.cpan.org/~mlehmann/EV-4.22/EV.pm

[5] статью о событийно-ориентированном программировании: http://pragmaticperl.com/issues/01/pragmaticperl-01-всё-что-вы-хотели-знать-об-anyevent-но-боялись-спросить.html

[6] DataTables: https://datatables.net

[7] документации: https://datatables.net/faqs/#speed

[8] lame: http://lame.sourceforge.net

[9] fullcalendar: https://fullcalendar.io

[10] https://github.com/puikinsh/gentelella: https://github.com/puikinsh/gentelella

[11] https://colorlib.com/polygon/gentelella/index.html: https://colorlib.com/polygon/gentelella/index.html

[12] Копируем код с гитхаба: https://github.com/crmdancer/crmdancer

[13] websocket: https://flask-socketio.readthedocs.io/en/latest/

[14] Источник: https://habrahabr.ru/post/322684/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best