Настройка voip телефона Polycom 331 для работа с TLS и DNS SRV
Странная логика у этого аппарата, ну понятно, что в виду нехватка документации объяснить какие-то пункты я не смогу, например, мне не понятно, как взаимодействует раздел SIP и line1. Но моя задача была настроить телефон так, чтобы он работал с TLS и DNS SRV, т.к. в текущем проекте, мы использовали DNS failover. Сразу скажу, что все получилось и сама схема DNS SRV failover прекрасна.
Общая схема такая: Polycom 331 —tls— Kamailio —udp— Asterisk
Итак, вот скрины настроек.
Остальные настройки вне line1 нужно сделать дефолтными, и всё пойдет.
3.02.2016Ограничение вызовов для exten
Ограничение одновременных вызовов в Астериске . Смысл прост: Ищем каналы по выражению “116-” а потом считаем кол-во элементов в массиве.
3.02.2016same => n,GotoIf($[“${FIELDQTY(CHANNELS(${CALLERID(num)}-), )}” > “1”]?busy-custom,s,1)
Asterisk. Annoucement. Dialplan. ChannelRedirect. Elastix. Part 2.
В предыдущей заметке описал решение с когда пользователь вручную набирает номер и воспроизводит, во время разговора, заранее записанное приветствие.
В этой заметке, мы несколько изменим решение, т.к. оказывается оно еще должно работать с роботом AMI которым пользователи активно пользуются.
Итак, для начала скрипт АМИ, которым осуществляется вызов:
Action: Login
Username: admin
Secret: werkjnfgopw
Action: Originate
ActionID: 12345
Channel: Local/117@tapi
Context: agent-custom
Exten: 12122396200
Priority: 1
Ранее смысл решения основывался на том, чтобы сохранить внутренний номер телефона, привязать к нему имя правильного канала (то куда позвонили) и собственно воспроизвести в этот канал файлик с названием “extension_x”.
Для реализации у нас в файле extension_custom.conf:
Этот контекст ловит определенный номер формата #*00x и записывает приветствие в файлик “extension_x”. Тут все просто.
[custom-record] ; Context for recording voice messages
exten => _#*00X,1,Noop(${EXTEN})
same => n,Set(CHANNEL(language)=ru)
same => “”n,Set(MONITOR_FILENAME=${DIR}${CALLERID(num)}_${EXTEN:4})
same”” => n,Playback(beep)
same => n,Answer
same => n,Record(${MONITOR_FILENAME}:wav)
same => n,Playback(vm-msgsaved)
same => n,Playback(${MONITOR_FILENAME})
same => n,Playback(vm-goodbye)
этот макрос выполняется когда мы совершаем вызов вручную в функцию Dial встроен код ‘M^(testsub)’ делалось это прям через веб инфтерфейс Эластикса.
[macro-testsub]
exten => s,1,NoOp(${CHANNEL})
exten => s,n,NoOp(${MYVAR})
exten => s,n,Set(DB(br/${MYVAR})=${CHANNEL})
exten => s,n,NoOp(${DB(br/${MYVAR})})
Этот контекст должен вызываться когда с ext приходит команда ##X что означает, что ext хочет воспроизвести приветствие.
[transfer-context]
exten => “” _##X,1,NoOp(${DB(br/${CALLERID(num)})})
exten”” => “”_##X,2,Set(DB(from_ext/last_ext)=${CALLERID(num)})
exten”” => “”_##X,3,ChannelRedirect(${DB(br/${CALLERID(num)})},play-context,${EXTEN},1)
exten”” => _##X,4,Playback(${DIR}${CALLERID(num)}_${EXTEN:-1}) ;
exten => _##X,5,Hangup()
А Это вспомогательный контекст, в него мы перескакием, когда собственно случается событие приоритета 3 в transfer-context. До сих пор не понятно, почему нельзя передать переменную через channelredirect – приходит ее сохранять в DB затем извлекать…
[play-context]
exten => _##X,1,noop(${DB(from_ext/last_ext)})
exten => _##X,2,Playback(${DIR}${DB(from_ext/last_ext)}_${EXTEN:-1}) ;
exten => _##X,3,Set(DB(from_ext/last_ext)=”0″)
exten => _##X,4,Hangup()
Итак, при вызове функции AMI Originate как я описал выше, вызывается два пользователя: внутренний 116 например и внешний внутренний вызывается в контексте [tapi]
[tapi]
exten => 116,1,Answer
exten => 116,n,SIPAddHeader(Alert-Info: Ring Answer)
exten => 116,n,NoOp(Context: agent)
exten => 116,n,Dial(SIP/116)
exten => 116,n,hangup
а затем вызывается контекст [agent-custom]: Это все делает тот AMI скрипт. В подчеркнутых строчках сохранение переменной какой внутренний номер позвонил и вызов макроса dialer-dial, который…
exten => _1XXXXXXXXXX,1,Answer
exten => _1XXXXXXXXXX,n,NoOp(Context: agent)
exten => _1XXXXXXXXXX,n,noop(CALLERID: “””${CALLERID(all)})
exten””” => _1XXXXXXXXXX,n,NoOp(EXTEN: ${EXTEN})
exten => _1XXXXXXXXXX,n,noop(SIP_to: ${SIP_HEADER(TO)})
exten => _1XXXXXXXXXX,n,noop(SIP_from: ${SIP_HEADER(FROM)})
exten => _1XXXXXXXXXX,n,Set(__FROM_EXT=${CHANNEL(name)})
exten => _1XXXXXXXXXX,n,Dial(SIP/t1/991${EXTEN},60,r^M(dialer-dial))
exten => _1XXXXXXXXXX,n,noop(DIALSTATUS= ${DIALSTATUS})
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “BUSY”]?busy-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “CONGESTION”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “CHANUNAVAIL”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,gotoif($[“${DIALSTATUS}”= “NOANSWER”]?congestion-custom,s,1)
exten => _1XXXXXXXXXX,n,hangup
который… сохранит для нас связку внутреннего номера и канала который мы вызываем в базу Астериска.
26.12.2015[macro-dialer-dial]
exten => s,1,NoOp(${FROM_EXT:6:3})
exten => s,n,Set(MYVAR_A=${FROM_EXT:6:3})
exten => s,n,Set(DB(br/${MYVAR_A})=${CHANNEL})
Asterisk. Annoucement. Dialplan. ChannelRedirect. Elastix.
Менеджеры услышав сигнал автоответчика обычно кладут трубку, либо оставляют свои координаты. При большом количестве звонков таких ситуаций может возмникать приличное кол-во.
Задача: Записать заранее приветствие в нескольких вариантах, и затем воспроизводить его абонентку по нажатию на кнопки быстрого набора.
Проблема: Быстрый набор – это просто очередной INVITE с заранее запрограммированным кодом, поэтому, для астериска это выглядит как поступление нового звонка.
Решение: Я решил эту задачу как для DTMF так и для использования speed-dial.
Ключ к решению: Основная проблема это заставить астериск воспроизводить заранее записанное приветствие нужному абоненту, если просто внести в dialplan запись вида ##1, где номер приветствия то астериск его проиграет тому кто прислал вызов, а нам нужно воспроизвести его абоненту, да еще и в терминологии Asterisk в другой канал.
Коротенько: Записывать приветствия мы будем формата XXXX_N.wav , XXXX- номер эксентшина который записывает приветствие, а N – это номер самого приветствия, таким образом мы можем записывать несколько приветствий для одного экстеншина.
[custom-record] ; Context for recording voice messages
exten => _#*00X,1,Noop(${EXTEN})
same => n,Set(CHANNEL(language)=ru)
same => “”n,Set(MONITOR_FILENAME=${DIR}${CALLERID(num)}_${EXTEN:4})
same”” => n,Playback(beep)
same => n,Answer
same => n,Record(${MONITOR_FILENAME}:wav)
same => n,Playback(vm-msgsaved)
same => n,Playback(${MONITOR_FILENAME})
same => n,Playback(vm-goodbye)
Воспроизведение: При звонке мы сохраняем данные кто звонил и имя канала куда позвонили : ${CALLEDRID(num)} – ${CHANNEL}, Делать это нужно в время команды Dial поэтому используется pre_dial_handler, для Эластикс это просто Опции в разделе General Setting. Tt – дает возмоножсть пользоваться DTMF, а ^M(testsub) выполняет макрос testsub.
[macro-testsub]
exten => s,1,NoOp(${CHANNEL})
exten => s,n,NoOp(${MYVAR})
exten => s,n,Set(DB(br/${MYVAR})=${CHANNEL})
exten => s,n,NoOp(${DB(br/${MYVAR})})
Чтобы запомнить переменную кто звонил мы добавляем строчку “exten => s,n,Set(__MYVAR=${CALLERID(number)})” в macro-user-callerid и отправляем этот макрос в файл extensions_override_elastix Это означает, что Эластикс будет читать его их этого файла, чтобы не случилось в конфигурации. __ – означает что эта переменная будем передана вновь создаваемому каналу командой Dial.
И теперь при получении кода ##N от пользователя мы просто смотрим есть такая переменная в DB Астериска, если есть, то переводим сохраненный ранее канал в место где начинается воспроизведение.
[transfer-context]
exten => “”_##X,1,NoOp(${DB(br/${CALLERID(num)})})
exten”” => “”_##X,2,ChannelRedirect(${DB(br/${CALLERID(num)})},transfer-context,${EXTEN},3)
exten”” => _##X,3,Playback(${DIR}${MYVAR}_${EXTEN:-1}) ;
exten => _##X,4,Hangup()
Таким образом никаких скриптов, все гибко, красиво, изящно.
Бай.
PS: http://asteriskfaqs.org/2010/09/09/asterisk-users/set-channel-variable-from-within-other-channel.html
PS: https://wiki.asterisk.org/wiki/display/AST/Function_DB
14.06.2015
ACK. OPENSIPS. SEMS. ROUTE
Столкнулся с проблемой при использовании SEMS. При звонке ACK отправлялся на тот же IP что и сервер. И не доходил до абонента. проблема решается удалением IP адреса из списка локальных доменов. решение было найдено в архиве за 2009 год.
Приятного Opensips всем.
4.06.2015Opensips Ростелеком chel.media.usi.ru
Чтобы подключить услугу НЕОФОН от Ростелекома на Опенсипсе – необхоидмо выполнить несколько условий.
1. Ростелеком трЕбует:
FROM: sip: 7337788@chel.media.usi.ru
2. Исходящий звонок должен происходить обязательно с железки с которой была регистрация, вот рабочая версия для модуля registrant:
modparam(“uac_registrant”,”uac”, “sip:chel.media.usi.ru,,sip:7298171@chel.media.usi.ru,,7337788,PASSWORD,sip:7298171@123.123.123.123:5060,,,”)
3. при получении пакета 407 необходимо отсылать логин и пароль:
route[AUTHCHECK] {
if (t_check_status(“(401)|(407)”)) {
xlog(“got auth request from”);
if ( uri=~”sip:.+@chel.media.usi.ru” ) {
$avp(authrealm) = “Realm”;
$avp(authuser) = “7337788”;
$avp(authpass) = “PASSWORD”;
}
}
failure_route[2] {
xlog(“second failure route”);
uac_auth();
route(AUTHCHECK);
}
6.11.2014Mysql. With recursive. Суммировать результаты запроса. Having.
Привет, давеча обнаружил что в таблице со звонками есть двойные звонки. Соответственно возникают две задачи:
1. Удалить дублирующися записи (легко),
2. Делать выборку без дублирующихся записей (легко),
3. Посчитать сумму звонков в продублированных записях. (интересно).
1 и 2 я опущу, это не интересно. А вот третий пункт делается так:
with recursive mytable as (select max(duration) from acc where time > ‘2014-08-01 00:00:00’ and srcip like ‘ip address’ and duration > 0 group by callid having count(callid) > 1) select sum(max) from mytable;
но это не включает секунды третьего звонка которые тоже есть.
создадим темповою табличку для экспериментов:
create temporary table acc2 as (select * from acc order by time desc limit 1000);
yooxy# delete from acc3 where id in (select max(id) from acc3 where time > ‘2014-08-01 00:00:00’ and srcip like ‘address’ and duration > 0 group by callid having count(callid) > 1);
DELETE 224
yooxy_1711=# delete from acc3 where id in (select max(id) from acc3 where time > ‘2014-08-01 00:00:00’ and srcip like ‘address’ and duration > 0 group by callid having count(callid) > 1);
DELETE 27
yooxy_1711=# delete from acc3 where id in (select max(id) from acc3 where time > ‘2014-08-01 00:00:00’ and srcip like ‘address’ and duration > 0 group by callid having count(callid) > 1);
DELETE 0.
правда это вручную, зато все под контролем. ну и удалим таблицу.
drop table acc2.
25.07.2014
Load Balancing SIP
UAC – > LB -> (PROXY1 | PROXY2 | … ) -> UAS
Задача: раскидать звонки между sip proxy, сначала на PROXY1, в случае его недоступности на PROXY2.
В идеале если хочется распределять нагрузку между несколькими прокси серверами, то нужно делать LB в режиме stateless (использовать функцию forward() совместо с модулем dispatcher) однако в таком случае нет возможности сделать failover в случае падения одного из проксей, т.к. response нельзя поймать в отдельный блок. (хотя возможно разместив код в разделе onreply_route… надо будет проверить).
Сначала учтем ситуацию что в сообщении уже содержится route и вызовем функцию loose_route. без нее вы рискуете на некоторых звонках, это зависит от вашего окружения, получить зацикливание сообщений ACK. Нужно обязательно проверить, чтобы loose вызывался для ACK сообщений и участвующих в диалоге, а не для первых сообщений в диалоге, т.к. это может привести к пробеганию через ваш прокси трафика на чужие сервера, либо на поставщиков услуг, что страшнее, т.к. это сразу минус бабки.
Ниже представлен конфиг:
request_route {
if (loose_route()) { #Если сообщение содержит Route: то функция выдаст 1. Если его там нет, то 0. Здесь мы проверяем, есть ли в сообщении Route и если да, то…
xlog(“L_INFO”,”ACK TRansported…”); #Выводим в лог любую хрень полезную для понимания
t_relay(); #Оправляем сообщение согласно этому заголовку Route.
exit; #Завершаем выполнение скрипта, т.к. больше LB делать ничего не надо.
}
if (!ds_select_domain(“1”, “8”)) # Здест мы пользуемся функцией которая выберет для нас адрес прокси (proxy1) и подставит его в URI (первая строчка в SIP сообщении)
{
xlog(“L_ALERT”,”404 No route $ru”); # ВЫводим в лог сообщение елси ни один из проксей не доступен.
send_reply(“404″,”No route”); #Посылаем “клиенту” 404 сообщение, мол, нет возможности отправить вызов.
exit; # Завершаем скрипт
} else {
t_on_failure(“RTF_DISPATCH”); # Если таки прокси для сообщения определен, то привязываем ответ к разделу “RTF_DISPATH”, там мы поймаем ответ, и если он говорит о проблеме с прокси, то отправим сообщение на другой прокси.
xlog(“L_ALERT”,”Passed dispatcher $ru via $du $rm”); # Запишем в лог информацию о том, что сообщение будет отправленно на $ru.(при вызове функции ds_select_domain $ru меняется на новый
route(RELAY); # собственно отправляем сообщение в раздел где оно отправится к $ru.
}
}
| Posted in Без рубрики, Готовые решения | No Comments »
Номера в городах России
Получили таблицу стоимости городских номеров.
Появился список городов в которых можно приобрести номер для рекламных целей. Подключение номера 5-7 дней. Переадресация выставляется на любой Ваш номер.
Для заказа 100@yooxy.ru или по телефону +7 351 7502213.
Город | Стоимость | Аб. плата |
Архангельск | 950 | 590 |
Астрахань | 950 | 590 |
Барнаул | 950 | 590 |
Владивосток | 2850 | 590 |
Волгоград | 950 | 590 |
Воронеж | 1900 | 590 |
Екатеринбург | 950 | 590 |
Ижевск | 950 | 590 |
Иркутск | 950 | 590 |
Казань | 950 | 590 |
Калининград | 950 | 590 |
Кемерово | 950 | 590 |
Краснодар | 950 | 590 |
Красноярск | 950 | 590 |
Минеральные Воды | 950 | 590 |
Москва | 950 | 590 |
Мурманск | 950 | 590 |
Нижний Новгород | 950 | 590 |
Нижний Тагил | 950 | 590 |
Новосибирск | 950 | 590 |
Омск | 950 | 590 |
Оренбург | 950 | 590 |
Пермь | 950 | 590 |
Петропавловск-Камчатский | 2850 | 590 |
Ростов-на-Дону | 950 | 590 |
Рязань | 1900 | 590 |
Самара | 950 | 590 |
Санкт-Петербург | 950 | 590 |
Саратов | 950 | 590 |
Сочи | 950 | 590 |
Ставрополь | 950 | 590 |
Сургут | 1140 | 590 |
Тверь | 1900 | 590 |
Тольятти | 950 | 590 |
Томск | 950 | 590 |
Тула | 1900 | 590 |
Тюмень | 1140 | 590 |
Ульяновск | 950 | 590 |
Уфа | 950 | 590 |
Хабаровск | 2850 | 590 |
Челябинск | 950 | 590 |
Южно-Сахалинск | 2850 | 590 |
Якутск | 2850 | 590 |
Ярославль | 1900 | 590 |
| Posted in Городские номера, Готовые решения | No Comments »
Аналитика занятости линий для БД
Скриптик для анализа одновременного занятия линий.
Суть простая выбираем промежуток когда считаываем кол-во звонков, например каждые 5 минут начиная с 2012-09-01. И смотрим какие звонки к какому промежутку относятся. на выходе пара: время – кол-во линий.
Играя SQL запросами можно получить статистику по каналу или по префиксу, вообщем инструмент нужный, но данная реализация крайне медленная. Времени на разработку совсем нет, а решение принимать хочется на основании конкретных данных.
#!/usr/bin/perl
use strict;
use DBI;
use Time::Local;
use Date::Parse;
my $dbh = DBI->connect(‘dbi:Pg:database=yooxy’, ‘pavel’,”) or die $DBI::errstr;
#get all calls
my $date_start = ‘2012-09-09 00:00:00’;
my $leg = 300; #in seconds
my $calls_query = ‘select time,duration from acc where time > \”.$date_start.’\’;’;
my $sth = $dbh->prepare($calls_query);
$sth->execute;
my $ary_ref = $sth->fetchall_arrayref;
my $start_time = str2time($date_start);
my $a;
my $i,my $i2;
my %values;
$dbh->disconnect;
foreach ($a=$start_time;$a = $a + $leg;) {
$i2++;
foreach (@$ary_ref) {
$i++;
my $date1 = $_->[0];
my $duration = 60;# $_->[1];
my $line = $a;
my $start = str2time($date1);
my $end = str2time($date1) + $duration;
if (($line > $start) and ($line < $end)) { print “o$start < $line < $end\n”; $values{$a}++};
}
if ($values{$a} > 0) {print scalar(localtime($a)),”;”,$values{$a},”\n”;} else {print scalar(localtime($a)),”;”,0,”\n”;
};
last if $a > time();
}
print “\ncalls $i2; iter $i;”;
| Posted in ssl\tls, Безопасность, Готовые решения, Проблемы при настройке | No Comments »