Отправка и приём SMS сообщений шлюзов Yeastar TG серии

Отправка и приём SMS сообщений шлюзов Yeastar TG серии

Так как мы занимаемся продажами VoIP оборудования, к нам часто обращаются с различными техническими вопросами. Иногда доходит до того, что клиенты просят примеры кода на конкретных языках программирования. Работа с SMS и интеграция их в бизнес процессы — как раз один из таких регулярных вопросов, поэтому и хочется остановится на нём и рассмотреть более подробно.

Родина Yeastar, Китай, хотя, как мне кажется, в Yeastar стараются делать устройства с большей претензией на качество и удобство использования, чем их конкуренты. Это касается как физического, так и программного исполнения. Так, например, документация не всегда поспевает за изменениями в новых прошивках, а в отдельных случаях в ней могут отсутствовать важные моменты.

Web интерфейс

Отправлять и принимать сообщения можно через web интерфейс, в общем то, это стандартный способ для подобных железок. В шлюзах Yeastar этот интерфейс чем то отдаленно напоминает простенькие почтовые web морды — «папочки» Inbox и Outbox с незатейливыми фильтрами и поиском. В любом случае, это на голову выше чем то, что есть в GoIP, а главное хранятся не последние пять входящих сообщений для каждого канала, а значительно больше. Только, к сожалению, не понятно сколько, опять же в datasheet про это нет ни слова.

Так же как и в большинстве подобных железок, отправить сообщение можно с помощью GET запроса, что в общем не удивительно, это один из самых простых способов интеграции. Естественно, у Yeastar своя реализация со своими особенностями.

Для начала надо включить возможность отправлять SMS сообщения и USSD запросы. Для этого необходимо активировать «API Settings», если вы предпочитаете интерфейс на русском языке, то данный раздел будет называться «Настройки AMI» (правда очень логично?). Во вторых, необходимо поменять пароль по умолчанию, пока этого не сделаешь, авторизация не проходит, об этом опять же ни слова в документации.

API SMS сообщений шлюзов Yeastar TG

После этих манипуляций мы можем использовать запросы для SMS и USSD соответственно:

http://192.168.5.150/cgi/WebCGI?1500101=account=skytel&password=skytel&port=1&destination=89991234567&content=test

Response: Success
Message: Commit successfully!

http://192.168.5.150/cgi/WebCGI?1500102=account=skytel&password=skytel&port=1&content=%2A100%23

Request: 1,*100#
Response: Success
Message: Ваш баланс:
36.3 р.

Коротко о параметрах:

account=skytel – имя пользователя что мы указали в настройках API Settings
password=skytel – пароль из API Settings
port=1 – канал, с которого будет осуществлена отправка
destination=89991234567 – номер получателя, используется только при отправке SMS
content=test – текст сообщения или USSD запроса

Главное отличие от GoIP: при отправке SMS с Yeastar нет необходимости контролировать занят канал или нет, наше сообщение ставится в очередь и как только канал освобождается оно будет отправлено. А с USSD запросами работа происходит синхронно, т. е. ответ мы получаем сразу и нет необходимости его где то потом искать. Минус только в том, что ответы нам приходят в виде plain text, а хотелось бы что то более подходящее: JSON или XML.

Asterisk Managment Interface

Вся линейка шлюзов Yeastar построена вокруг Asterisk (программный сервер IP-телефонии от компании Digium), поэтому поддержка такого специфического протокола как SMPP отсутствует. Зато есть родной для Asterisk'a протокол AMI, работать с которым достаточно просто.

Для начала посмотрим как принимать сообщения:

#!/usr/bin/perl -w
 
use utf8;
use strict;
use warnings;
use AnyEvent::Impl::Perl;
use Asterisk::AMI;
use Data::Dumper;
use URI::Escape;
use feature 'say';
 
# Подключаемся к AMI
my $astman = Asterisk::AMI->new(
            PeerAddr => '192.168.5.150', # Адрес шлюза
            Username => 'skytel', # Имя пользователя из API Settings
            Secret  => 'skytel', # Пароль из API Settings
            Events  => 'on',
            Handlers => { 
                ReceivedSMS => \&received_sms # Подписываемся на приём сообщений
            },
            Keepalive => 60,
            on_error => sub { print "Error occured on socket\r\n"; exit; },
            on_timeout => sub { print "Connection to asterisk timed out\r\n"; exit; }
        );
 
die "Unable to connect to asterisk" unless ($astman);
 
sub received_sms {
    my ($asterisk, $event) = @_;
 
    say Dumper($event);
    # Приводим сообщение к читаемому виду
    say uri_unescape($event->{'Content'}) if ($event->{'Content'});
 
    return 1;
}
 
AnyEvent::Impl::Perl::loop;
 
=result 
$VAR1 = {
          'Total' => '1',
          'Recvtime' => '2016-07-15 17:49:55',
          'ID' => '',
          'Event' => 'ReceivedSMS',
          'Privilege' => 'all,smscommand',
          'Index' => '1',
          'GsmSpan' => '4',
          'Sender' => '+375297654433',
          'Smsc' => '+375296819996',
          '--END SMS EVENT--' => undef,
          'Content' => '%EF%BB%BF%D0%9A%D1%83-%D0%BA%D1%83'
        };
 
Ку-ку
=end

Пример достаточно прост и мне кажется, что всё должно быть понятно. Единственное, на что хочу обратить внимание это «GsmSpan». Мы все привыкли, что индексация массивов начинается с 0, здесь же не 0 и не 1, а 2, последовательный номер канала отображается как номер + 1, поэтому минимальное значение GsmSpan это 2.

Так же через AMI мы может отправлять SMS и USSD запросы:

#!/usr/bin/perl -w
 
use strict;
use warnings;
use Asterisk::AMI;
use Data::Dumper;
use URI::Escape;
use Encode;
use feature 'say';
 
# Connect to asterisk
my $astman = Asterisk::AMI->new(
            PeerAddr => '192.168.5.150',
            Username => 'skytel',
            Secret  => 'skytel',
            Timeout => 30, # Таймаут на выполнение команд, если используем USSD то ставим побольше
            Keepalive => 60,
            on_error => sub { print "Error occured on socket\r\n"; exit; },
            on_timeout => sub { print "Connection to asterisk timed out\r\n"; exit; }
        );
 
die "Unable to connect to asterisk" unless ($astman);
 
# Отправляем USSD запрос
my %action = (
    Action => 'smscommand',
    Command => 'gsm send ussd 2 "*100#"'
);
my $actionid = $astman->send_action(\%action);
my $response = $astman->get_response($actionid);
 
my @cmd = @{$response->{CMD}};
my $i = 0;
while ($i <= $#cmd) {
    if ($cmd[$i] =~ /USSD Message: (.+)/) {
        # Ответ будет закодирован, поэтому потребуется не много магии
        my $decodedHex = pack('H*', $1);
        say decode("UCS-2BE", $decodedHex);
    }
 
    $i++;
}
 
# Отправляем SMS
%action = (
    Action => 'smscommand',
    Command => 'gsm send sms 2 89991234567 "Привет" 11111'
);
$actionid = $astman->send_action(\%action);
$response = $astman->get_response($actionid);

По USSD думаю всё понятно, только не забываем, что каналы нумеруются с двойки. А по SMS есть небольшое уточнение: если нам судьба сообщения безразлична и статус отслеживать не надо, то после текста сообщения можно ничего не указывать. В противном случае, необходимо указать уникальный индификатор. Тогда, когда судьба смски станет известна, система отправит нам сообщение о её состоянии. Что-то такого вида:

$VAR1 = {
          'ID' => '11111',
          'Event' => 'UpdateSMSSend',
          '--END SMS EVENT--' => undef,
          'Status' => '1',
          'Privilege' => 'all,smscommand',
          'Smsc' => '+375296819996'
        };

Status = 1 говорит нам о том, что сообщение успешно доставлено, а в случае ошибки статус будет равен 0. Получать подобные сообщения можно, подписавшись на события UpdateSMSSend, делается это точно так же, как и при приеме SMS.

В качестве заключения

Лично мне было бы удобнее работать со шлюзом Yeastar TG400 через AMI. С другой стороны, я не вижу каких-то больших проблем и в случае использования GoIP. О чем я сознательно умолчал: у каждого из производителя есть бесплатный программный SMS сервер, в случае GoIP чтобы его использовать потребуется PHP, Apache и MySQL, а в случае Yeastar – Windows. Подобные продукты больше подходят для рассылки одинаковых сообщений по заранее подготовленным базам номеров, а не интеграции с какими-то приложениями. Это и есть причина, по которой я их пропустил. Если кому-то интересно, на сайтах производителей должны быть соответствующие описания.