воскресенье, 25 марта 2012 г.

Астериск и звонки в Skype через шлюз SIPNET

Я не люблю скайп-клиенты на мобильных телефонах — быстро разряжают аккумулятор, сидят в фоне и неизвестно чем в этом фоне занимаются. Более того, возникла потребность звонить в скайп с VoIP-телефона, умеющего делать буквенный набор, но не имеющего скайп-модуля. Платить аренду за скайп-бизнес мне не хотелось, а SFA уже не продавался.

Поэтому было принято решение сделать дозвон через шлюз всем известного оператора SIPNET. Сразу оговорюсь, что статья расчитана на пользователей, которые, как минимум, понимают, что такое extensions, макросы и хотя бы слегка владеют синтаксисом команд и функций Астериска. 



Для звонка необходим транк с пользователем на сервере SIPNET, у меня транк называется sipnet.ru. У вызываемого абонента звонящий определяется как skype.sipnet.ru, это, пожалуй, единственный минус такого решения.
Исходной идеей послужили две публикации (см. Материалы).

Система у меня развернута на Ubuntu Server 11.04, поэтому все пути относительно нее.

Итак, правим в каталоге /etc/asterisk/ файл extensions_custom.conf.

// контекст для тех, кому разрешено звонить на скайп
[skype]

// подключаем файл дозвона, о нем ниже ;
#include custom_sipuri.conf

// включаем из этого файла процедуру логики звонка
include => enable-sipuri-dialing

// у меня люди со скайпом звонят также локально
include => local

// и по всей России
include => russa_all


Далее там же (/etc/asterisk/) создаем файл custom_sipuri.conf следующего содержания.

// этот макрос просто звонит Dial-ом
[macro-uridial]
exten => s,1,NoOp(calling remote SIP peer ${ARG1})
exten => s,2,Set(CALLERID(num)=pabloescobar)

// он просто звонит
exten => s,3,Dial(SIP/${ARG1},120,tr)
exten => s,4,Congestion()

// вот тут, собственно, и сама идея - завернуть все звонки через процедуру проверки домена
[enable-sipuri-dialing]
exten => _.,1,NoOp(outbound call to ${EXTEN}@${SIPDOMAIN})

// если число символов в домене равно нулю, то это обычный вызов, не на sip-uri, иначе выходим
exten => _.,n,GotoIf($[${LEN(${SIPDOMAIN})} = 0]?skipuri)

// отрезаем порт
exten => _.,n,Set(SIPDOMAINNOPORT=$["${SIPDOMAIN}" : "(.*)\:"])

// если пустой, то это тоже обычный вызов
exten => _.,n,ExecIf($[ "${SIPDOMAINNOPORT}" = "" ] ?Set(SIPDOMAINNOPORT=${SIPDOMAIN}))

// если вызываемый домен и домен астериска совпадают, то это внутренний вызов, выходим
exten => _.,n,NoOp(EXTEN is [${EXTEN}], SIPDOMAIN is [${SIPDOMAIN}])
exten => _.,n,GotoIf($[${SIPDOMAINNOPORT} = ${MYDOMAIN}]?skipuri)

// иначе это вызов на внешний домен
exten => _.,n,NoOp(mydomain is [${MYDOMAIN}], calling domain is [${SIPDOMAINNOPORT}])
exten => _.,n,NoOp(domain [${SIPDOMAINNOPORT}] is remote, treating as SIP URI)

//если в этом домене первые пять символов - "skype", это и name@skype.com, и name@skype.sipnet.ru,
//то будем звонить через транк sipnet.ru пользователю EXTEN@skype.sipnet.ru
exten => _.,n,ExecIf({${SIPDOMAIN}:0:5}=skype) ?Set(DIALSTRING=sipnet.ru/) & Set(SIPDOMAIN=skype.sipnet.ru)


// собственно, звоним через макрос [macro-uridial] строкой sipnet.ru/EXTEN@skype.sipnet.ru
exten => _.,n,Macro(uridial,${DIALSTRING}${EXTEN}@${SIPDOMAIN})
exten => _.,n,HangUp()

// это выход из макроса в случае звонка внутри своей сети (MYDOMAIN) либо на обычный цифровой номер
exten => _.,n(skipuri),NoOp(continuing normal dialplan execution [${SIPDOMAIN}])
exten => _.,n,Goto(from-internal,${EXTEN},1)

// спасибо, до свидания
exten => h,1,HangUp()


Материалы:
1. P2P SIP URI Dialing! www.blyon.com/p2p-sip-uri-dialing/
2. SIP URI Handling projects.colsolgrp.net/issues/427

Комментариев нет:

Отправить комментарий