Автоматизация вызовов абонентов в Asterisk.


Конфигурация плана набора в Asterisk - extensions.conf отвечает за то, чтобы поступающие от кого-либо вызовы попадали на нужный екстеншен, через заданный канал. Если же возникает необходимость, чтобы инициатором вызова было какое-либо внешние приложения, то существует несколько методов для этой задачи.

Существует четыре основных способа совершить исходящий вызов в Asterisk:

  • С использованием .call файлов. Это текстовые файлы, которые при помещении их в определенную директорию заставляют Asterisk совершить исходящий вызов.
  • Использую Интерфейс управления сервером Asterisk для инициации исходящего вызоваl. См. описание команды: Originate.
  • С использованием CLI интерфейса.
  • В Asterisk 1.4 и выше. При помощи команды плана набора FollowMe. Так как при помощи этой команды можно совершать несколько вызовов одновременно, ее можно задействовать “не по назначению” для совершения исходящих вызовов.

.call файлы

  • Просто помещаем наш call файл в директорию (по умолчанию): /var/spool/asterisk/outgoing
  • Если в файле конфигурации modules.conf установлено: autoload=no, убедитесь, что у Вас загружен модуль pbx_spool.so, иначе работа с .call файлами производиться не будет .
  • При обнаружении .call файла, Asterisk немедленно создаст канал для вызова, который будет соединен с указанным екстеншеном и приоритетом из плана набора. Все эти параметры будут взяты из .call файла.
  • Если дата модификации .call файла больше текущей даты на сервере, то Asterisk отложит обработку данного файла до тех пор, пока время модификации этого файла не сравняется или не станет больше текущего времени.
  • В качестве примера См.: "sample.call", который идет в составе дистрибутива.


Синтаксис .call файлов

  • Определяем куда и как нужно совершить вызов
    • Channel: : Канал, который будет использоваться для исходящего вызова.
    • CallerID: Name CallerID, please note that it may not work if you do not respect the format: CallerID: Some Name <1234>
    • MaxRetries: Количество попыток перед тем, как вызов будет считаться неудачным (не включая первую попытку, т.е. 0 = означает совершить 1 попытку вызова). Значение по умолчанию: 0.
    • RetryTime: Количество секунд между попытками вызова, не стоит очень часто ломиться на недоступный телефон. Значение по умолчанию: 300 (5 минут)
    • WaitTime: Количество секунд для ожидания ответа на вызов. Значение по умолчанию: 45.
    • Account: Установка поля “account code”для записи в CDR.
  • Если первый участник вызова ответил, то далее описываем с кем и как его соединить
    • Context: Контекст в файле extensions.conf.
    • Extension: Название екстеншена в extensions.conf..
    • Priority: Номер приоритета для екстеншена, с которого нужно начать выполнение.
    • Set: Установка переменных канала для использования их в логике обработки вызова на заданный екстеншен (например: file1=/tmp/to ).
    • Application: Имя приложения Asterisk, которое необходимо выполнить (используется вместо параметров context, extension и priority).
    • Data: Параметры для запускаемого приложения.
  • Новое (?) в Asterisk 1.4
    • AlwaysDelete: Yes/No – Если время модификации .call файла больше текущего, то этот файл не будет удален.
    • Archive: Yes/No – Переносить или нет .call файл в поддиректорию "outgoing_done" с установленным значением поля "Status: значение", где значение может быть: Completed, Expired или Failed.

Для совершения вызова в .call файле как минимум должно быть определено одно приложение или екстеншен, наряду с названием канала, с которого совершается вызов.



Специальный екстеншен 'failed'

Если на вызов не ответили, и существует стандартный екстеншен с именем failed и команда с приоритетом 1 в заданном (в .call файле) контексте, управление будет передано этой команде (Данная возможность реализована в Asterisk версии 1.2 и выше. Замечание: Эта функциональность уже работала в asterisk версии 1.2.14)
    • Замечание 1: Это работает, если вы совершаете вызов с использованием полей context, extension и priority, и естественно не будет работать, если используется приложение для совершения вызовов.
    • Замечание 2: В этом екстеншене довольно удобно обрабатывать CDR поле UserField, занося туда номер телефона, до которого не удалось дозвониться, с помощью команды: SetCDRUserfield(). Сервер Asterisk (например: 1.2.10) никуда не заносит имя канала, по которому совершался вызов (например: IAX2/15551234567), следовательно, чтобы получить его имя в этом екстеншене, используйте поле Set: в.call файле.
    • Замечание 3: Переменная канала ${REASON} принимает значение, которое определяет причину, по которой попытка вызова закончилась неудачей. Подробней можно почитать на странице: Переменная Asterisk: Reason.

Пример:

строка в .call файле:
Set: PassedInfo= 15551234567-moreinfo-evenmoreinfo

в extensions.conf:
exten => failed,1,Set(NumberDialed=$WARNING: No such module CUT! )
exten => failed,n,SetCDRUserField(${NumberDialed})




Пространство видимости переменных

  • Не забывайте к переменным добавлять префикс “_” или “__” , для того, чтобы они были видимы в создаваемых (“дочерних”) каналах!
  • Используйте приложения DBGet и DBPut, если возникают проблемы с передачей значений переменных.

Создание и перемещение .call файлов.


Вследствие того, что сервер Asterisk может прочитать эти файлы в любое время (например, когда он еще записан только наполовину), не создавайте их непосредственно в директории, где их читает Asterisk (/var/spool/asterisk/outgoing). Процедура их создания должна быть примерно следующей:
  • Создайте Ваш . call файл в какой-либо другой временной директории, например, в: /var/spool/asterisk/temp1234
  • Установите владельца и группу для этого файла, командой: chown asterisk:asterisk /var/spool/asterisk/temp1234 (если файл temp1234 создан от пользователя root, а Asterisk работает от системного пользователя asterisk).
  • Переместите этот файл, командой: mv /var/spool/asterisk/temp1234 /var/spool/asterisk/outgoing

Это будет корректно работать в большинстве случаев, т.к. в Unix операция перемещения файла в пределах одного раздела (команда mv) просто перемещает "inode" – указатель на файл – и он появиться в указанной директории уже сразу целиком, что предотвращает ситуацию, когда Asterisk считывает частично записанный файл. (Обратите внимание: Что все вышесказанное относиться только к тому случаю, если источник и точка перемещения файла находятся в пределах одного логического раздела, иначе, будет использоваться эквивалентная комбинация из команд "cp" и "rm", что может привести к вышеописанной проблеме.)
Замечание: Использование команды копирования файла (cp) не является безопасным методом для добавления .call файла в директорию сервера Asterisk для его последующей обработки, т.к. он может быть прочитан другой программой в середине процесса его копирования, когда он еще не полностью скопирован.

VB Scripts для создания .call файлов из простого текстового файла и перемещения готового .call файла в spool директорию сервера asterisk, используя команду pscp.


Сначала создайте обычный текстовый файл, который содержит номера телефонов на которые надо совершить вызовы. Скрипту требуется, чтобы на каждой строке было по одному номеру.
Скрипт проходит строки файла и создает новую поддиректорию в текущей рабочей директории для каждых из 20 номеров в этом текстовом файле.
Он использует номера телефонов в качестве имен для создаваемых .call файлов. Каждая поддиректория, в которой может быть до 20 .call файлов именуется как порядковое число, начиная с единицы.
Когда все .call файлы будут созданы, используется второй скрипт для переноса их в spool директорию asterisk при помощи программы pscp.
Pscp – свободная программа для передачи файлов, через протокол SSH, перед использованием этого примера убедитесь, что у Вас на машине установлена программа putty, создайте и сохраните сессию в putty до Вашей машины с asterisk.

;!!!!!!!!!!!!!!!!!!!!!
; Создаем текстовый файл с номерами телефонов по одному на строке
; например: phonenumbers.txt
; 1234567890
; 2134567890
; если текстовый файл содержит только вышеописанные две строки (без символов двоеточия),
; то в качестве результата работы скрипта будут созданы два текстовых файла, 
; по одному на каждый номер.
; Вы можете скопировать этот текст и вставить его код в какой-нибудь .vbs файл в  windows,
; а потом попробовать его выполнить.
; поместите его в туже директорию, где находиться файл с номерами телефонов.

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFilePhones = objFSO.OpenTextFile("C:\calls\Phone.txt",1) ;измените на путь к вашему файлу
strfname=1
strcnt=1
Set objFolders=objFSO.CreateFolder(strfname)
Do While objFilePhones.AtEndOfStream = False
	If strcnt=20 then; Вы можете изменить это значение на любое другое,
; если требуется другое число одновременных вызовов.
		strfname=strfname+1

		Set objFolders=objFSO.CreateFolder(strfname)
		strcnt=1
	end if
	strLine = objFilePhones.ReadLine

	strFullName = objFSO.BuildPath(strfname, strLine)
	Set objFileCalls = objFSO.CreateTextFile(strFullName)
	objFileCalls.close
	Set objFileCalls = objFSO.OpenTextFile(strFullName,2)
	objFileCalls.Write("Channel: ") ;сюда поместите информацию о Вашем канале
	objFileCalls.WriteLine(strLine)
	objFileCalls.WriteLine("CallerID: Name <1234>") ;укажите тут требуемое значение для  caller id 
	objFileCalls.WriteLine("MaxRetries: 0"); укажите тут требуемое значение попыток
	objFileCalls.WriteLine("RetryTime: 3")
	objFileCalls.WriteLine("WaitTime: 30") ;тут укажите время ожидания ответа
	objFileCalls.WriteLine("Context: test") ;Тут укажите название контекста
	objFileCalls.WriteLine("Extension: 800") ; Тут укажите название екстеншена
	objFileCalls.WriteLine("Priority: 1") ; Тут укажите  номер приоритета
	objFileCalls.Close
	strcnt=strcnt+1
Loop
;конец кода первого скрипта, который создает .call файлы.

;следующий скрипт переносит файлы в spool директорию сервера asterisk spool.
; Предполагается, что pscp находиться в c:\pscp.exe и 
;имя сохраненной сессии в putty до Вашего asterisk сервера - asterisk.

Set objFSO = CreateObject("Scripting.FileSystemObject")

strcnt=1
strfldr="C:\calls\"&strcnt

for i=1 to 1000  ;предполагается, что у Вас не более 1000 директории и не более 20000 файлов
If objFSO.FolderExists(strfldr) Then
	strfldr="C:\calls\"&strcnt ; поменяйте путь на тот, который Вам необходим
	Set objShell = CreateObject("WScript.Shell")
	strcmd="C:\pscp -pw passwordhere c:\calls\"&strcnt&"\* root@asterisk:/var/spool/asterisk/outgoing"
        ;поменяйте username и password на нужные, а также путь до pscp и .call файлов
	objShell.Run strcmd
	strcnt=strcnt+1

else
	exit for
End If

wscript.sleep(36000)
next
;конец кода




Примеры



Пример 1


Файл: 1.call
Channel: Zap/1/1XXXXXXXXXXXX
MaxRetries: 2
RetryTime: 60
WaitTime: 30
Context: callme
Extension: 800
Priority: 2


Соединит канал ZAP, и произведет вызов, начиная с приоритета: 2 екстеншена: 800 в контексте: callme из плана набора в extensions.conf.

Пример 2

Совершается вызов на номер 14109850123 через аналоговый канал в ZAP группе 2 и соединяет его с неким екстеншеном с номером 84 (например, это будет команда: 84,1,Dial(SIP/84) ) в Вашей сети.

#
# Совершаем вызов через 2 группу аналоговых линий
# Устанавливаем некоторые таймеры ожидания ответа и число попыток
#
Channel: Zap/g2/14109850123
MaxRetries: 2
RetryTime: 60
WaitTime: 30
#
# Предполагается, что Ваш локальный екстеншен описан в контексте [extensions]
#
Context: extensions
Extension: 84
Priority: 1


Вышеприведенные примеры хорошо подходят, если нужно автоматически проигрывать какие-либо предварительно записанные сообщения абонентам или что-либо запускаете, когда вызываемый абонент снимает трубку. Однако, если Вы будете использовать эти примеры для соединения двух абонентов: когда пойдет вызов первого абонента и он снимает трубку, то только тогда зазвонит телефон второго абонента. В данном случае, второй абонент, когда снимет трубку, уже пропустит “алло” и приветственные фразы первого абонента, а может и несколько фраз!

Пример 3


Совершаем вызов на номер 14109850123 с SIP телефона с именем bt101.
Помещаем файл с нижеприведенным содержимым в spool директорию asterisk (не забываем, что на данный файл у сервера asterisk должны быть права на чтение и удаление):

Channel: SIP/bt101
MaxRetries: 1
RetryTime: 60
WaitTime: 30
#
# Предполагается, что обработка исходящих вызовов происходит в контексте [outgoing]
#
Context: outgoing
Extension: 14109850123
Priority: 1



Пример 4

Автоматический дозвон до указанного номера и проигрывание предварительно записанного сообщения. Позволяет повторить сообщение и подтвердить его получение. Дополнительная информация на странице: Автодозвон и отправка сообщений


Пример 5


Вызов внутреннего или внешнего екстеншена и соединение его с AGI скриптом для отправки сообщения.

Channel: Local/1000@from-internal
MaxRetries: 0
RetryTime: 15
WaitTime: 15
Application: AGI
Data: myagi.agi


В trixbox/freepbx системах, этот .call файл будет вызывать внутренний екстеншен 1000 и соединяет его с заданной AGI программой. Обратите внимание, что в отличие от синтаксиса файла extensions.conf , где вызов AGI скрипта производиться командой AGI(file.agi), здесь команда и ее аргумент задаются в двух разных полях.


Пример 6: Использование Asterisk в качестве системы оповещения.


Сервер Asterisk можно довольно просто использовать как систему оповещения, где оставленное голосовое сообщение на определенном номере будет проиграно всем телефонам или телефонам из определенной группы.

Это довольно просто реализовать. Для начала подготовим два голосовых приглашения:

pa-welcome.wav с примерным текстом:
Пожалуйста, оставьте Ваше оповещение после звукового сигнала. По окончании, нажмите решетку.

pa-confirm.wav с примерным текстом:
Нажмите 1 для отправки Вашего сообщения на все телефоны. Ноль – для отмены.

В Вашем плане набора создайте контекст, например, с именем: [pa-system] , куда совершаем переход по Goto, когда кто-то вызывает, к примеру, екстеншен 911 (или любой другой номер екстеншена, который Вам нравиться).


exten => 911,1,Goto(pa-system,s,1)

[pa-system]
exten => s,1,Answer
exten => s,n,Wait(2)
exten => s,n,Playback(pa-welcome)
exten => s,n,Wait(1)
exten => s,n,Record(pa-message.wav)
exten => s,n,Wait(1)
exten => s,n,Background(pa-confirm)
exten => s,n,WaitExten(10)
exten => s,n,Hangup()
exten => 1,1,System(cp /etc/asterisk/pa-system/*.call /tmp/)
exten => 1,n,System(mv /tmp/*.call /var/spool/asterisk/outgoing/)
exten => 0,1, Hangup()


Далее, в этом контексте будут копироваться dct .call файлы, которые Вы предварительно подготовили в поддиректории созданной в /etc/asterisk в spool директорию сервера asterisk. Для предотвращения частичного чтения файла при его копировании, сначала файлы копируются во временную директорию, а потом переносятся (командой rm) в директорию asterisk, где он обрабатывает .call файлы.
Файлы, которые предварительно подготовлены, удобно именовать как: <номер екстеншена>.call для удобства последующей работы с ними. например:

Файл: 218.call

Channel: SIP/218
Callerid: 911
MaxRetries: 10
RetryTime: 5
WaitTime: 20
Context: pa-call-file
Extension: 10


Итак, все .call файлы в директории /etc/asterisk/pa-system имеют одинаковое содержимое, за исключением первой строки, где Вы указываете, кому совершать вызов (например: Zap/1 или SIP/218)
В параметре “Context” указываем название контекста, из которого будут выполняться команды в случае успешного соединения с абонентом. Этот контекст также надо описать в файле плане набора - extensions.conf:


[pa-call-file]
exten => 10,1,Answer()
exten => 10,n,Wait(1)
exten => 10,n,Playback(pa-message)
exten => 10,n,Wait(1)
exten => 10,n,Hangup()


Все, теперь у Вас есть система группового оповещения!

Еще одна “фишка”, которую можно добавить:
Если используются SIP телефоны, которые поддерживают различные типы вызываемого звукового сигнала, Вы можете запрограммировать для них использование отличного от стандартного типа вызывного сигнала, когда идет вызов из системы оповещения (для индикации его еще на уровне вызывного сигнала).



Как задать выполнение .call файла в определенное время.


Файлы, у которых дата модификации файла больше текущего времени, будут игнорироваться до того момента, как их дата модификации не станет равной или меньшей системного времени на сервере. Просто создайте файл, например, в директории: /var/spool/asterisk/tmp, модифицируйте его параметр “mtime”, используя команду "touch", и переместите его в директорию, где их “ждет” сервер asterisk...

$ date
Mon Mar 19 13:52:30 EDT 2007
$ touch -d 20080101 /var/spool/asterisk/tmp/blah
$ mv /var/spool/asterisk/tmp/blah .
$ ls -l blah
-rw-r--r-- 1 andrew users 0 Jan 1 00:00 blah


Пример на Bash: выполнить .call файл через 100 секунд:

# Получаем текущее время в секундах
NOW=`date +%s`
# добавляем к ней 100 секунд
let NOW=$NOW+100
# создаем timestamp (строку с датой и временем), которую потом используем в команде 'touch -t' (между %M. %S не должно быть пробелов)
TOUCH_TMSP=`date -d "1970-01-01 $NOW sec GMT" +%Y%m%d%H%M. %S`
# выполняем команду “touch”
touch -t $TOUCH_TMSP blah


Замечания по работе с большим количеством одновременных исходящих вызовов.


Вы можете просто ограничить число одновременных исходящих вызовов, контролируя число файлов в директории для .call файлов (по умолчанию: /var/spool/asterisk/outgoing). Например, если нужно ограничить число одновременных вызовов в Asterisk 10 вызовами, просто в процессе, который отвечает за перенос .call фалов, контролируем, чтобы их число не превышало десяти в spool директории. Если их там становиться меньше, переносим следующие ждущие обработки во временной директории файлы в директорию, где их “ждет” asterisk, тем самым поддерживая число одновременных исходящих вызовов на нужном уровне.

Обратите внимание: Было получено несколько сообщений от пользователей, что Asterisk “задыхается” (не обрабатывает некоторые.call файлы), когда сразу очень много файлов помещается для обработки в spool директорию сервера Asterisk. Чтобы избежать таких ситуаций переносите файлы небольшими порциями с небольшой задержкой.


.call файлы и CDR записи

  • Предотвращение отсутствия CDR записей при использовании .call файлов
Для этого используйте один из вариантов для совершения вызовов:
    • a) Используйте параметры Context/Extension/Priority в .call файле вместо непосредственного указания приложения для выполнения: Application/Data.
    • b) Совершайте вызовы через локальный канал вместо непосредственного указания имени канала для вызова абонента.
В противном случае Asterisk не будет выполнять ту часть внутренней логики, которая отвечает за протоколирование вызовов и, следовательно, CDR записи не будет сгенерированы. Когда вы используете для совершения вызова связку: Context/Extension/Priority, вы просто как бы выполняете безусловный переход в определенную точку плана набора, как будто вызов поступил в нее обычным образом, а в этом случае все вызовы будут протоколироваться обычным образом.

  • Номер вызываемого абонента не будет сохранен в CDR записи. Если Вам необходима эта информация в последующей обработке CDR записей, Вы можете установить поле CallerID в .call файле в значение этого номера и он будет сохранен в CDR записи. Однако, в этом случае, абоненту, которого Вы вызываете, будет высвечиваться, что ему звонит он сам (его же номер телефона), что не имеет особого смысла и может вводить их в заблуждение. Лучшем решением этой задачи будет указание этого номера в переменной канала, при помощи параметра “Set:” в.call файле с последующим занесением его в поле UserField CDR записи в логике плана набора отвечающей за вызов абонентов.


Советы и замечания

  • Создавайте .call файлы там, где Вам удобно и только после этого переносите их (не рекомендуется использовать команду копирования файлов по причинам, изложенным выше) в директорию, где их обрабатывает сервер asterisk. Asterisk очень агрессивно проверяет директорию, где он ищет эти файлы и, если вы будете создавать файлы непосредственно в этой директории, то скорее всего Вы будете очень часто получать сообщения asterisk о неправильном формате .call файлов, т.к. велика вероятность того, что он будет считывать не до конца сформированные файлы.
  • .call файлы должны иметь того же владельца файла или быть в одной группе с соответствующими правами для группы, от которого запущен сервер asterisk , иначе asterisk не сможет получить доступ к ним или удалить их, а Вы будете получать сообщения о невозможности доступа к файлу.
  • Если вы используете аналоговые внешние (городские) линии, подключенные через FXO интерфейсы, то, скорее всего, Вы столкнетесь с проблемой определения момента, когда вызываемый абонент снял трубку, особенно это касается вызовов на сотовые телефоны. Когда сервер Asterisk совершает вызов через FXO интерфейс (в момент, когда набор номера завершен и пошли тональные сигналы вызова), система считает, что вызов перешел в состояние 'отвечено', и продолжает выполнение дальнейших операций. Это означает, что Ваши голосовые сообщения будут проигрываться, когда с той стороны идут вызывные гудки, а когда абонент поднимет трубку, то он услышит только тишину, т.к. все приветствия были проиграны ранее.
Эту проблему можно попытаться решить так:
    • В файле chan_dahdi.conf (zapata.conf), попробуйте добавить параметр callprogress=yes до определения ваших FXO каналов директивой: “channel => n”. Обратите внимание, что эта установка распространяется на все каналы, которые будут описаны далее в этом файле__, перед описанием каналов, для которых это не нужно, не забудьте сбросить этот параметр директивой: callprogress=no !
      • Эта функциональность отмечена как: “HIGHLY EXPERIMENTAL“, если она используется, то необходимо выставить правильное значение параметра “progzone”, который определяет, набор тональных сигналов (их частота и отношение сигнал/пауза) какой страны используется для распознавания различных состояний аналоговых линий.
      • В некоторых случаях, включение callprogress=yes приводит к ложному срабатыванию детектора коротких гудков (сигнала отбоя), и вызов может беспричинно прерываться в середине разговора.
    • Как второй вариант, можно периодически повторять ваше сообщение. Например, установив, командой Set(TIMEOUT(response)=2), паузу между повторами в 2 секунды и переходить на повтор сообщения по команде: GoTo(s,1).
    • Еще один метод, состоит в использовании команды "WaitForSilence", которая будет ждать заданный промежуток времени, в течение которого в канале присутствует тишина, перед проигрыванием сообщения. В asterisk 1.2 существует проблема с данной командой, смотри описание багрепорта #2467 на issues.asterisk.org.
  • Для определения автоответчика можно попробовать использовать приложение: app_machinedetect.c. Работает с каналами: PRI, VoIP, или на FXO интерфейсах с включенным callprogress.

Что еще можно делать при помощи .call файлов.

  • Дозвон в определенно время при помощи cron'a.
  • Вы можете использовать некий екстеншен для проверки наличия зарегистрированного пользователя в системе (chanIsAvailable) и если это так, позвонить ему и напомнить ему, что надо выйти из системы и идти домой :-)
  • Другие приложения могут создавать .call файлы для систем оповещения и тревоги.
  • Вызов группы, куда включены агенты, обрабатывающие входящие вызовы.
  • Создание системы с обратным вызовом на основе DISA. Абонент звонит на определенный екстеншен, asterisk отбивает вызов и перезванивает абоненту, соединяя его с DISA и генерируя абоненту диалтон.

Ссылки по теме