Как сделать будильник при помощи Asterisk, FreeBSD и наличии небольшого количества свободного времени

Сижу я на работе утром. Коллега опаздывает, делать особо нечего. Скучно. Наконец приходит опоздавший и жалуется, что не проснулся по своему будильнику. То ли не завел, то ли не услышал, этого я уже не помню. Почему бы не устроить ему звонок с работы, с утра пораньше?

Сказано — сделано. Решил я написать будильник.

Как будем реализовывать?


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

Записывать кому и когда звонить в сам скрипт мне показалось неудобным, поэтому был создан текстовый файл alarmnumbers.txt вида:

89993332211 1000 1100 1200 1300 1400 1500 1600 /recs/macroform-robot_dity
89993332211 1005 1105 1205 1305 1405 XXXX XXXX /recs/macroform-robot_dity


Где первое это номер телефона, на который будет совершен звонок, дальше идут 7 блоков, разделенных пробелами, с указанием времени звонка: первый блок для понедельника, второй для вторника, и так далее. В самом конце путь к файлу, который будет воспроизведен, если поднимут трубку. Это должен быть любой звуковой файл, который астериск сможет скушать. Имя файла пишется без разрешения, астер сам выберет, какое ему нужно. Можно звуковой файл и вовсе не указывать, в таком случае при поднятии трубки звонок будет сразу же сброшен. Ну а XXXX вместо времени указывает не совершать побудку. К примеру, в выходные.

В файлике может быть сколько угодно строк, повторения номеров тоже допустимы. Решение, конечно, не самое оптимальное, но поскольку использование будильника предполагается только внутри IT отдела, то вполне подходящее. Разместил я его на шаре win сервера, подцепив её к FreeBSD при помощи mount_smbfs.

Сам скрипт запускается по крону раз в сутки. Т.е. обновление данных кому и куда звонить происходит каждый день в 00.02.

Разберем, что делает скрипт:


#!/bin/sh

echo 'START!'

filename=/recs/alarmnumbers.txt
CallFileName=AlarmFile
NewCallFilesPath=/var/spool/asterisk/outgoing_new/
AsteriskCallFilePath=/var/spool/asterisk/outgoing/



Указываем, где что лежит и как называть call-файлы для asterisk (ему, кстати, имя файла не важно, можете указать любое)
filename — наш файл со списком кому и когда
CallFileName — имя call-файла, только для нашего удобства
NewCallFilesPath — каталог для создаваемых call-файлов. Создавать их там, откуда их берет астер, нельзя — читает он их быстро и часто, может прочитать неполный. Этот каталог должен быть на том же разделе с каталогом, где астер ищет call-файлы для выполнения.
AsteriskCallFilePath – каталог, из которого астер читает call-файлы. Т.к. у меня FreeBSD, у вас они могут располагаться в другом месте.


currentdate=$(date +%Y%m%d)
weekday=$(date +%u)
var0=0



Запоминаем текущее время и день недели.


case "$weekday" in
        1)wd="13-16";;
        2)wd="18-21";;
        3)wd="23-26";;
        4)wd="28-31";;
        5)wd="33-36";;
        6)wd="38-41";;
        7)wd="43-46";;
esac



В зависимости от дня недели будем выбирать соответствующий блок в alarmnumbers.txt. Это параметры для cut.


cat $filename | while read line
do
 DialTime=$(echo $line | cut -c$wd)
 var0=`expr $var0 + 1`
 number=$(echo $line | cut -c1-11)
 dialwavfile=$(echo $line | cut -c48-200)



Читаем alarmnumbers.txt. построчно, выбирая нужные параметры из каждой строки.
DialTime — время звонка, тут используется параметр wd, который мы задали раньше.
number — номер, на который будет совершен звонок
dialwavfile — звуковой файл


 echo "Channel: SIP/providername/$number" > "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "CallerID: CompanyNumber" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "MaxRetries: 2" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "RetryTime: 450" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "WaitTime: 20" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "Application: Playback" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "Data: $dialwavfile" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
 echo "Archive: yes" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"



Создаем call-файл. Структура у него получится вот такая:


Channel: SIP/providername/89993332211
CallerID: CompanyNumber
MaxRetries: 2
RetryTime: 450
WaitTime: 20
Application: Playback
Data: /recs/macroform-robot_dity
Archive: yes



Channel — Через что звонить, указывается любой канал. У меня указан пир для внешних звонков. Если вы собираетесь звонить и на внутренние номера, то придется дописать условие, по которому будут подставляться различные каналы.
CallerID – Думаю, понятно. Не актуально, если это звонок на внешнюю линию и провайдер не позволяет менять CallerID
MaxRetries — Не поднял трубку? Не беда! Позвоним еще, вдруг не проснулся? Параметр сообщает сколько раз пытаться вызвать абонента
RetryTime — Через столько перезвонить, в секундах
WaitTime — Дозваниваться до абонента столько секунд.
Application — Это то приложение астериска которое будет использовано если абонент поднял трубку.
Data — Данные для приложения с предыдущей строки
Archive — Сохранять выполненные call-файлы, для анализа к примеру


 chmod 755 $NewCallFilesPath$CallFileName$DialTime$var0.call
 chown asterisk $NewCallFilesPath$CallFileName$DialTime$var0.call
 chgrp asterisk $NewCallFilesPath$CallFileName$DialTime$var0.call



Выдаем права на чтение, изменение и удаление пользователю, от которого запущен астериск.


 time=$(echo $line | cut -c$wd)
 case $time in
  XXXX) rm $NewCallFilesPath$CallFileName$DialTime$var0.call ;;
  "") rm $NewCallFilesPath$CallFileName$DialTime$var0.call ;;
  *) touch -t "$currentdate$time" "$NewCallFilesPath$CallFileName$DialTime$var0.call" ;;
 esac



Тут мы правим время изменения или удаляем call-файл в том случае если вместо времени указано XXXX.
Один из важных моментов в работе с call-файлами. Астериск будет читать только файлы с датой и временем изменения меньше или равной текущей. Таким образом, если мы укажем время модификации в будущем, астер будет ждать, пока не наступит нужное время.


 mv $NewCallFilesPath$CallFileName$varr$var0.call $AsteriskCallFilePath



Перемещаем наш созданный файл к астеру на съедение.

done

echo "HAPPY END!!!"

exit 0



Завершаем скрипт. С ним все.

Осталось только запихать его выполнение в крон. Не забудьте дать ему права на исполнение.


2       0          *       *       *       /usr/bin/my/alarm.sh



Ну, в общем-то, будильник готов. Заполняем alarmnumbers.txt и ждем звонка. У нас исправно работает уже 3 недели. Глюков пока не обнаружено.

Что в итоге?


Мы получили будильник с расписанием. Есть возможность исключать любые дни недели из расписания. Система будет создавать call-файлы, а астериск выполнять, когда наступит подходящее время. Ничего сложного.

Что плохо:


  • Запуск раз в сутки. Если захочется проснуться сегодня через 4 часа, астер ничего не сделает, звонок будет только через неделю.
  • Критично содержание alarmnumbers.txt. Если в нем будет косяк по неаккуратному заполнению, звонка не будет. Самому астеру ничего, конечно, не будет, но все равно неприятно.
  • Требуется заранее конвертировать звуковой файл в нужный формат. Автоконвертацию прикрутить поленился.
  • Коллега все равно не реагирует и на этот будильник.


Приветствуется критика, замечания, пожелания по улучшению.
  • +4
  • 11,6k
  • 7
Поделиться публикацией

Комментарии 7

    +1
    Спасибо! Буду завтра попробовать — как раз на работе хотели сделать что то подобное.
    Только у меня была мысль относительно ввода 4 цифр как пароля, иначе астер должен перезвонить через 3 минуты (один товарищ достал на работу опаздывать )) )
      +1
      Была такая мысль! Но тогда уже придется править диалплан. Добавлять новый контекст и прочее.
      Хотя у меня идея была более изощренная, это генерация случайного пароля, проговаривание его в трубку и ожидание ввода, не верно? Генерируем новый call-файл.
        +1
        EvilMause, вообще один опытный товарищ посоветовал поковыряться в сторону AEL для решения вопроса.
          +1
          А у меня и так весь диалплан в AEL. Там по сути почти тоже самое, только более наглядно и более похоже на язык программирования, многое более удобно. Хотя конечно там до ЯП далеко. Но следует отметить, что профи его не используют, причины мне до конца не ясны. Но суть в том что AEL транслируется в обычный диалплан, и знать его все равно надо.
          Я вашу идейку завтра в голове покручу. Может выдам примерно как оно должно быть. В AEL. Если совсем хорошо пойдет, реализую свою.
          Еще бы неплохо конечно попробовать AGI, но его я не умею. Грусть — тоска.
      +1
      echo "Channel: SIP/providername/$number" > "$NewCallFilesPath$CallFileName$DialTime$var0.call"
      echo "CallerID: CompanyNumber" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
      echo "MaxRetries: 2" >> "$NewCallFilesPath$CallFileName$DialTime$var0.call"
      ....
      
      Можно заменить на
      cat > "$NewCallFilesPath$CallFileName$DialTime$var0.call" << EOF
      CallerID: CompanyNumber
      MaxRetries: 2
      ...
      EOF
      

      chown asterisk $NewCallFilesPath$CallFileName$DialTime$var0.call
      chgrp asterisk $NewCallFilesPath$CallFileName$DialTime$var0.call
      
      на
      chown asterisk:asterisk $NewCallFilesPath$CallFileName$DialTime$var0.call
      
        +1
        Благодарствую! Дельное замечание. Там вообще огрехов много было. Многое поправил пока пост писал.
        0
        Я бы сделал список номеров будильника и даты/время звонка в формате кронтаб — привычнее, чтоли.
        Вместо команды можно писать уже номер.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое