Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Пример использования REST API для генерации сотрудников
В релизе 2023.2 реализовано API для быстрого создания большого количества сотрудников, в процессе тестирования мы описали алгоритм генерации нового сотрудников в ChatGPT и провели нагрузочное тестирование по созданию 700 произвольных учетных записей на разных языках. На загрузку ушло около 1 минуты, тест прошел успешно.
Для выполнения этой задачи был написан запрос к ChatGPT следущего содержания.
Мы разрабатываем сервер телефонии, и нам нужно протестировать функцию
программного создания учетных записей сотрудников.
Для получения структуры данных необходимо выполнить следующий GET запрос с пустым ID
http://127.0.0.1:8081/pbxcore/api/extensions/getRecord?id=
В ответ мы получим JSON примерно такого содержания:
{"jsonapi":{"version":"1.0"},
"result":true,
"data":
{
"type":"SIP",
"show_in_phonebook":"1",
"is_general_user_number":"1",
"public_access":"0",
"number":"275",
"user_id":"",
"user_avatar":"",
"user_username":"",
"user_email":"",
"mobile_uniqid":"EXTERNAL-7870338E",
"mobile_number":"",
"mobile_dialstring":"",
"sip_uniqid":"SIP-PHONE-5A230111",
"sip_secret":"Nk9oOFI2ZXdDMEU9",
"sip_type":"peer",
"sip_qualify":"1",
"sip_qualifyfreq":60,
"sip_enableRecording":"1",
"sip_dtmfmode":"auto",
"sip_transport":" ",
"sip_networkfilterid":"none",
"sip_manualattributes":"",
"fwd_ringlength":45,
"fwd_forwarding":"",
"fwd_forwardingonbusy":"",
"fwd_forwardingonunavailable":""
},
"messages":[],
"function":"getRecord",
"processor":"MikoPBX\\PBXCoreREST\\Lib\\Extensions\\GetRecord::main",
"pid":42448,
"meta":{
"timestamp":"2023-09-06T08:51:51+03:00",
"hash":"a16f973985cb3d0cbe75abb2c10a7c39b2203a87"
}
}
Если параметр result в ответе будет равен true, то возьмем данные для создания нового пользователя
из параметра data и выполним POST запрос на адрес http://127.0.0.1:8081/pbxcore/api/extensions/saveRecord,
используя полученный после модификации массив полей формы отправив его в формате x-www-form-urlencoded.
Скрипт должен быть написан на Python без использования NodeJS и позволять создать 100 различных сотрудников.
В поле user_username вписываем имя и фамилию произвольного человека, чтобы использовались национальные алфавиты.
В поле user_email произвольный адрес электронной почты.
В поле mobile_number произвольные мобильные номера телефонов, только цифры, без дублей.
В поле fwd_forwarding, fwd_forwardingonbusy заполнить значениями mobile_number.
При создании сотрудника, вывести user_username, user_email и number в консоль и сообщить о результате.
Если в результате запроса к saveRecord или getRecord мы получили result равный false,
то также в консоль вывести параметр messages из полученного ответа.
Если в результате ответа мы получили ошибку другого типа, или ответ невозможно разобрать,
то вывести в консоль полный текст полученный от API сервера.
В результате был сгенерирован Python скрипт, который позволил выполнить этот тест.
import requests
import random
from faker import Faker
def generate_random_mobile():
return ''.join(random.choice("1234567890") for _ in range(11))
def main():
faker_ru = Faker('ru_RU')
faker_en = Faker('en_US')
faker_zh = Faker('zh_CN')
faker_ja = Faker('ja_JP')
for number in range(1, 699):
# Получение шаблона данных для нового сотрудника
get_response = requests.get("http://127.0.0.1:8081/pbxcore/api/extensions/getRecord?id=")
if get_response.json().get('result', False) == False:
print(f"getRecord error: {get_response.json().get('messages', 'Unknown error')}")
continue
data = get_response.json().get('data', {})
# Генерация произвольных данных
locale = random.choice(['ru_RU', 'en_US', 'zh_CN', 'ja_JP'])
faker = locals()[f"faker_{locale.split('_')[0].lower()}"]
data['user_username'] = faker.name()
data['user_email'] = faker.email()
data['mobile_number'] = generate_random_mobile()
data['fwd_forwarding'] = data['mobile_number']
data['fwd_forwardingonbusy'] = data['mobile_number']
# Создание нового сотрудника
post_response = requests.post("http://127.0.0.1:8081/pbxcore/api/extensions/saveRecord", data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
if post_response.json().get('result', False):
print(f"Created user {number} -> Username: {data['user_username']}, Email: {data['user_email']}, Mobile: {data['mobile_number']}")
else:
print(f"saveRecord error for user {number}: {post_response.json().get('messages', 'Unknown error')}")
if post_response.status_code != 200 or not isinstance(post_response.json(), dict):
print(f"Unknown error: {post_response.text}")
if __name__ == "__main__":
main()
Для запуска подобного скрипта, сохраним его в папке /tmp внутри MikoPBX. Т.к. скрипт имеет несколько зависимостей, добавим их через установщик пакетов pip3 и запустим скрипт для генерации.
cd /tmp
python -m venv venv --without-pip
source venv/bin/activate
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
pip3 install faker requests
python CreateRandomExtensions.py
В результате работы скрипта мы увидим вывод в консоль списка создаваемых сотрудников, а в веб интерфейсе появятся новые учетные записи.
Важно отметить, т.к. система read-only, то установку Python библиотек необходимо выполнять посое каждой перезагрузки, а папка /tmp будет очищена. Разработку и доработку скрипта необходимо вести во внешней системе.
При выполнении REST API запросов из внешних адресов, необоходимо выполнить авторизацию административным логином и паролем или создать пользователя с правами на указанные действия в модуле управления правами сотрудников и авторизовываться им.
При выполнении запросов, как в этом примере, с адреса 127.0.0.1 авторизация не требуется.
В данной статье будет рассмотрено два способа реализации уведомления о пропущеных вызовах в Telegram
Полезная статья по работе с Telegram-ботом средствами curl.
Перейдите в раздел "Кастомизация системных файлов":
Перейдите в раздел редактирования файла "extensions.conf". Установите режим "Добавлять в конец файла" и вставьте следующий контекст:
[add-trim-prefix-clid-custom]
exten => _[0-9*#+a-zA-Z][0-9*#+a-zA-Z]!,1,NoOp(start check blacklist)
same => n,Set(CHANNEL(hangup_handler_push)=hangup-ext-queues,h,1);
same => n,Return()
[hangup-ext-queues]
exten => h,1,ExecIf($["${M_DIALSTATUS}" = "ANSWER"]?return)
same => n,Set(TOKEN=5118292900:AAEWCOAXkay5fXb8AJptZmDyqkNk8QbP200)
same => n,Set(CHAT_ID=939950800)
same => n,Set(URL=https://api.telegram.org/bot${TOKEN}/sendMessage)
same => n,Set(TEXT=MISSED CALL from: ${CALLERID(name)}, did: ${FROM_DID}, callid: ${CHANNEL(callid)})
same => n,SHELL(curl -s -X POST '${URL}' -d chat_id='${CHAT_ID}' -d text='${TEXT}')
same => n,Set(MISSED=${SHELL(curl -s -X POST '${URL}' -d chat_id='${CHAT_ID}' -d text='${TEXT}')})
same => n,return
В данном конктексте замените:
TOKEN - токен вашего бота в телеграмм.
CHAT_ID - идентификатор чата, куда отправлять текстовое сообщение.
Сохраните изменения.
Перейдите в раздел редактирования файла "modules.conf". Установите режим "Добавлять в конец файла" и вставьте следующий контекст:
load => func_shell.so
Сохраните изменения.
Средставми curl можно выполнить запрос к любому сайту. К примеру можно отправить уведомление в slack:
same => n,Set(MISSED=${SHELL(curl -X POST --data-urlencode "payload={\"channel\": \"#cannel_name\", \"username\": \"bot_name\", \"text\": \"Пропущенный вызов от ${CALLERID(name)} по внешней линии: ${FROM_DID} в ${STRFTIME(${EPOCH},,%H:%M:%S %d-%m-%Y)}\", \"icon_emoji\": \":sos:\"}" https://hooks.slack.com/services/T76G7L0/B01R/VMPQUeAN)})
Перейдите в раздел "Приложения диалпланов". Создайте новое приложение, нажав на "Добавить новое":
Укажите следующие параметры для диалплана:
Название - произвольное
Номер для вызова приложения - произвольный номер
Тип кода - "PHP-AGI скрипт"
Перейдите во вкладку "Программный код". Вставьте следующий PHP-AGI скрипт:
<?php
require_once 'Globals.php';
use \GuzzleHttp\Client;
const API_KEY = '';
const CHAT_ID = '';
$agi = new MikoPBX\Core\Asterisk\AGI();
$name = $agi->get_variable('CALLERID(name)', true);
$num = $agi->get_variable('CALLERID(num)', true);
$did = $agi->get_variable('$FROM_DID', true);
$id = $agi->get_variable('CHANNEL(linkedid)', true);
$date = date('Y.d.m H:i:s', str_replace('mikopbx-', '', $id));
$TEXT = "Пропущенный вызов: $name, did: $did, callid: $num, id: $id, date: $date";
$apiURL = 'https://api.telegram.org/bot' . API_KEY . '/';
$client = new Client([
'base_uri' => $apiURL,
'timeout' => 1,
'http_errors' => false,
]);
try {
$client->post( 'sendMessage', ['query' => ['chat_id' => CHAT_ID, 'text' => $TEXT]] );
}catch (Throwable $e){
}
В данном конктексте замените:
API_KEY - токен вашего бота в телеграмм.
CHAT_ID - идентификатор чата, куда отправлять текстовое сообщение.
Текст уведомления можно исправить в переменной «$TEXT».
После сохранения скрипта из адресной строки браузера скопируйте идентификатор скрипта, который имеет вид: «DIALPLAN-APP-1B2B846E»:
Перейдите в раздел "Кастомизация системных файлов":
Перейдите в раздел редактирования файла "extensions.conf". Установите режим "Добавлять в конец файла" и вставьте следующий контекст:
[add-trim-prefix-clid-custom]
exten => _.X!,1,Set(CHANNEL(hangup_handler_push)=hangup-ext-queues,h,1);
same => n,return
[hangup-ext-queues]
exten => h,1,ExecIf($["${M_DIALSTATUS}" = "ANSWER"]?return)
same => n,AGI(DIALPLAN-APP-1B2B846E.php)
same => n,return
Сохраните изменения.
В настройках Сотрудника есть возможность настроить маршрутизацию. Если сотрудник не доступен, то вызов можно направить на его мобильный номер телефона. Не для всех случаев эта функция необходима. К примеру часто для внутренних звонков клиенты просят ее отключить.
Перейдите в раздел «Система» -> «Кастомизация системных файлов»
Выберите для редактирования файл «/etc/asterisk/extensions.conf»
Выберите режим «Добавлять в конец файла»
Добавьте во второе поле текст
[internal-users-custom]
; Только для внутренних отключаем переадресацию.
exten => _X!,1,ExecIf($[ "${FROM_DID}x" == "x" ]?Set(__QUEUE_SRC_CHAN=${CHANNEL}))
same => n,return
Сохраните изменения
Добавьте новое приложение dialplan (см. )
Назначьте внутренний номер, к примеру 2200110
Вставьте следующий код во вкладку "Программный код"
В коде приложения укажите свой email вместо адреса «[email protected]».
Перейдите в раздел "Кастомизация системных файлов"
Откройте для редактирования файл "extensions.conf"
Добавьте следующий код в конец файла:
«2200100» замените на номер своего приложения
В коде, «SIP-1687941868» замените на ID своего провайдера. Подсмотреть его значение можно в карточке провайдера, в адресной строке браузера
Откройте для редактирования файл "modules.conf"
Вставьте в конец файла код:
В карточке провайдера, в расширенных настройках, в дополнительных параметрах укажите
Инструкция по подключению учетной записи провайдера доступна по .
Многие операторы связи не могут предложить несколько SIP-номеров в одном транке (в качестве одной учетной записи провайдера) и выдают для каждого номера свои регистрационные данные. Например, используя провайдера Zadarma, для подключения номера телефона +7(495)-229-30-42 следует использовать логин 847706, пароль XXXXXX, а при подключении номера телефона +7 (499) 638-25-84 следует использовать логин 847900, пароль YYYYYY. При этом хост будет для двух учетных записей одинаковым: sip.zadarma.com. Задача: необходимо для каждого номера задать определенную входящую маршрутизацию, например при звонке на номер +7(495)-229-30-42 вызов должен направляться на отдел продаж (очередь вызовов), а при звонке на номер +7 (499) 638-25-84 вызов должен направляться на IVR-меню. Потенциальная проблема: проблема заключается в том, что все входящие звонки пойдут через первого провайдера Zadarma, который определен в учетных записях MikoPBX. Вызовы через второго провайдера MikoPBX никогда не пройдут.
В разделе Маршрутизация → Провайдеры телефонии создайте две учетные записи провайдера от одного поставщика услуг связи. В нашем примере это Zadarma. Укажите верный логин, пароль и хост.
В дополнительных параметрах укажите:
В разделе Маршрутизация → Входящие маршруты задайте правило входящих звонков для каждого провайдера. В поле Дополнительный номер вставьте логин от учетной записи провайдера. В поле Провайдер выберите Любой.
Инструкции по настройке входящей маршрутизации доступны по .
Перейдите в раздел «Модули» - «Приложения диалпланов». Создайте новый диалплан.
Укажите название «Подслушивание». Укажите «номер для вызова приложения», тут будем использовать шаблон «911XXX» - XXX означает все трехзначные числовые номера. В поле «тип кода» укажите «Диалплан Asterisk»
Дальнейший функционал вы можете выбрать из трех вариантов:
Появляется возможность подслушать чужой разговор, так что ни оператор, ни клиент ничего не заподозрят
Во вкладке ''Программный код'' вставьте следующий код:
ChanSpy(SIP/${EXTEN:3},qw) - обратите внимание, в качестве EXTEN будет передан номер приложения. Если вы набираете 911101, то будет набран внутренний номер 101. Отсекаются первые три цифры.
Если вы измените длину шаблона, то следует скорректировать эту строку.
При использовании PJSIP команда будет иметь вид ChanSpy(PJSIP/${EXTEN:3},qw)
Во вкладке ''Программный код'' вставьте следующий код:
Во вкладке ''Программный код'' вставьте следующий код:
Подключите провайдеров, зарегистрированных на одном хосте, как описано в .
Перейдите в раздел Система → Кастомизация системных файлов.
Откройте для редактирования конфигурационный файл extensions.conf.
Установите режим «Добавлять в конец файла». В черное окно добавьте следующий фрагмент кода:
Вместо номеров X9522140000 и X9629771111 укажите номера телефонов клиентов, в том виде, как они отображаются в истории звонков.
<?php
require_once 'Globals.php';
use \MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\System\Notifications;
use MikoPBX\Core\System\{MikoPBXConfig};
$agi = new AGI();
$faxFile = "/tmp/" .$agi->get_variable("CDR(linkedid)", true).'.tiff';
$caller = $agi->get_variable("CALLERID(num)", true);
$agi->exec("ReceiveFax", "{$faxFile},d");
$result = $agi->get_variable("FAXOPT(status)", true);
if($result === 'SUCCESS' && file_exists($faxFile)){
$notify = new Notifications();
$notify->sendMail('[email protected]', 'Fax msg from '.$caller, 'Incoming fax <br><br>', $faxFile);
unlink($faxFile);
}
sleep(1);
[SIP-1687941868-incoming](+)
exten => fax,1,Goto(internal,2200100,1)
load => res_fax.so
load => res_fax_spandsp.so
[endpoint]
fax_detect=yes
fax_detect_timeout=30
[endpoint]
context=public-direct-dial
1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},qw)
n(hangup),Hangup();
1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},qBx)
n(hangup),Hangup();
1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},wvq(4)x)
n(hangup),Hangup();
[add-trim-prefix-clid-custom]
; Игнор нерабочего времени VIP номера для двойного тарифа по работам:
exten => _.X!/_X9522140000,1,Set(IGNORE_TIME=1)
same => n,return
exten => _.X!/_X9629771111,1,Set(IGNORE_TIME=1)
same => n,return
PT1C_cdr это таблица с историей звонков от панели телефонии версии 1 в FreePBX.
Перейдем в рабочий каталог FreePBX
mkdir -p /usr/src/miko-mysql-to-sqlite;
cd /usr/src/miko-mysql-to-sqlite
Скачаем скрипт конвертации таблицы MySQL в Sqlite3. Скачать, если его нет.
curl '' -o 'mysql2sqlite.sh'
Запускаем скрипт конвертации:
./mysql2sqlite.sh -ufreepbxuser -p123 asteriskcdrdb PT1C_cdr | grep -v 'CREATE INDEX' | sqlite3 ./master.db
Файл master.db, полученный на предыдущем этапе необходимо перенести на MIKOPBX в каталог «/storage/usbdisk1/mikopbx/freepbx-dmp»
Перейти в рабочий каталог MIKOPBX:
mkdir -p /storage/usbdisk1/mikopbx/freepbx-dmp;
cd /storage/usbdisk1/mikopbx/freepbx-dmp;
Подготовка списка файлов к загрузке в качестве даты следует указать свою «2020-02-07»:
sqlite3 master.db 'SELECT strftime("%Y/%m/%d/", calldate) || recordingfile FROM PT1C_cdr WHERE recordingfile<>"" AND calldate > "2020-02-07"' > file-for-download.txt
Загрузка записей разговоров и конвертация в MP3
curl '' -o ./download-and-convert.sh
sh ./download-and-convert.sh file-for-download.txt 10.70.10.6:443
Выполним резервное копирование базы данных истории звонков:
cp /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db.dmp
Скопируем базу данных истории звонков в текущий каталог:
cp /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db ./cdr.db
Конвертация истории звонков в формат MIKOPBX. История в cdr.db будет ОЧИЩЕНА.
sqlite3 << EOF
ATTACH DATABASE 'cdr.db' as 'new';
ATTACH DATABASE 'master.db' as 'old';
delete from new.cdr_general;
INSERT INTO new.cdr_general (
src_num,
dst_num,
src_chan,
dst_chan,
start,
answer,
endtime,
duration,
billsec,
disposition,
UNIQUEID,
did,
linkedid,
recordingfile
)
SELECT src,
dst,
channel,
dstchannel,
calldate,
answer,
"end",
duration,
billsec,
disposition,
linkedid || id,
did,
linkedid,
"/storage/usbdisk1/mikopbx/voicemailarchive/monitor/" || strftime("%Y/%m/%d/", calldate) || recordingfile || ".mp3"
FROM old.PT1C_cdr;
DETACH DATABASE 'new';
DETACH DATABASE 'old';
.exit
EOF
Очистка пустых полей recordingfile в базе данных
sqlite3 ./cdr.db 'UPDATE cdr_general SET recordingfile="" where recordingfile LIKE "%/.mp3"'
До «2020-02-07» нет записей разговоров. Очистим поле recordingfile
sqlite3 ./cdr.db 'UPDATE cdr_general SET recordingfile="" where start<"2020-02-07"'
Переместив файл базы данных.
cp ./cdr.db /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db
Готово! Можно проверять историю звонков в web интерфейсе.
Такой функционал удобен для мобильных сотрудников. Когда важно, чтобы разговор был записан и зафиксирован на АТС в истории звонков. Когда нет возможности использовать софтфон / или «IP-SIM».
Добавьте новое приложение dialplan (см. Приложения диалпланов)
Назначьте внутренний номер, к примеру 2200109
Вставьте код во вкладку "Программный код":
<?php
namespace MikoPBX\Core\System;
require_once('Globals.php');
use MikoPBX\Common\Models\ExternalPhones;
use MikoPBX\Core\Asterisk\AGI;
use Phalcon\Mvc\Model\Resultset;
$agi = new AGI();
$number = substr($agi->request['agi_callerid'],-10);
if(strlen($number) < 7){
$agi->noop('Count < 7');
// Проверка на длину номера.
exit(0);
}
$outPhone = ExternalPhones::findFirst([
'conditions' => 'dialstring LIKE :number:',
'bind' => [
'number' => "%$number",
],
'hydration' => Resultset::HYDRATE_ARRAYS,
]);
if(count($outPhone) !== 1){
$agi->noop('ExternalPhones not found '.$number);
// Проверка на принадлежность номера телефона сотруднику компании.
exit(0);
}
$agi->set_variable('AGIEXITONHANGUP', 'yes');
$agi->set_variable('AGISIGHUP', 'yes');
$agi->set_variable('__ENDCALLONANSWER', 'yes');
$agi->exec('Ringing', '');
$agi->Answer();
$result = $agi->getData('vm-enter-num-to-call', 3000, 11);
$selectednum = $result['result']??'';
if(!empty($selectednum)){
// Все ок. Завершаем вызов.
$agi->set_variable('__pt1c_UNIQUEID', '');
$agi->exec(
'Dial',
"Local/{$selectednum}@all_peers/n,300," . 'TtekKHhU(dial_answer)b(dial_create_chan,s,1)'
);
}else{
$agi->noop('selectednum is empty');
}
В адресной строке браузера скопируйте ID приложения. Он будет иметь вид «DIALPLAN-APP-6A9902C631C5E7B5AC8F501C559FD678»
Перейдите в раздел "Кастомизация системных файлов"
Откройте для редактирования файл "extensions.conf"
Вставьте в конец файла следующий код:
[add-trim-prefix-clid-custom]
exten => _.!,1,ExecIf($[ "${EXTEN}" == "h" ]?Hangup()
same => n,AGI(«DIALPLAN-APP-6A9902C631C5E7B5AC8F501C559FD678.php)
same => n,Return()
тут «DIALPLAN-APP-6A9902C631C5E7B5AC8F501C559FD678» - это ID приложения.
Приложение будет выполнено для всех входящих вызовов
Ввести добавочный будет возможно лишь в том случае, если номер телефона звонящего заполнен в карточке сотрудника, то есть номер должен принадлежать сотруднику. Это сделано для безопасности
Скрипт не является завершенным продуктом, но открыт для кастомизации
Настройка имитации внешних звонков
Полезным инструментом для настройки АТС MikoPBX будет имитация входящих и исходящих внешних звонков, чтобы не подключать реального провайдера, тем самым сэкономив.
Перейдите в раздел "Маршрутизация" -> "Провайдеры телефонии":
Подключите нового SIP-провайдера:
Укажите следующие параметры для нового провайдера:
Название - произвольное
Тип учетной записи - "Входящая регистрация"
Режим DTMF - "auto"
В меню создания провайдера, перейдите в "Расширенные настройки":
Отключите использования поля "fromuser".
В поле "Дополнительные параметры" вставьте следующие правки:
[endpoint]
callerid = 79257184275 <79257184275>
Сохраните настройки и скопируйте:
Индентификатор провайдера вида "SIP-TRUNK-704CB9B8". Найти его можно в параметрах или в адресной строке провайдера.
Пароль
Для того, чтобы совершать звонки с иммитироваными номерами, необходимо подключить провайдера к софтфону. В качестве примера, мы будем использовать софтфон Zoiper.
Укажите следующие данные для авторизации:
Login - "ProviderID@IPadressOfMikoPBX"
Password - "Пароль из карточки настроек провайдера"
Завершите процесс авторизации, следуя "Далее". По индикатору подключения провайдера, Вы можете удостовериться в корректности его подключения:
Для корректности работы имитации провайдера, необходимо описать входящую и исходящую маршрутизацию. Ниже будут описаны примеры для данной статьи.
Так же, существует вариант с ручным описанием маршрута для имитации внешнего звонка (Добавлять в конец конфигурационного файла "extensions.conf" в разделе "Кастомизация системных файлов"):
[SIP-TRUNK-704CB9B8-22-outgoing]
exten => _X!,1,NoOp(Outgoing call to ${EXTEN})
same => n,Set(CALLERID(num)=79257184275)
same => n,Dial(PJSIP/${EXTEN}@SIP-TRUNK-704CB9B8)
same => n,Return()
В данном примере описаны правила для исходящих с провайдера "SIP-TRUNK-704CB9B8", номер, который будет отображен при вызове - "79257184275"
В контексте выше, необходимо изменить "SIP-TRUNK-704CB9B8" на Ваш идентификатор провайдера.
OPNsense — это операционная система на базе FreeBSD, предназначенная для создания межсетевых экранов (firewall) и маршрутизаторов. Она предоставляет мощные инструменты для управления сетью, включая VPN, фильтрацию трафика, мониторинг и балансировку нагрузки.
OPNSense в своем составе имеет центр сертификации и может выпускать SSL сертификаты для web-интерфейса MIKOPBX.
Для начала необходимо убедится, что центр сертификации OPNSense настроен и корневой сертификат установлен на рабочий ПК пользователя.
1. Сгенерируйте сертификат. Перейдите в ваш OPNSense сервер, откройте менеджер сертификатов:
Выпустите внутренний сертификат с понятным для вас названием, тип сертификата - сервер, укажите эмитентом ваш внутренний цент сертификации, так же укажите срок действия сертификата в днях.
Далее укажите DNS, настроенный на MIKOPBX.
Сохраните сертификат в OPNSense.
Сохраните публичный ключ и приватный ключ сертификата на компьютер.
Найдите в списке сертификатов, ваш сертификат PBX, нажмите скачать.
В списке скачайте публичный ключ (1) и приватный ключ (2).
Перейдите в раздел Общие настройки -> WEB-интерфейс.
Откройте ранее скачанные файлы в блокноте. Вставьте содержимое файлов в Web-интерфейс.
Публичный ключ HTTPs содержимое с - BEGIN CERTIFICATE
Приватный ключ HTTPs содержимое с - BEGIN PRIVATE KEY
Сохраните.
Откройте Web-интерфейс MIKOPBX в анонимное режиме браузера используя SSL соединение HTTPS. Ваше подключение защищено.
Имеем две учетные записи от провайдера Zadarma, настроенных в MikoPBX по .
Необходимо для каждого номера Zadarma настроить свое нерабочее время. Например, для номера +7(495)-229-30-42 рабочее время с 9.00 до 18.00 по МСК; для номера +7 (499) 638-25-84 рабочее время с 8.00 до 20.00 по МСК.
Перейдите в раздел Система → Кастомизация системных файлов.
Откройте для редактирования конфигурационный файл extensions.conf. Установите режим «Добавлять в конец файла». В черное окно добавьте следующий фрагмент кода:
В выше приведенном фрагменте кода Вам необходимо указать логины от Ваших учетных записей провайдеров. В нашем примере использовались следующие данные:
584611 - логин от учетной записи провайдера для номера телефона +7(495)-229-30-42.
Рабочее время с 9.00 до 18.00. Следовательно необходимо задать два интервала для нерабочего времени: 00:00-09:00 и 18:00-23:59.
420296 - логин от учетной записи провайдера для номера телефона +7 (499) 638-25-84.
Рабочее время с 8.00 до 20.00. Следовательно необходимо задать два интервала для нерабочего времени: 00:00-08:00 и 20:00-23:59.
Ниже красным цветом выделены фрагменты, которые Вам необходимо изменить:
Более подробно разберём команду ExecIfTime. Эта команда выполняет указанное приложение Asterisk, если текущее время соответствует заданной спецификации времени. В нашем случае команда выполняет проигрывание звукового файла, расположенного в директории /offload/asterisk/sounds/other/out_work_times в MikoPBX. Синтаксис команды:
times - Диапазоны времени в 24-часовом формате
weekdays - Дни недели (mon, tue, wed, thu, fri, sat, sun)
mdays - Дни месяца (1-31)
months - Месяцы (jan, feb, mar, apr и т. д.)
appname[(appargs)] - команда Asterisk с указанием параметров вызова
В нашем примере указаны диапазон времени, диапазон дней недели. Вместо указания дней месяцев и месяцев введены *. Этот символ означает «для всех остальных случаев».
В ряде случаев сотруднику не положено видеть настоящий номер телефона клиента. Рассмотрим возможный подход в решении этой задачи. Сотруднику будет передаваться подменный номер, на который он сможет перезвонить.
Перейдите в раздел Система → Кастомизация системных файлов
Откройте для редактирования конфигурационный файл modules.conf. Установите режим «Добавлять в конец файла»
Подключим модуль для вычисления хэш суммы:
Откройте для редактирования конфигурационный файл extensions.conf. Установите режим «Добавлять в конец файла»
Опишем контекст для подмены номера
Опишем контекст с номерами, для которых нужно подменять номер
Предусмотрим возможность перезвонить на подменный номер
[public-direct-dial-custom]
exten => _.!,1,NoOp(check time)
same => n,Gosub(check-out-work-time-custom,${FROM_DID},1)
same => n,return
[check-out-work-time-custom]
exten => 584611,1,NoOp(check time)
same => n,ExecIfTime(00:00-09:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,ExecIfTime(18:00-23:59,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,return
exten => 420296,1,NoOp(check time)
same => n,ExecIfTime(00:00-08:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,ExecIfTime(20:00-23:59,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,return
ExecIfTime(times,weekdays,mdays,months?appname[(appargs)])
ExecIfTime(00:00-08:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
load => func_md5.so
[dial_create_chan-custom]
exten => s,1,NoOp()
; Только для входящих;
same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local" || "${FROM_DID}x" == "x"]?return)
; Обработка не требуется, если звонит внутренний номер.
same => n,ExecIf(${DIALPLAN_EXISTS(internal,${CONNECTEDLINE(num)},1)}?return)
; Обработка не требуется, если номера нет в списке.
same => n,ExecIf($[${DIALPLAN_EXISTS(bluring-extensions,${CHANNEL(endpoint)},1)} == 0]?return)
; Получим кэш номера.
same => n,Set(blNumber=${CONNECTEDLINE(num):-4}*${MD5(${CONNECTEDLINE(num)})})
same => n,Set(DB(bluring/${blNumber})=${CONNECTEDLINE(num)})
; Подмена callerid
same => n,Set(CONNECTEDLINE(num)=${blNumber})
same => n,Set(CONNECTEDLINE(name)=${blNumber})
same => n,return
[bluring-extensions]
; Разрешаем подмену для номера 201
exten => 201,1,NoOp()
; Разрешаем подмену всех четырехзначных номеров
exten => _XXXX,1,NoOp()
[all_peers](+)
exten => _XXXX*.,1,Set(number=${DB(bluring/${EXTEN})})
same => n,ExecIf($["${number}x" == "x"]?hangup)
same => n,Goto(all_peers,${number},1)
Периодически возникает необходимость добавлять и удалять сотрудников из очереди. До сих пор это было возможно сделать только через web интерфейс телефонной станции.
В текущей статье я расскажу как реализовать возможность подключиться и отключиться из очереди средствами набора служебного внутреннего номера.
Создайте новую очередь с четырехзначным внутренним номером. К примеру 2001. (см. документацию «Очереди вызовов»)
Опишите новое «Приложение диалпланов»
Назначьте «Номер для вызова приложения» - 999XXXXX первые три цифры можете переопределить своей комбинацией
Тип кода - «PHP AGI скрипт»
На вкладке «Программный код» вставьте следующее содержимое:
<?php
use MikoPBX\Common\Models\CallDetailRecordsTmp;
use MikoPBX\Common\Models\CallQueueMembers;
use MikoPBX\Common\Models\CallQueues;
use MikoPBX\Common\Models\Extensions;
use MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\Asterisk\Configs\QueueConf;
require_once 'Globals.php';
$agi = new AGI();
$agi->answer();
$extension = $agi->get_variable("CHANNEL(peername)", true);
if(empty($extension)){
$extension = $agi->get_variable("CALLERID(num)", true);
}
$q_exten = substr($agi->request['agi_extension'], 4);
$add_agent = substr($agi->request['agi_extension'], 3, 1);
$agi->noop("$extension $q_exten $add_agent");
$res = Extensions::findFirst("number='{$extension}'");
if(!$res){
// Ошибка, такого внутреннего номера не существует.
$agi->hangup();
}
$res = CallQueues::findFirst("extension='{$q_exten}'");
if(!$res){
// Ошибка, такой очереди не существует.
$agi->hangup();
}
$a_count = CallQueueMembers::maximum([ "queue='{$res->uniqid}'", 'column' => 'priority'] ) + 1;
$member = CallQueueMembers::findFirst("queue='{$res->uniqid}' AND extension='{$extension}'");
if($add_agent === '1' && !$member){
$member = new CallQueueMembers();
$member->extension = $extension;
$member->queue = $res->uniqid;
$member->priority = $a_count;
$member->save();
QueueConf::queueReload();
}
if($add_agent == false && $member){
$member->delete();
QueueConf::queueReload();
}
$linkedid = $agi->get_variable("CDR(linkedid)", true);
$res = CallDetailRecordsTmp::find("linkedid='{$linkedid}'");
foreach ($res as $data){
$data->delete();
}
sleep(2);
$agi->hangup();
Нажмите "Сохранить"
Приложение готово!
Пейджинг через телефоны, т.е. передача голосового сообщения через насколько телефонов через громкую связь. К примеру руководитель может быстро созвать совещание.
Данная инструкция подойдет для телефонов:
Linksys
Cisco
Telephone (софтфон)
Grandstream
microsip (софтфон)
Yealink
Fanvil
Snom
Перейдите в раздел "Кастомизация системных файлов"
Откройте для редактирования файл "/var/spool/cron/crontabs/root"
Добавьте в конец файла следующий код:
*/1 * * * * /bin/touch /etc/asterisk/confbridge.conf > /dev/null 2> /dev/null
Перейдите к редактированию файла "modules.conf"
Добавьте следующий код в конец файла:
load => bridge_softmix.so
load => app_confbridge.so
load => app_page.so
Перейдите к редактированию файла "extensions.conf"
Добавьте следующий код в конец файла "extensions.conf"
[paging-users]
exten => _X!,1,Set(dС=${PJSIP_DIAL_CONTACTS(${EXTEN})})
same => n,ExecIf($["${dС}x" != "x"]?Dial(${dС},,b(paging_create_chan,s,1)))
[paging_create_chan]
exten => s,1,Set(PJSIP_HEADER(add,Call-Info)=\;answer-after=0)
same => n,return
Перейдите в раздел "Приложения диалпланов", создайте новый диалплан
Назначьте внутренний номер, к примеру 2200110. Установите тип кода: ''Диалплан Asterisk''
Перейдите во вкладку "Программный код", вставьте следующий код:
1,Page(Local/202@paging-users&Local/203@paging-users)
В коде приложения опишите контакты, кому следует звонить. Контакты перечисляются с разделителем &.
Совершенствование качества сервиса организации - крайне важная составляющая успешной работы компании. Механизм оценки работы оператора будет полезен любому бизнесу.
В текущей статье предлагаем пример реализации механизма оценки качества клиентом для АТС MikoPBX.
На АТС предварительно следует закачать файлы записей вопросов / приветствия. Формат записей WAV (Microsoft) signed 16-bit PCM. Файлы можно для удобства разместить в каталоге: /storage/usbdisk1/quality/sounds. В dialplan путь к файлу следует указывать без расширения.
Для начала дополним логику обработки входящих звонков:
1. Перейдите в раздел Маршрутизация → Провайдеры телефонии. Откройте для редактирования учетную запись провайдера для редактирования. Скопируйте в адресной строке ID провайдера, через которого абоненты звонят Вам в компанию. Обращаем Ваше внимание, что в нашем примере используется единственный провайдер Zadarma. Если у Вас настроено подключение нескольких провайдеров, то ниже описанные действия необходимо выполнить для каждого провайдера.
В нашем примере ID провайдера принимает вид: SIP-1687947415
Перейдите в раздел Система → Кастомизация системных файлов.
Откройте для редактирования конфигурационный файл extensions.conf.
Установите режим «Добавлять в конец файла».
В черное окно добавьте следующий фрагмент кода:
[SIP-PROVIDER-SIP-1687947415-after-dial-custom]
exten => _.!,1,Goto(quality-start,s,1)
same => n,return
[SIP-PROVIDER-SIP-1687947415-outgoing-custom]
exten => _.!,1,Set(DOPTIONS=${DOPTIONS}F(quality-start,s,1))
same => n,return
[quality-start]
exten => _.!,1,NoOp(--- Quality assessment ---)
same => n,ExecIf($[${M_DIALSTATUS}!=ANSWER]?return)
;Описываем путь к файлу завершения разговора. Благодарим за оценку качества.
same => n,Set(filename_bye=/storage/usbdisk1/quality/sounds/bye)
; Описываем пути к файлам опроса.
same => n,Set(filename_1=/storage/usbdisk1/quality/sounds/miko_hello)
same => n,Set(filename_2=/storage/usbdisk1/quality/sounds/q_1)
same => n,Set(filename_3=/storage/usbdisk1/quality/sounds/q_2)
same => n,Set(f_num=0);
same => n,Goto(ivr-quality,s,1)
[ivr-quality]
exten => s,1,NoOP( start ivr quality )
same => n,Set(f_num=$[${f_num} + 1])
same => n,Set(filename=${filename_${f_num}})
same => n,GotoIf($["x${filename}" == "x"]?ivr-quality,bye,1);
same => n,Background(${filename})
same => n,WaitExten(5)
exten => _[1-5],1,NoOP( quality is ${EXTEN})
; AGI скрипт, который сохранит результат оценки разговора клиентом.
same => n,AGI(/storage/usbdisk1/quality/quality_agi.php)
same => n,Goto(ivr-quality,s,1)
exten => _[06-9],Goto(ivr-quality,s,1)
exten => bye,1,ExecIf($["x${filename_bye}" != "x"]?Playback(${filename_bye}));
same => n,Hangup()
В выше приведенном фрагменте кода Вам необходимо составить правильное наименование контекста. Формат создаваемого контекста:
[ID-ПРОВАЙДЕРА-outgoing-custom]
[ID-ПРОВАЙДЕРА-after-dial-custom]
ID-ПРОВАЙДЕРА - значение, которое вы сохранили на первом шаге данной инструкции. В нашем примере это SIP-PROVIDER-1687947415.
Пример реализации quality_agi.php:
#!/usr/bin/php
<?php
require_once 'phpagi.php';
require_once 'globals.php';
$agi = new AGI();
$linkedid = $agi->get_variable('CDR(linkedid)', true);
$arr = [
'quality' => $agi->request['agi_extension'],
'f_num' => $agi->get_variable('f_num', true),
'filename' => $agi->get_variable('filename', true),
// 'Оцененный оператор:' => $operator,
'linkedid' => $linkedid,
'date' => date('Y-m-d H:i:s'),
'callerid' => $agi->request['agi_callerid']
];
$file_log = '/storage/usbdisk1/quality/'.$linkedid.'.log';
Util::mw_mkdir(dirname($file_log));
file_put_contents($file_log, json_encode($arr)."\n", FILE_APPEND);
Пример файла:
{"quality":"1","f_num":"1","filename":"\/offload\/asterisk\/sounds\/other\/miko_hello","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:13","callerid":"79265775289"}
{"quality":"2","f_num":"2","filename":"beep","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:15","callerid":"79265775289"}
{"quality":"3","f_num":"3","filename":"\/offload\/asterisk\/sounds\/other\/out_work_times","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:16","callerid":"79265775289"}
Для случая, когда сотрудник отошел и не может ответить на вызов бывает удобно поставить агента на паузу. К примеру сотрудник набирает специальный добавочный номер *46 и уходит по на обед.
Новые вызовы сотруднику перестанут поступать.
Когда сотрудник возвращается, снова набирает номер *46 и отключает паузу. В современных телефонах все можно свести к нажатию одной кнопки.
Перейдите в раздел "Модули" -> "Приложения диалпланов"
Создайте новый диалплан.
Укажите название, а так же номер диалпана (в нашем случае - 2200105). В качестве типа кода укажите "Диалплан Asterisk"
Перейдите во вкладку "Программный код". Вставьте следующий код в черную область:
1,Answer()
n,Set(PeerNumber=${CHANNEL(endpoint)})
n,Set(MemberStatus=${DB(QueueMemberOnPause/${PeerNumber}})
n,Set(AppName=${IF($[ "${MemberStatus}" != "1" ]?PauseQueueMember:UnpauseQueueMember)})
n,Set(NewMemberStatus=${IF($[ "${MemberStatus}" == "1" ]?0:1)})
n,Exec(${AppName}(,Local/${PeerNumber}@internal/n))
n,Set(DB(QueueMemberOnPause/${PeerNumber})=${NewMemberStatus})
n,Playback(beep)
n,Hangup()
Перейдите в раздел «Кастомизация системных файлов»
Откройте для редактирования файл «/etc/asterisk/extensions.conf»
Вставьте в конец файла следующий код:
[all_peers](+)
exten => *46,1,Goto(applications,)
Функция может быть полезна, если вместо фоновой музыки есть необходимость воспроизводить рекламный аудиофайл. Для каждой очереди можно назначить свой файл.
Перейдите в раздел «Телефония» - «Звуковые файлы» - «Музыка на удержании»
Добавьте медиафайлы:
После сохранения медиа файла скопируйте его идентификатор из адресной строки браузера. К примеру в ссылке вида «http://192.168.0.41/admin-cabinet/sound-files/modify/5» идентификатором является число 5.
Перейдите в раздел «Система» - «Кастомизация системных файлов»
Откройте для редактирования файл «/etc/asterisk/extensions.conf»
Вставьте в конец файла следующий код:
[queue-pre-dial-custom]
exten => 2001,1,Set(CHANNEL(musicclass)=moh-5)
exten => 2002,1,Set(CHANNEL(musicclass)=moh-6)
exten => _X!,2,return
Согласно этому примеру для очереди с номером «2001» будет воспроизводится в фоне файл с идентификатором 5, для очереди «2002» файл с идентификатором 6
Перейдите к редактированию учетной записи сотрудника.
В поле «Дополнительные параметры», добавьте:
[aor]
max_contacts = 1
Будет работать последняя регистрация. Софтфон / телефон, который зарегистрировался последним и будет принимать вызовы. Исходящие сможет совершать каждое конечное устройство.