Pull to refresh

Собственный «Кто звонил?» на базе Мультифона

Reading time 6 min
Views 7.6K
На какие вопросы вы найдёте ответы в этой статье:

  • как отправлять USSD через GSM/3G/4G-модем и читать ответы;
  • как отправлять SMS через Мультифон;
  • как использовать Яндекс SpeechKit в автоответчике на Asterisk.

На какие вопросы вы не найдёте ответов:

  • зачем нужен собственный «Кто звонил?».

Все понимают механизм сервиса, аналогичного «Кто звонил?»: по недоступности звонок уходит на специальный номер, там его принимают, сообщают звонящему, что абонент недоступен, и отправляют абоненту СМС с информацией о звонке. В приёме звонка с Мультифона на Астериске никаких тонкостей нет.

Вкратце
Регистрация:

register => 79xxxxxxxxx@multifon.ru:<password>:79xxxxxxxxx@sbc.megafon.ru/79xxxxxxxxx

Транк:

[multifon](!)
type=peer
insecure=port,invite
host=sbc.megafon.ru
fromdomain=multifon.ru
context=from_multifon
dtmfmode=inband

[multifon-in](multifon)

; Если мы ещё и звонить хотим
[multifon-out](multifon)
defaultuser=79xxxxxxxxx
fromuser=79xxxxxxxxx
secret=<password>


Первый вопрос, который встал передо мной в полный рост…

Как отправить СМС?


Самый простой путь, конечно же, купить человеческий HTTP API у какого-нибудь гейта и задёшево отправлять через него. Но во-первых, это неспротивно. А во-вторых, у меня на тарифе несколько сотен СМС включено в абонплату, и с телефона я их почти не отправляю.
Перепробовав все возможные варианты с MessageSend (и через диалплан, и через АМИ, и через чёрта лысого), а также помурыжив SipSak, я сдался и попробовал скрипт для Yate.
О чудо! СМС через Мультифон отправляются.
Но Yate – слишком тяжеловесный движок, подумал я. Yate должен крутиться демоном и требует совершенно избыточной для этих задач конфигурации, подумал я. Поэтому, изучив перловый скрипт, я вычленил из него главное и вернулся к сипсаку. Главным оказался набор нестандартных заголовков (включая обязательного совершенно определённого юзер-агента, ага):

User-Agent: MCPC-MG-1-0-34-3490/2.0.0.5301
X-Movial-Content: sms/text
X-Movial-DeliveryReport: true
Content-Type: text/plain; charset=ISO-10646-UCS-2

Кстати, кастомный юзер-агент, насколько я понимаю, изначально полностью исключал возможность отправки через Астериск.
На первый взгляд, всё казалось просто. Но сообщения всё равно не уходили. Включив дебаг у сипсака, я понял, что эта скотина имеет набор предопределённых заголовков, которые тупо дублируются, если заданы в командной строке. Пришлось брать в руки напильник.
Обработав сипсак так, чтобы кастомные заголовки он считал приоритетными, а дубли отбрасывал, я добился наконец первого результата. Но моя радость была бы неполной, если бы сипсак корректно кодировал передаваемое сообщение. Расписывать всю эпопею вкостыливания в код, построенный на нуль-терминированных строках, UCS2-BE я не буду, сразу представлю на ваш суд результаты своих трудов: https://github.com/wolandtel/sipsak. Сам процесс отправки СМС я обернул в удобный скрипт. Для комплекта в последнем репозитории есть скрипт settings.sh, который упрощает настройку пароля и маршрутизации (SIP, GSM+SIP, GSM) на мультифоне.
Ну, вот, отправлять СМС мы умеем. Теперь можно настроить сам…

Автоответчик


Здесь нам поможет AGI. Скажу сразу, что от природной лени протокол AGI реализовывать я не стал, а лишь сделал в скрипте такую эмуляцию, которая позволяет выполнить задачу. А именно, скрипт должен дождаться от астериска ответа на команду STREAM FILE, чтобы проигрывание не закончилось раньше времени.

Диалплан
; autoresponder
exten => 79xxxxxxxxx,1,NoOp
same => n,AGI(notify.agi)
same => n,Set(MSG="Абонент умер. Когда воскреснет, может перезвонить. Но хотите ли Вы этого?")
same => n,Answer
; Params: speaker, emotion, robot
; speaker: [мужские]: zahar, ermil; [женские]: jane, omazh.
; emotion: good, neutral, evil, mixed.
; robot: true, false.
same => n,AGI(tts.agi,${MSG},ermil,neutral,true)
same => n,Hangup


Можно было, конечно, отправлять СМС из того же скрипта, которым проигрывается сообщение, но с точки зрения структуры приложения логично было их разнести.

notify.agi
#!/bin/bash

while read line; do
    [ -z "$line" ] && break
    num=$(echo $line | grep 'agi_callerid: ' | sed 's/agi_callerid: //')
    [ -n "$num" ] && break
done < /dev/stdin

[ -z "$num" ] && exit 1

/usr/local/multifon/sms.sh 79xxxxxxxxx "Пропущенный вызов [$(date '+%Y-%m-%d %H:%M:%S')] с номера +$num"


Абсолютно никакого AGI, по факту. Просто делаем, что требуется, и закругляемся.

tts.agi
#!/bin/bash

text="$1"
speaker="$2"
emotion="$3"
robot="$4"

key=<yandex API key>
dir=/var/lib/asterisk/tts
url=https://tts.voicetech.yandex.net/generate
dbg=${0/.agi/.log}

fname="$dir/$(echo -n "$text-$speaker-$emotion-$robot" | md5sum | cut -d' ' -f1)"
wav="${fname}.wav"

echo -e "$@" > "$dbg"
while read line; do
    echo $line >> "$dbg"
    [ -z "$line" ] && break
done < /dev/stdin

[ -f "$wav" ] || /usr/bin/curl -s --data-urlencode "text=$text" \
                        --data-urlencode format=wav \
                        --data-urlencode lang=ru-RU \
                        --data-urlencode "speaker=$speaker" \
                        --data-urlencode "key=$key" \
                        --data-urlencode "emotion=$emotion" \
                        --data-urlencode "robot=$robot" \
                        "$url" > "$wav" || exit 1

for f in al ul gsm; do
    [ -f "${fname}.$f" ] || /usr/bin/sox "$wav" -r8k "${fname}.$f" || exit 2
done

echo -e "STREAM FILE $fname 0"

read line < /dev/stdin
echo $line >> "$dbg"


Здесь тоже нет AGI. Мы только дожидаемся ответа от Астериска командой read, чтобы не закрыться раньше, чем он проиграет сообщение. Прошу обратить внимание на параметр -r8k для сокса. Почему-то без параметров он неправильно выставляет рейт выходному файлу, и сообщение получается замедленным в два раза. Подробности самого Яндекс SpeechKit API и то, как получить ключ, есть в документации.

Основная часть на этом закончена. Но статья будет неполной без описания нюансов.

Подготовка сим-карты


Очевидно, чтобы всё это завелось, потребуется отдельный номер с Мультифоном. Вы покупаете сим-карту и…? Простой и очевидный путь – вставить её в телефон, набрать необходимые запросы и начать использовать. Но мы не ищем лёгких путей. У меня с давних времён валяется пачка 3G-модемов (знаменитый E1550 и чуть менее знаменитые E150), но нет ни одного свободного телефона. А использовать свой для таких операций я не люблю: лишний раз выключать, вскрывать.
Итак, для получения и отправки СМС мы будем использовать давно и хорошо всем известный smstools. Настройку я описывать не буду, ибо боян. Только автодекодинг не включайте. А то будет вам мусор вместо тела сообщения.
Итак, после приобретения сим-карты нужно сделать две вещи:

  • получить пароль для личного кабинета (ибо эти бабуины убили Сервис-гид, и теперь для каждого номера требуется собственный логин);
  • подключить Мультифон (ибо эти бабуины сломали веб-интерфейс, и без USSD не обойтись).

И если с приёмом СМС всё понятно, то USSD – не такая прозрачная вещь.
Перед тем, как продолжить, можете ознакомиться с репозиторием дополнений к smstools, облегчающих работу с SMS и USSD. В разделе example репозитория есть перловый инструмент работы с USSD, который я использовал для понимания сути кодирования/декодирования символов в USSD. Авторство не указано, и ссылок нет потому, что я не помню, где его взял. Можно использовать его, но лично я не понимаю perl, а техномагию я не люблю. Поэтому, чтобы почувствовать контроль над ситуацией, я написал утилиты encode и decode. Как мне кажется, код на си куда проще для понимания, поэтому утилиты будут полезны тем, кто тоже захочет знаний.
Подозреваю, что если выставить другую кодировку командой AT+CSCS, проблема отпадёт сама собой, но мы же не ищем…
В остальном работа с USSD проста: модем регистрирует три девайса ttyUSB. Пишем в первый, читаем из последнего.
Для подключения личного кабинета достаточно просто отправить 105: ./ussd.sh '*105#' (подразумевается, что /dev/modem указывает на первый из ttyUSB модема). Дожидаемся СМС с паролем (удобнее всего это делать командой ls -lrt /var/spool/incoming), после чего смотрим ./viewsms <path>.
Мультифон подключается в два этапа: запустив cat /dev/ttyUSB2, отправляем *137#. Копируем ответ и выясняем, что там написано, с помощью decode. А приходит в ответ менюшка, где для подключения нужно выбрать пункт 1. Выполняем ./ussd 1 и получаем в СМС пароль.

На этом можно было бы и завершить, но так как мы имеем дело с бабуинами, стоит упомянуть про…

Организационные моменты


  1. Обязательно укажите, чтобы новую сим-карту подключили на существующий лицевой счёт. Насколько я понял, отключение по неактивности не работает, если на все номера один баланс. Да и вообще удобнее. Когда был жив сервис-гид с возможностью управлять всеми номерами из-под одной учётной записи, этот совет был ещё актуальнее.
  2. В современном мире наебизнеса уже никому не надо объяснять, что сразу после покупки сим-карты необходимо отключать все условно-бесплатные опции (в том числе, «Кто звонил?», ага). Но! Бабуины пошли дальше и некоторые из условно-бесплатных опций не отображаются в личном кабинете. Отключить их можно только звонком в контакт-центр. Желательно подробно прочитать описание тарифа, чтобы выяснить, каких же замечательных вещей вам впарилипродали.
  3. В качестве номера для отправки СМС я использую свой основной номер, потому что на нём есть предоплаченный пакет. Естественно, для этого Мультифон на нём тоже должен быть включен (но совершенно не обязательно держать его постоянно зарегистрированным). Дополнительный номер используется только для приёма звонков.

PS


К сожалению, Мультифон не передаёт номер, переадресовавший звонок. Поэтому, если вы хотите подключить свой бонусный «Кто звонил?» друзьям/родным, придётся на каждого человека покупать отдельный номер под автоответчик. Ещё можно попробовать переадресацию с доп. набором (+79XXXXXXXXXpXX), но я не проверял. Если проверите, пишите.

А вот теперь – всё!
Tags:
Hubs:
+4
Comments 7
Comments Comments 7

Articles