17.09.2019

Кастомная служба отправки SMS в Bitrix

Не так давно в Bitrix появилась возможность отправлять SMS из "коробки". Ее работу обеспечивает новый модуль Служба сообщений (messageservice). В списке модулей он описывается так:

Модуль осуществляет отправку текстовых и иных сообщений

Для отправки SMS используется \Bitrix\Main\Sms\Event. Пример

$sms = new \Bitrix\Main\Sms\Event(
	"SMS_USER_CONFIRM_NUMBER",
	[
		"USER_PHONE" => $phoneNumber,
		"CODE" => $code,
	]
);
$smsResult = $sms->send(true);

  • Первый параметр конструктора это Тип события, который создается так же как и почтовые типы событий тут Настройки > Настройки продукта > Почтовые и СМС события > Типы событий
  • Второй параметр конструктора это массив полей, которые будут переданы в шаблон в качестве макроподстановок.

По умолчанию модуль messageservice предоставляет ряд уже готовых Службы отправки SMS: SMS.RU, SMS-ассистент, Twilio.com. Мне понадобилось на одном из проектов сделать отправку SMS через существующий аккаунт SMS.RU. И тут возникли проблемы. После заполнения всех полей формы, на соответсвующей странице настроек модуля, в SMS.RU почему то зарегистрировался новый аккаунт. После повторного ввода данных, не приходили SMS с подтверждением. После копания в недрах модуля messageservice нашел, что для работы с SMS.RU используется не документрированное API, о котором ТП SMS.RU ни чего не может сказать.

В итоге оказалось проще написать собственную реализацию Службы отправки SMS.RU для модуля messageservice. И вот что у меня получилось:

<?php

namespace Gricuk\MessageService;

use Bitrix\Main\Application;
use Bitrix\Main\Error;
use Bitrix\Main\Result;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
use Bitrix\Main\Loader;

use Bitrix\MessageService\Sender\Result\MessageStatus;
use Bitrix\MessageService\Sender\Result\SendMessage;

use Bitrix\MessageService;

class SmsRu extends \Bitrix\MessageService\Sender\Base
{
    const API_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    public static function isSupported()
    {
        return true;
    }

    public function getId()
    {
        return 'customsmsru';
    }

    public function getName()
    {
        return "Custom Sms.ru";
    }

    public function getShortName()
    {
        return 'sms.ru';
    }

    public function isDemo()
    {
        return false;
    }

    public function canUse()
    {
        return true;
    }


    public function sendMessage(array $messageFields)
    {
        if (!$this->canUse()) {
            $result = new SendMessage();
            $result->addError(new Error(self::getMessage('MESSAGESERVICE_SENDER_SMS_SMSRU_CAN_USE_ERROR')));
            return $result;
        }

        $params = array(
            'to' => $messageFields['MESSAGE_TO'],
            'msg' => $messageFields['MESSAGE_BODY']
        );

        $result = new SendMessage();
        $apiResult = $this->callExternalMethod('sms/send', $params);
        $resultData = $apiResult->getData();

        if (!$apiResult->isSuccess()) {
            if ((int)$resultData['status_code'] == 206) {
                $result->setStatus(MessageService\MessageStatus::DEFERRED);
                $result->addError(new Error($this->getErrorMessage($resultData['status_code'])));
            } else {
                $result->addErrors($apiResult->getErrors());
            }
        } else {
            $smsData = current($resultData['sms']);

            if (isset($smsData['sms_id'])) {
                $result->setExternalId($smsData['sms_id']);
            }

            if ((int)$smsData['status_code'] !== 100) {
                $result->addError(new Error($this->getErrorMessage($smsData['status_code'])));
            } elseif ((int)$smsData['status_code'] == 206) {
                $result->setStatus(MessageService\MessageStatus::DEFERRED);
                $result->addError(new Error($this->getErrorMessage($smsData['status_code'])));
            } else {
                $result->setAccepted();
            }
        }

        return $result;
    }

    public function getMessageStatus(array $messageFields)
    {
        $result = new MessageStatus();
        $result->setId($messageFields['ID']);
        $result->setExternalId($messageFields['EXTERNAL_ID']);

        if (!$this->canUse()) {
            $result->addError(new Error(self::getMessage('MESSAGESERVICE_SENDER_SMS_SMSRU_CAN_USE_ERROR')));
            return $result;
        }

        $params = array(
            'sms_id' => $result->getExternalId()
        );

        $apiResult = $this->callExternalMethod('sms/status', $params);
        if (!$apiResult->isSuccess()) {
            $result->addErrors($apiResult->getErrors());
        } else {
            $resultData = $apiResult->getData();
            $smsData = current($resultData['sms']);

            $result->setStatusCode($smsData['status_code']);
            $result->setStatusText($smsData['status_text']);

            if ((int)$resultData['status_code'] !== 100) {
                $result->addError(new Error($this->getErrorMessage($smsData['status_code'])));
            }
        }

        return $result;
    }

    public static function resolveStatus($serviceStatus)
    {
        $status = parent::resolveStatus($serviceStatus);

        switch ((int)$serviceStatus) {
            case 100:
                return MessageService\MessageStatus::ACCEPTED;
                break;
            case 101:
                return MessageService\MessageStatus::SENDING;
                break;
            case 102:
                return MessageService\MessageStatus::SENT;
                break;
            case 103:
                return MessageService\MessageStatus::DELIVERED;
                break;
            case 104: //timeout
            case 105: //removed by moderator
            case 106: //error on receiver`s side
            case 107: //unknown reason
            case 108: //rejected
                return MessageService\MessageStatus::UNDELIVERED;
                break;
            case 110:
                return MessageService\MessageStatus::READ;
                break;
        }

        return $status;
    }

    private function callExternalMethod($method, $params = [])
    {
        $url = 'https://sms.ru/' . $method;
        $params["api_id"] = self::API_ID;

        $httpClient = new HttpClient(array(
            "socketTimeout" => 10,
            "streamTimeout" => 30,
            "waitResponse" => true,
        ));
        $httpClient->setHeader('User-Agent', 'Bitrix24');
        $httpClient->setCharset('UTF-8');

        $isUtf = Application::getInstance()->isUtfMode();

        if (!$isUtf) {
            $params = \Bitrix\Main\Text\Encoding::convertEncoding($params, SITE_CHARSET, 'UTF-8');
        }
        $params['json'] = 1;

        $result = new Result();
        $answer = array();

        if ($httpClient->query(HttpClient::HTTP_POST, $url, $params) && $httpClient->getStatus() == '200') {
            $answer = $this->parseExternalAnswer($httpClient->getResult());
        }

        $answerCode = isset($answer['status_code']) ? (int)$answer['status_code'] : 0;

        if ($answerCode !== 100) {
            $result->addError(new Error($this->getErrorMessage($answerCode, $answer)));
        }
        $result->setData($answer);

        return $result;
    }

    private function parseExternalAnswer($httpResult)
    {
        try {
            $answer = Json::decode($httpResult);
        } catch (\Bitrix\Main\ArgumentException $e) {
            $data = explode(PHP_EOL, $httpResult);
            $code = (int)array_shift($data);
            $answer = $data;
            $answer['status_code'] = $code;
            $answer['status'] = $code === 100 ? 'OK' : 'ERROR';
        }

        if (!is_array($answer) && is_numeric($answer)) {
            $answer = array(
                'status' => $answer === 100 ? 'OK' : 'ERROR',
                'status_code' => $answer
            );
        }

        return $answer;
    }

    private function getErrorMessage($errorCode, $answer = null)
    {
        $message = self::getMessage('MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_' . $errorCode);
        if (!$message && $answer && !empty($answer['errors'])) {
            $errorCode = $answer['errors'][0]['status_code'];
            $message = self::getMessage('MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_' . $errorCode);
            if (!$message) {
                $message = $answer['errors'][0]['status_text'];
            }
        }

        return $message ?: self::getMessage('MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_OTHER');
    }


    public static function getMessage($code)
    {
        $MESS = [];
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_NAME"] = "Компания SMS.RU";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_CAN_USE_ERROR"] = "Провайдер компании SMS.RU не настроен";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_201"] = "Не хватает средств на лицевом счету";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_202"] = "Неправильно указан получатель";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_203"] = "Нет текста сообщения";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_204"] = "Имя отправителя не согласовано с администрацией";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_205"] = "Сообщение слишком длинное (превышает 8 СМС)";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_206"] = "Будет превышен или уже превышен дневной лимит на отправку сообщений";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_207"] = "На этот номер (или один из номеров) нельзя отправлять сообщения, либо указано более 100 номеров в списке получателей";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_209"] = "Вы добавили этот номер (или один из номеров) в стоп-лист";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_230"] = "Превышен общий лимит количества сообщений на этот номер в день";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_231"] = "Превышен лимит одинаковых сообщений на этот номер в минуту";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_232"] = "Превышен лимит одинаковых сообщений на этот номер в день";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_301"] = "Неправильный пароль, либо пользователь не найден";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_302"] = "Аккаунт не подтвержден (пользователь не ввел код, присланный в регистрационной смс)";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_303"] = "Код подтверждения неверен";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_304"] = "Отправлено слишком много кодов подтверждения. Пожалуйста, повторите запрос позднее";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_305"] = "Слишком много неверных вводов кода, повторите попытку позднее";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_703"] = "Опечатка в номере мобильного телефона или номер из неподдерживаемой страны";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_900"] = "Код подтверждения указан неверно";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_901"] = "Неверно указан номер телефона, ошибка в формате";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_903"] = "В данный момент регистрация номера с таким кодом недоступна";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_905"] = "Пользователь не указал имя (или оно меньше 2х символов)";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_906"] = "Пользователь не указал фамилию (или оно меньше 2х символов)";
        $MESS["MESSAGESERVICE_SENDER_SMS_SMSRU_ERROR_OTHER"] = "Системная ошибка. Необходимо повторить запрос еще раз";


        return $MESS[$code];
    }


    public static function onGetSmsSenders()
    {
        $class = __CLASS__;
        return [new $class()];
    }

    public function getFromList()
    {
        $result = $this->callExternalMethod('my/senders');

        if ($result->isSuccess()) {
            $from = array();
            $resultData = $result->getData();
            foreach ($resultData['senders'] as $sender) {
                if (!empty($sender)) {
                    $from[] = array(
                        'id' => $sender,
                        'name' => $sender
                    );
                }
            }

            return $from;
        }
        return [];
    }
}
?>

А вот так выполняется подключение данной Службы отправки SMS

<?php
	
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
    "messageservice",
    "onGetSmsSenders",
    array(
        Gricuk\MessageService\SmsRu::class,
        "onGetSmsSenders",
    )
);
?>

Как видно для кастомной службы отправки SMS.RU необходимо написать класс, наследованный от \Bitrix\MessageService\Sender\Base. Так же существует класс \Bitrix\MessageService\Sender\BaseConfigurable. Он является наследником \Bitrix\MessageService\Sender\Base и отличается от него тем, что позволяет вывести в настройки модуля messageservice форму с параметрами службы.


hentaigox hentai-fan.com futanari! oshioki time 5
www video sxs videoxlist.mobi nayanathara
its showtime feb 2 2023 pinoyshowstv.com abot kamay na pangarap dec 21 2022
www xxnx vidos com indianhottube.com charas hindi film
صور سكسمتحركة geficktporno.com سكس الام الجميلة
suhagratsex brunetteporntrends.com www porn mobi com
مدرس ينيك طالبه meeporn.net سكس تكساس
telugu sex aunty hornyanaltube.net nazriya fahad
hentai makai hentai-pics.net merman hentai
tamil sex video free download erovoyeurism.info gita rabari
outdoor xvideo ultraporn.mobi telugu sex videos.com
local sex xxx tubetria.mobi xxxvdeocom
mature xnxx indianfuck.org xxx film sexy
tv patrol april 11 2023 teleseryehot.com kmjs september 18 2022
موقع برزرز porn-dumps.com صور زب اسمر