- PVSM.RU - https://www.pvsm.ru -
Все началось с того, что мои друзья попросили подобрать CRM-систему для менеджеров оптовой компании. Мы потратили уйму времени на поиски, но ничего подходящего не нашли. В итоге я предложил написать написать свою CRM. Удивительно, но готовый продукт внедрили сразу, без долгой «раскачки». Система работает и выложена в открытом доступе. В этой статье я расскажу, как ее сделал, — возможно, кому-нибудь пригодится.
Для начала посмотрите ролик, мы сняли его для менеджеров. Показали, как работает CRM и какие функции в ней реализованы.
Исходящий звонок клиенту инициировали с помощью Python AMI Client [1] и
Asterisk Manager Interface (AMI) [2]
Что сделали:
pip install asterisk-ami
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.
Когда у менеджера звонит 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
Для таблицы с клиентами использовали DataTables [6].
Этот jquery-плагин делает из обычной html-таблицы таблицу с поиском, сортировкой, фильтрами, разбиением больших таблиц на страницы и другими полезными функциями.
Это работало с небольшим количеством клиентов. Как только импортировали большую базу, увеличилось время открытия страницы, а общий размер html-таблицы составил 12 000 строк.
Загуглили и нашли в документации [7] следующее:
Обработка на стороне клиента с помощью DOM: ~5'000 строк
Обработка на стороне клиента с загрузкой данных через Ajax: ~50'000 строк
Обработка на стороне сервера — миллион строк
Переделали загрузку на Ajax. Загрузка страницы с 4-х секунд упала до >1 секунды.
У нас не было технической возможности сжимать записи в mp3 на FreePBX.
Поэтому сделали rsync с FreePBX на сайт с CRM.
Принцип такой: файлы перебрасываются в wav формате, потом конвертируются в mp3. Конвертируем с помощью lame [8] и perl-скрипта convertor.pl — он находит wav файлы, которые еще не сконвертированны. Файл length.pl создает csv-файлы в формате «название mp3-файла — время звучания». Для каждого дня свой csv-файл.
Чтобы менеджерам было удобно вести план работ, записывать предстоящие дела и события, мы установили jquery-плагин fullcalendar [9]. К нему дописали возможность хранения событий в mysql и цветовую идентификацию событий.
Как это реализовано можно посмотреть в коде на гитхабе.
При верстке использовали замечательный темплейт админки:
https://github.com/puikinsh/gentelella [10]
Очень красивый и удобный. Здесь демо-версия:
https://colorlib.com/polygon/gentelella/index.html [11]
Единственный минус — меню слева по умолчанию развернуто. Чтобы получить дополнительное пространства, свернули вот так:
заменили на Других изменений не вносили, все работает из коробки.
Бывает пишешь что-нибудь, используешь передовые технологии, а в итоге никто продуктом не пользуется. У нас были подобные истории, поэтому когда писали 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
Нажмите здесь для печати.