Русский
Русский
English
Статистика
Реклама

Смс

Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

29.04.2021 12:05:53 | Автор: admin


Представляю вам цикл статей по созданию собственного шлюза для отправки SMS-сообщений.
В первой части мы определим цели и некоторые аспекты использования своего шлюза, настроим программное обеспечение для отправки SMS с использованием USB-модемов, а также рассмотрим несколько интересных вариантов отправки

Начнем мы статью с вопроса для чего отправлять SMS, ведь на дворе 2021 год? Да, мы уже привыкли к различным мессенджерам, уведомлениям в чат-боты, но SMS до сих пор обладает наиболее гарантированным уровнем доставки. Нет зависимости от наличия интернета и сообщение поступит даже в сети 2G и при слабом сигнале и на устройство без доступа к интернет. Таким образом, если планируется отправлять сообщения, которые гарантированно должны быть доставлены и критичны к времени доставки, то SMS это возможно лучший выбор.

Под SMS-шлюзом мы понимаем программно-аппаратное устройство подключенное к сети сотового оператора и позволяющее производить автоматизированную отправку сообщений.

Где можно применять это решение?


Самое простое и очевидное применение отправка уведомлений. Мы отслеживаем доступность различных сервисов и хотим знать о любых сбоях. Применение в системе умного дома позволит нам быть в курсе всех произошедших событиях. Да, многие IoT устройства умного дома оборудованы разъемом под сим-карту и могут сами отправлять SMS, но в этом случае на каждое устройство нужна отдельная сим-карта.
Мы можем проводить двухэтапную аутентификацию пользователей или производить валидацию введенного номера телефона. Регистрировать пользователей по номеру телефона, без необходимости создания логина и пароля. Отправлять сообщения о формировании заказа, отправке, получении, изменении баланса.

Но сообщения можно не только отправлять, их также можно получать и обрабатывать, что еще сильнее расширяет варианты применения. Все зависит только от вашей фантазии.

Стоит обратить особое внимание так как мы рассматриваем в статье вариант создания шлюза, то это позволяет отправлять сообщения сразу со всех описанных сервисов используя всего одно устройство.

Плюсы и минусы собственного шлюза


Как и любое решение, собственный шлюз обладает рядом достоинств и недостатков.
Мы не будем рассматривать плюсы с точки зрения точного сравнения с платными сервисами ввиду разного уровня получаемой услуги. Я приведу наиболее явные и вы сможете решить на сколько каждый пункт критичен именно для вас.

Плюс

  • Очень низкая цена за отправленное сообщение в районе 5 копеек за сообщение (при отправке 1000), против 2.65 на коммерческих шлюзах

Минусы

  • Отсутствует возможность задать имя в качестве отправителя будет номер телефона, а не Baton.ru, например. Но есть способ как решить это красиво и мы рассмотрим это в статье.
  • Отправка сообщения не чаще чем 1 раз в 10 секунд. Решается увеличением количества модемов.
  • Нет защиты от сбоев как на аппаратном, так и программном уровне. Объективности ради стоит отметить, что платные шлюзы тоже примерно раз в неделю сообщают о проблемах с доставкой определенным операторам. Мой опыт использования такого шлюза показывает всего один сбой за более чем 12 месяцев.

Итак, мы определили, что нам ожидать от собственного шлюза и мы готовы начать.

Устанавливаем Gammu, подключаем модемы


В качестве аппаратного ядра системы я буду использовать Orange Pi PC с Armbian просто потому, что он у меня есть и ничем не занят. Свою версию вы можете сделать на основе RPi, компьютера/сервера на Linux и даже виртуальной машины с проброшенными внутрь USB-портами это не имеет особого значения, главное мы будем использовать Linux.

Для общения с модемами я буду использовать Gammu очень мощный продукт позволяет общаться не только с классическими модемами, но и использовать телефоны в качестве модемов. Также этот продукт избавит нас от необходимости самим формировать пакеты сообщений, считать их длины, составлять и отправлять АТ-команды и самое главное позволить отправлять сообщения в PDU-формате.

Итак, подключимся к серверу, обновим систему и установим gammu:

ssh root@<IP_вашего_сервера>apt updateapt upgrade -yapt install gammu -y

Теперь нужно подключить модемы к USB-портам. Я буду использовать два модема, чтобы нагляднее показать чем отличаются настройки. Подключаем и смотрим какие порты они заняли.



Следующей командой мы смотрим, какой модем сколькими портами обладаем. Среди этих портов будет только несколько (или один), через который можно отправлять сообщения. Остальные это технические и нам не интересны.



На скрине выше видно, что установленный модем usb 5-1, Alcatel, имеет пять каналов. Теперь нам нужно определить какие из них используются для связи. Сделать это не сложно. Пишем в терминале:

screen /dev/ttyUSB1

В открывшемся окне вбиваем АТ и если в ответ получили ОК, то запоминаем этот порт это то, что нужно. Выходим CTRL+A затем D
Обратите внимание, что найдя один порт, все равно нужно проверить оставшиеся ttyUSB2, ttyUSB3, и далее. Например, используемый мной Alcatel, имеет 2 независимых канала на ttyUSB4 и ttyUSB5, что позволяет одновременно отправлять два SMS-сообщения через один модем.

Настройка Gammu


Итак мы определились с портами и теперь нужно настроить Gammu. Есть утилита конфигурации gammu-config, но мы не будем ее использовать, а запишем сразу данные в конфигурационный файл.

nano ~/.gammurc

дописываем в нижней части файла данные конфигураций. Каждый блок [gammu] это канал модема. [gammu] это канал по умолчанию, [gammu1] первый канал и т.д. Далее мы еще коснемся этого, когда будем отправлять сообщение. Итого я добавляю 3 доступных канала от двух своих модемов:

[gammu]port = /dev/ttyUSB0model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu1]port = /dev/ttyUSB4model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu2]port = /dev/ttyUSB5model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =

Краткое описание настроек:

  • model тип модема и как gammu следует общаться с модемом, at с помощью AT-команд
  • connection = at тип соединения. Подключаемся на скорости 9600
  • use_locking говорит gammu, что нужно блокировать доступ к модему на время работы с ним. Иначе возможны различные ошибки и сбои


Вид настроек в терминале

Проверка работы


Gammu настроена и теперь мы можем протестировать отправку сообщений.

Базовая команда для отправки сообщения на английском и всего с одного модема выглядит следующим образом:

gammu sendsms TEXT +70001234567 -text "Test message"



Теперь рассмотрим варианты поинтереснее с использованием дополнительных аргументов

Если в системе несколько модемов, то добавляем нужный порт аргументом "-s <номер_порта>". Мы затрагивали этот момент, когда заполняли настройки. Нумерация начинается с 0 и в нашем случае это промежуток 0-2.

gammu -s 1 sendsms TEXT +70001234567 -text "Test message"

Отправка сообщений в PDU-формате (кириллица, прочие языки и спецсимволы) "-unicode"

gammu -s 1 sendsms TEXT +70001234567 -unicode -text "Тестовое сообщение"

Отправка сообщений в PDU-формате с автоматической разбивкой на несколько сообщений и последующей склейкой на телефоне "-autolen 5". Данный аргумент подразумевает использование числа, хотя объективно оно ни на что не влияет, по крайней мере у меня работает одинаково при любом числе, поэтому я решил, что сообщения длиной больше 5 у меня не будет и использую это число

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -text "Тестовое сообщение"

Есть очень интересный аргумент, который полезен, если вы отправляете одноразовые коды "-replacemessages 1". Суть его работы заключается в следующем мы указываем телефону, что при получении сообщения он должен заменить сообщений с ID 1, если оно имеется в полученных на то, что поступило сейчас. Таким образом телефон автоматически затирает все ранее полученные сообщения и в переписке всегда отображается только последнее полученное. Единственное, решить стоит его использовать или нет, желательно в самом начале, чтобы всегда заменять ID 1

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"



А теперь, самый интересный, на мой взгляд аргумент "-flash".

Сообщение отправленное с этим параметром отображается телефоном сразу во всплывающем окне. Проигнорировать его невозможно, т.к требуется решение абонента о сохранении или отклонении данного сообщения. Но самое главное абонент не видит номер телефона отправителя в этом окне (помните это был один из минусов собственного шлюза). Таким образом, если мы используем шлюз для авторизации пользователей и отправки им кодов, то это решение позволяет сразу показать код без захода в программу Сообщения. По получении звукового сигнала о входящем сообщении человек берет телефон, снимает блокировку и сразу видит его на экране. Если абонент нажмет на Отмена, то сообщение не будет сохранено в памяти телефона. Отправляем сообщение и смотрим как оно выглядит:

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"


Всё!

В следующей статье


В этой части статьи мы настроили отправку сообщений, но оно происходит вручную. Во второй части статьи мы напишем небольшое API (на PHP) для получения запросов по http, сохранении их в базу данных и автоматической отправке через активный шлюз и не только

Подробнее..

Свой личный SMS-шлюз. Часть 2 создаём API и форму отправки

03.05.2021 12:05:25 | Автор: admin

Представляю вам вторую часть из серии статей по созданию своего шлюза.
В первой части мы настроили Gammu, рассмотрели особо интересные параметры и успешно произвели отправку SMS сообщения. Сейчас нам предстоит задача посложнее создать некую программную прослойку (API), для того, чтобы можно было работать со шлюзом путем отправки запросов на этот API. В первую очередь это комфорт, во вторую большое количество дополнительных возможностей.

Если вы не знакомы с первой частью, советую сначала ознакомиться с ней:
Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

Постановка задачи


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

На чем будем писать backend
Тут все просто, что умеем, на том и пишем, поэтому в моем случае PHP

Авторизация
Конечно. Сервис будет смотреть в интернет, поэтому авторизация обязательна.

Один пользователь одна sim-карта?
Конечно нет. У нас сервис для личного пользования и мы хотим иметь один логин, но при этом отправлять с нескольких номеров. Но если появится необходимость выделить один шлюз под конкретный сервис, мы должны иметь возможность добавления пользователей.

Как мы хотим общаться с этим API, откуда будут попадать запросы
Общение будет через POST/GET. Запросы могут отправляться различными устройствами, в том числе и теми, которые не умеют POST или заморочно реализовать, поэтому принимать и обрабатывать будем $_REQUEST. Также мы хотим иметь возможность отправки сообщений через простую форму на сайте.

Один запрос один адресат?
Нет. В одном запросе с одним текстовым сообщением должна быть возможность указать несколько адресатов. Суть этого понятна. Например я отслеживаю наличие ЭЭ на даче и в случае отключения хочу получить уведомление на все свои телефоны, а может даже телефон супруги почему бы и нет, ведь уведомление важное.

История отправленных сообщений
Конечно, история это наша важная составляющая жизни, поэтому ее мы всегда храним

Балансировка нагрузки на карты
Да. Мы обладаем чувством меры и будем отправлять с одной карты не более какого-то числа сообщений, а значит их нужно считать и перед выбором шлюза проверять на исчерпание лимита.

Задача на разработку поставлена, цель ясна, итак приступим


Первое что мы сделаем, определим структуру база данных. Без нее, при наших потребностях никак. Использовать будем MySQL.
Дальше нужно будет написать пару классов, к которым мы были обращаться.

Приступим к созданию БД и создание таблиц
Я буду использовать несколько таблиц для:

  • users данные пользователей
  • smsc_gateway данных шлюзов
  • gateway_smscount счётчик отправленных сообщений по каждому шлюза в конкретный месяц
  • sms_queue очередь отправки сообщений
  • sms_archive история сообщений

Структура БД дамп таблиц
# Дамп таблицы users# ------------------------------------------------------------CREATE TABLE `users` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(10) NOT NULL,  `login` varchar(50) NOT NULL,  `password` varchar(32) NOT NULL,  `comment` varchar(200) NOT NULL,  `status` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы smsc_gateway# ------------------------------------------------------------CREATE TABLE `smsc_gateway` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `uuid` varchar(10) NOT NULL,  `host` varchar(15) NOT NULL DEFAULT '',  `port` varchar(5) NOT NULL,  `password` varchar(12) DEFAULT '',  `maxcount` varchar(6) NOT NULL,  `status` int(1) NOT NULL,  `gateway_id` int(2) DEFAULT NULL,  `state` varchar(11) DEFAULT NULL,  `comment` varchar(100) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы gateway_smscount# ------------------------------------------------------------CREATE TABLE `gateway_smscount` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `date` varchar(10) NOT NULL,  `count` int(6) NOT NULL DEFAULT '0',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_queue# ------------------------------------------------------------CREATE TABLE `sms_queue` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(11) NOT NULL DEFAULT '',  `dateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  `status` varchar(10) DEFAULT NULL,  `data` varchar(500) NOT NULL DEFAULT '',  `phone` varchar(11) NOT NULL DEFAULT '',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_archive# ------------------------------------------------------------CREATE TABLE `sms_archive` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `gateway_id` varchar(11) DEFAULT NULL,  `dateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  `data` varchar(500) DEFAULT NULL,  `status` varchar(11) DEFAULT NULL,  `phone` varchar(11) DEFAULT NULL,  `uuid` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;



Стоит подробнее остановиться на таблице с данными шлюза и используемых полей smsc_gateway. Здесь мы используем:

  • uuid id пользователя, которому назначен данный шлюз
  • host ip-адрес компьютера с модемами для подключения по ssh
  • port порт мы тоже укажем, так как желательно не использовать стандартный 22-й, а также это позволит при необходимости использовать port forwarding
  • password пароль ssh
  • gw_phone фактической номер телефона
  • maxcount ограничение на количество отправляемых сообщений
  • status статус. 1 активен, 0 заблокирован.
  • gateway_id канал этого модема в Gammu (помните, у нас может быть несколько модемов)
  • state статус шлюза. lock заблокирован.
  • comment свободное поле с комментарием, чтобы просто делать заметки, если нужно

Классов будет всего 5:

  1. Авторизация пользователя Users_Auth.class.php
  2. Работа с PDO MYSQL_PDO.class.php
  3. Обработчик по работе с входящими данными SMS SMS_data_handle.class.php
  4. Работа с Gammu Gammu_SMS.class.php
  5. Возврат http ответов в json http_response.class.php

Дальше я буду объяснять как поток данных будет ходить по API опираясь на базу данных. Мне кажется так нагляднее и понятнее. Также я приведу куски этого кода под спойлерами.

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

Пользователь отправляет запрос с параметрами:

.../smsc.php?login={user_name}&pass={user_password}&tel={phone_number}&msg={message}&flash=1&replacemessages_id=1

Значения flash и replacemessages мы рассматривали в прошлой статье. В {phone_number} можно указать несколько номеров телефонов через ",". + в номере телефона указывать не нужно, но обязательно указывать номер в международном формате (для России это 7). Так же в стоку можно добавить еще один параметр &attempts={число} количество попыток внутри одной отправки, то есть, если можем при отправке вернул ошибку, пытаться ли отправить тут же еще раз?

Вот, что происходит под капотом smsc.php

<?phprequire_once __DIR__.'/functions/config.php';XSS_secure();if (!Users_Auth::do($PDO)) http_response::return(401, ["description" => "User not found or login / password is incorrect"]);$sms_handle = new SMS_data_handle($PDO);$sms_handle->save();function XSS_secure() {     function replace($arr) {        $filter = array("<", ">");        $filter_replace = array("<", ">");         for ($i=0; $i < count($filter) ; $i++) {            $str = str_replace($filter[$i], $filter_replace[$i], $arr);        }    return $str;    }     if ($_GET) $_GET = replace($_GET);    if ($_POST) $_POST = replace($_POST); }?>

Первым делом мы подключаем файл с настройками config.php:

require_once __DIR__.'/functions/config.php';

Содержание файла:

<?php // ini_set('error_reporting', E_ALL);// ini_set('display_errors', 1);// ini_set('display_startup_errors', 1); spl_autoload_register(function ($class_name) {    require_once "classes/{$class_name}.class.php";}); $PDO_param = ['db_host' => '__', // database hostname or ip'db_name' => '__', // database name'db_user' => '__', // database username'db_pass' => '__', // databse user password'db_charset' => 'utf8','pdo_opt' => [        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_LAZY,        PDO::ATTR_EMULATE_PREPARES => false,        ]]; $PDO = new MYSQL_PDO($PDO_param); ?>

делаем небольшую проверку на XSS, а далее проверяем авторизацию, вызывая метод класса Users_Auth::do($PDO):

class Users_Auth{    static function do($PDO)    {      if (!@$_REQUEST['login'] || !@$_REQUEST['pass']) http_response::return(403, ['description' => 'Check your login and password']);     // Проверяем авторизацию      $user = $PDO->query("SELECT id, status FROM users WHERE login= ? AND password= ?", [$_REQUEST['login'], md5(trim($_REQUEST['pass']))]);      if ($user->rowCount() == 0)          return 0;          // http_response::return(401, ["description" => "User not found. Login and password is incorrect"]);      $row = $user->fetch();      if ($row->status != 'active')          return 0;          // http_response::return(401, ["description" => "User status: {$row->status}"]);      return 1;    }}

Если получили false авторизация не удалась, возвращаем код и описание ошибки в json, если необходимо.

Если авторизация успешная вызываем $sms_handle->save(), проверяем переданы ли обязательные параметры телефон и текст сообщения, проверяем в БД статус пользователя, разбираем строку запроса и приводим в нужный нам вид, удаляем пробелы и "+" из номера телефона, а также разделяем их по запятой. Таким образом получаем массив номеров телефонов и текста сообщения, которое нужно на них отправить. Делаем из этого json и сохраняем в таблицу очереди на отправку. Проверка на наличие телефона обязательна. Если попытаться отправлять сообщения без указания номера телефона, возникнет ошибка в Gammu, и шлюз будет занят на несколько секунд. Когда шлюз освободится возникнет аналогичная повторная ситуация, что в свою очередь создаст так называемую пробку и последующие сообщения из очереди просто не смогут уйти.

public function save() {if (empty($_REQUEST['tel'])) http_response::return(400, ["success" => false, "description" => "Phone number is empty"]);if (empty($_REQUEST['msg'])) http_response::return(400, ["success" => false, "description" => "Message is empty"]);$msg = $_REQUEST['msg'];$search = array(' ', '+');$replace = array('', '');$phone_array = explode(",", str_replace($search, $replace, $_REQUEST['tel']));$query = $this->PDO->query("SELECT uuid FROM users WHERE login = ?", [$_REQUEST['login']]);$user_uuid = $query->fetch();$data = [    'message' => $msg,    'flash' => @$_REQUEST['flash'],    'replacemessages_id' => @$_REQUEST['replacemessages_id'],    'attempts' => @$_REQUEST['attempts']];$data['attempts'] = (@$_REQUEST['attempts']) ?: 1;foreach ($phone_array as $phone) {    $data['phone'] = $phone;    $this->PDO->query("INSERT INTO sms_queue SET uuid= ?, data= ?, phone= ?", [$user_uuid->uuid, json_encode($data, JSON_UNESCAPED_UNICODE), $phone]);}http_response::return(200, ["success" => true, "description" => "Saved to queue"]);  }

Дальше мы используем простой скрипт, который поставим в cron и будем вызывать раз в 5-10 секунд (по вкусу) send_queue.php

<?php require_once __DIR__.'/config.php'; $sms_handle = new SMS_data_handle($PDO);$sms_handle->send(); /*add follow lines to cron  crontab -edon't forget to replace <full_path_to> to your really path* * * * * ( php /<full_path_to>/send_queue.php )* * * * * ( sleep 10 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 20 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 30 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 40 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 50 ; php /<full_path_to>/send_queue.php )*/?>

Он будет обращаться к методу класса обработчика сообщений SMS_data_handle->send(). Здесь уже начинается самое интересное.

Мы получаем сообщение за последние 10 минут без тегов статуса. Если нашли такое, ставим на него тег process и берём в работу.

Извлекаем из тела json uuid пользователя, обращаемся к таблице и получаем список активных шлюзов. Идем в таблицу со счётчиком и проверяем, не превышен ли лимит на отправку. Если мы получили активный шлюз и счётчик не превышен, ставим на него тег lock, чтобы никакой другой процесс уже не смог параллельно к нему обратиться. Все вызовы происходит внутри метода send(), но логика раскидана по другим методам класса. По указанному выше описанию работы метода эти обращения легко видны.

Далее мы создаем объект класса $send_proc = new Gammu_SMS($param) с параметрами и обращаемся к методу $send_proc->send($attr) с атрибутами

Весь код метода send():

public function send($с = 1) {     $sended_sms = 0;    for ($i = 0; $i < $с; $i++) {       $sms_queue = $this->get_sms_queue(1);      if (!$sms_queue->rowCount()) http_response::return(200, ["description" => "Nothing to do. Sent. count: {$sended_sms}"]);       $sms_count = $sms_queue->rowCount();      $msg_row = $sms_queue->fetch();       $this->PDO->query("UPDATE sms_queue SET status = ? WHERE id = ?", ["process", $msg_row->id]);       $user_gateway = ($this->get_gateway($msg_row->uuid));       if (!$user_gateway) {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      http_response::return(403, ["description" => "Not active gateways or get limit of message count"]);      }       $this->gateway_lock($user_gateway->id);       $param = [      'host' => $user_gateway->host,      'port' => $user_gateway->port,      'login' => 'root',      'password' => $user_gateway->password,      ];       $sms_data = json_decode($msg_row->data);      $sms_data->message = $sms_data->message;       $attr = [      'phone' => $sms_data->phone,      'message' => $sms_data->message,      'attempts' => $sms_data->attempts,      'flash' => $sms_data->flash,      'replacemessages_id' => $sms_data->replacemessages_id,      'gateway' => $user_gateway->id,      ];      // sleep(5);      $send_proc = new Gammu_SMS($param);      if ($send_proc->send($attr)) {      $this->sms_2archive($msg_row->id, $user_gateway->id);      $this->update_gwcount($user_gateway->gw_phone);      $sended_sms++;       } else {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      }     $this->gateway_release($user_gateway->id);     }     http_response::return(200, ["success" => true, "description" => "Message sent. Count: {$sended_sms}"]);   }

Если объект вернул true, то переносим сообщение в архив и увеличиваем счётчик отправленных сообщений. Иначе снимаем тег proccess и через некоторое время будет повторная попытка отправки по cron.

Особо внимательные заметили, что мы вызываем метод с дефолтным параметром равным одному send($с = 1). Параметр $c заложен на перспективу и позволяет нам, в случае необходимости получать пачку сообщений из базы данных и обрабатывать их отправку в цикле. Для этого в файле, вызываемом в cron нужно в вызове метода указать число сообщений для выборки их БД $sms_handle->send({число});

Обратим внимание еще на один момент. В файле smsc.php есть возможность отправлять сообщения сразу после того, как оно было добавлено в БД. Для этого нужно раскомментировать следующую строку:

// $sms_handle->send();

Это позволит нам отказаться от cron, но есть один нюанс желательно использовать этот метод, если вы отправляете сообщения только на один номер и запросы к шлюзу не могут быть чаще чем раз в 30 секунд. Иначе возможны ошибки связанные с наложением запросов и если шлюз занят, то сообщение не отправится.

Теперь наш шлюз работает через API и умеет отправлять сообщения.

Ну и бонусом мы сделаем простую форму для отправки сообщений с сайта. Ее код не нуждается в пояснении, она просто принимает от вас тест и отправляет POST-запрос на указанный нами скрипт. Единственное в блоке отправки ajax нужно заменить url: "/<*.php>" на адрес вашего скрипта smsc.php

Итак подведем итоги проделанной работы. Мы создали аппаратную платформу, научились отправлять сообщения через терминал и расширили возможности системы собственным API для легкого доступа к шлюзу устройств способных отправлять GET/POST-запросы. Хранить историю и балансировать нагрузку между картами и прочее. Все это сильно упрощает работы со шлюзом и позволяет хранить все в одном сервисе.
Внимание, я не претендую на великолепную красоту кода и буду рад любой объективной критике для понимания своих ошибок (в случае наличия) и совершенствования навыков.
Репозиторий данного проекта на Github

Подробнее..

Оптимизация расходов на СМС

13.08.2020 20:11:58 | Автор: admin
Андрей Бышенко, руководитель направления ИТ-дирекции МКБ



Коллеги, привет!
Как и любой банк, мы информируем наших клиентов частных лиц с помощью СМС и тратим на это довольно много денег. В прошлом году мы провели внимательный осмотр СМС-коммуникаций с целью сокращения расходов.
Мы работаем, как и многие участники этого рынка, и напрямую с операторами, и с СМС-агрегаторами, в зависимости от того, где стоимость СМС дешевле.
Идею перевести 85% сообщений в push-сообщения мы не стали принимать, поскольку не поверили в ее реалистичность: стандарт СМС простой и уходит корнями в 1984 год, в то время как push-сообщения предполагают значительную вариативность и активно развиваются только последние 10 лет. Кроме того, структура нашей клиентской базы была такова, что бОльшая часть использовала кнопочные телефоны, без возможности push =)

Начали мы с поиска слонов, которых можно было бы безболезненно отстрелить, и такой нашелся. СМС с текстом Вы входите в мобильный банк дата-время не несла в себе особого смысла, поскольку отправлялась на то же устройство, с которого производился вход в этот момент. Также ее положение отягчала стоимость за штуку: она относилась не к самым дешевым транзакционным (изменение баланса), но к сервисным, которые в среднем в два раза дороже. Мы оставили это уведомление через СМС только в одном случае: когда клиент входит в мобильный банк с нового телефона. По итогам 2019 года, отказавшись от этого сообщения, мы получили 45% сокращений расходов на СМС.
Далее мы обратились к поиску дубликатов сообщений на одну операцию, такие возникали в случае рассинхрона систем-инициаторов. Например, при внесении денег на карту через терминал МКБ клиент получал два сообщения: одно от карточного процессинга, второе от системы, управляющей терминалами.
На следующем шаге мы обратили внимание на механизм оплаты. Счет выставляется за количество отправленных сегментов. Это означало, что надо посмотреть на количество сегментов и на стоимость доставки.
Длину сегментов наверняка все знают, википедия наглядно задает их таблицей:



В части сегментов, атомарной единицы СМС, наша работа разделилась на несколько направлений.
Первое привести самые массовые транзакционные СМС об изменении баланса к одному сегменту. С этим мы справились, исключив из текста лишнюю информацию, типа даты совершения операции, а еще заменив в сумме валюту: руб на р.
Не очень популярные и не очень важные (но обязательные) клиенту сообщения мы оставили в транслите, чтобы поместить больше текста в один сегмент. При этом то же самое сообщение, но отправляемое через push в нашем мобильном приложении переведено на русский язык. Во-вторых, мы думали, как для пожилых клиентов игнорировать длину сегмента, и все сообщения посылать кириллицей, чтобы бабушки и дедушки все с ходу поняли, но эту доработку еще не реализовали.
Третьим направлением стала работа с заказчиками с просьбой пересмотреть тексты и формулировки для сокращения. Это была хорошая гигиеническая задача, но, честно сказать, супермасштабного эффекта она не оказала.
В процессе мы столкнулись с интересными моментами. Например, расходы на транзакционные СМС абонентам Билайн после приведения к одному сегменту почему-то не сократились. Поковыряв биллинг, мы поняли, что каждое сообщение по-прежнему состоит из двух сегментов. Подивившись, мы пошли смотреть на доставленные сообщения и обнаружили среди транслита слово баланс кириллицей, вставленное то ли шаблоном, то ли обработчиком Билайна. Это нам была наука, что надо досконально проверять результат на соответствие ожиданиям.
Выше я упоминал о том, что оплачиваются именно отправленные сообщения, соответственно, имело смысл посмотреть на степень доставки сообщений. Здесь же мы решили обогатить состав номеров, на которые делается отправка. Если ранее мы не отсылали на городские и 8-800 номера, то сейчас стараемся не посылать сообщения на номера, на которые, к примеру, рекламные СМС не доставлялись более восьми раз. При этом если СМС не доставлялись, пока наш клиент находился в другой стране, номер не считается неактивным.
Параллельно с этими активностями велась напряженная совместная работа с агрегатором с тем, чтобы информационные сообщения считались операторами именно как сервисные, а не рекламные. Это направление работы внесло значимый вклад в сокращение расходов, поскольку рекламное сообщение вдвое дороже сервисного.
Я хотел бы сказать, что это все не случилось бы без нашего бывшего коллеги Алексея, который помогал нам мудрым советом. Без нашего коллеги Ивана, который реализовал все идеи и сгенерил десятки новых. А также без наших коллег в агрегаторе, которые активно помогали нам на протяжении всего времени. Этот проект позволил нам без увеличения бюджета продолжать рассылать сообщения до конца года, при этом количество коммуникаций на одного клиента выросло почти в полтора раза.
В этом году, когда у нас появилось время после околоковидных активностей, связанных с переходом на удаленку, мы, понимая, что еще есть над чем поработать, планируем сформировать регулярную отчетность для бизнес-заказчиков, посмотреть в сторону валидации эффекта от рассылок СМС, проработать механизм регулярных инвентаризаций шаблонов, текстов и правил. Если получится интересно, мы обязательно поделимся.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru