Опубликовано 6 комментариев

WooCommerce и Телеграм. Часть 2.

Итак продолжим писать наш плагин для WooCommerce и Телеграм. В первой части мы написали каркас плагина, зарегистрировали его в списке плагинов и добавили в интеграции WooCommerce.

Создаем бота в Telegram.

Теперь давайте создадим нашего бота. Для этого необходимо в Telegram найти отца всех ботов @BotFather и дать ему команду /newbot.

Переписка с BotFather
Переписка с BotFather

Добавляем поля в наш плагин.

Примерно в таком ключе пройдет Ваше общение с @BotFather в результате которого Вы получите token. Теперь этот токен необходимо сохранить в нашем плагине. Этим токеном мы будем подписывать наши сообщения боту. Давайте добавим наше первое поле в настройку плагина. После добавления наш класс Telegram в файле includes/telegram.php будет выглядеть так :

    public function __construct()
    {
        $this->id = "woo-telegram";
        $this->method_title = "Телеграм бот для WooCommerce";
        $this->method_description = "Плагин связывает WooCommerce c Telegram ботом.";
        $this->init_form_fields();
        $this->init_form_fields();
        $this->token = $this->settings['token'];
        add_action( "woocommerce_update_options_integration_" . $this->id, array( $this, "process_admin_options" ) );
    }

    public function init_form_fields(){
        $this->form_fields = [
            "token" => array(
                "title"       => "Токен Telegram",
                "description" => "Введите token полученный от BotFather",
                "type"        => "text",
                "desc_tip"    => true,
                "default"     => get_option( "token" )
            ),
        ];
    }

Мы добавили новый метод init_form_fileds в котором описали настройку необходимых нам полей , название, описание, тип, также добавили опцию по умолчанию . После того как мы сохраним наши настройки то заново зайдем на эту страницу и в это поле автоматически подставится сохраненное значение. Дальше мы определяем переменную token нашего класса Telegram и заполняем ее значением настройки ‘token’. После этого добавляем action «process_admin_options» который и сохранит все наши настройки в БД WooCommerce.

Теперь в Настройках WooCommerce у нас появилось новое поле Token которое мы можем заполнить и сохранить.

Токен для Telegram
Токен для Telegram

Пишем код плагина для связи с Telegram.

Давайте подумаем, что мы хотим получить от нашего бота ? Самое простое это получать уведомление когда кто-то из клиентов совершает заказ. Это и будет наше минимальное ТЗ.

Но сначала давайте подружим WooCommerce и Телеграм. Все общение с Телеграм может происходить двумя способами. 1) Мы сами запрашиваем обновления. 2) Телеграм присылает нам уведомления на указанный нами адрес. Мы выберем способ номер 2). Для этого у нашего сайта должен быть установлен SSL сертификат. Чтобы наш бот мог отправлять нам сообщения, нам надо получить ИД чата между телеграм ботом и нашим персональным telegram. Для этого мы будем отсылать нашему боту в телеграме команду вроде /key=ИД_юзера_на_сайте&auth=ХЭШ_для_защиты. Наш сайт будет принимать от бота ИД чата и прописывать его нужному юзеру.

Добавим в конструктор __construct() нашего класса вызов action, добавим сам метод WooTelegramResponse, пару вспомогательных методов и так же напишем метод отправки сообщений в Telegram.

add_action('woocommerce_api_woo-telegram',[$this,'WooTelegramResponse']);

public function WooTelegramResponse()
{
    global $woocommerce;
    $data   = file_get_contents("php://input");
    $logger = wc_get_logger();
    try {
        $result = $this->decodePost($data);
        $userId = $this->parseText($result['text']);
        if (update_user_meta($userId, 'telegram', $result['chatId'])) {
            $text = 'Добро пожаловать в WooCommerce.' . PHP_EOL;
            $text .= 'На странице плагина Вы должны увидеть номер Вашего чата ' . $result['chatId'] . PHP_EOL;
            $text .= 'Спасибо !';
            $this->sendMessageToTelegram($text, $result['chatId'], $this->token);
        } else {
            if ($chatId = get_user_meta($userId, 'telegram', true)) {
                $text = 'Вы уже зарегистрированы в WooCommerce.' . PHP_EOL;
                $text .= 'Спасибо за то, что Вы с нами';
                $this->sendMessageToTelegram($text, $chatId, $this->token);
            }
        }
    } catch (Exception $e) {
        $logger->info(wc_print_r($e->getMessage(), true));
    }
}

private function parseText(string $text)
{
    $input = [];
    parse_str($text, $input);
    $userId = empty($input['key']) ? false : $input['key'];
    $hash   = empty($input['auth']) ? false : $input['auth'];
    if ($userId && $hash && $hash == md5('telegram2019' . $userId)){
        return $userId;
    }
    throw new Exception('Не найден пользователь или не совпал секрет !');
}

private function decodePost(string $post): array
{
    $data   = json_decode($post, true);
    $text   = empty($data['message']['text']) ? false : $data['message']['text'];
    $text   = substr($text, 1);
    $chatId = empty($data['message']['chat']['id']) ? false : $data['message']['chat']['id'];
    if ($text && $chatId) {
        return [
          'text'   => $text,
          'chatId' => $chatId,
        ];
    }
    throw new Exception('Не хватает аргументов text или chatId');
}

public function sendMessageToTelegram(
  string $text,
  string $chatId,
  string $token
): void {
    $url      = 'https://api.telegram.org/bot' . $token . '/sendMessage';
    $args     = [
      'timeout'     => 5,
      'blocking'    => true,
      'headers'     => ['Content-Type' => 'application/x-www-form-urlencoded'],
      'body'        => ['text' => $text, 'chat_id' => $chatId],
    ];
    $response = wp_remote_post($url, $args);
    $logger   = wc_get_logger();
    if (is_wp_error($response)) {
        $error_message = $response->get_error_message();
        $logger->info(wc_print_r($error_message, true));
    }
}

Добавив action woocommerce_api_woo-telegram мы тем самым задекларировали, что у нас по адресу https://наш_сайт.ru/?wc-api=woo-telegram будет находится наш обработчик WooTelegramResponse. Смысл этого обработчика в том, что он принимает данные с Telegram декодирует их, обрабатывает и записывает нужному юзеру ИД чата с Телеграм.

Добавляем необходимую информацию на страницу плагина.

Итак у нас готов обработчик, теперь надо сообщить Telegram куда он должен отсылать всю ту информацию которую он получит в чате. Но перед этим сделаем проверку на правильность введенного Токена. Переопределим встроенный метод validate_text_field и используем его для проверки нашего токена.

public function validate_text_field($key, $value)
{
    if ($key == 'token') {
        if (!$this->checkToken($value)) {
            $this->add_error('Токен не существует');
        }
    }
    return parent::validate_text_field($key, $value);
}

Здесь мы смотрим на полученный ключ сравниваем его с тем что мы задали в методе init_form_fields и запускаем проверку с помощью метода checkToken, если метод вернет false то добавим ошибку в список ошибок и дальше отдадим управление родительскому методу. Опишем метод checkToken :

private function checkToken(string $token)
{
    $url      = 'https://api.telegram.org/bot' . $token . '/getMe';
    $response = wp_remote_get($url);
    $body     = wp_remote_retrieve_body($response);
    if (!empty($body)) {
        try {
            $data = json_decode($body, true);
            if (!empty($data['result']['username'])) {
                return true;
            }
        } catch (Exception $e) {
        }
    }
    return false;
}

В Telegram есть простой метод getMe который возвращает данные бота или 404 ошибку если бота с таким токеном не существует. Если мы получаем от Телеграм username в данных, то считаем что проверка прошла и возвращаем true, во всех остальных случаях возвращаем false.

Теперь у нас есть валидация Token напишем метод setTelegramWebhook который указывает Telegram куда отсылать webhook.

private function setTelegramWebhook()
{
    $logger = wc_get_logger();
    $url    = 'https://api.telegram.org/bot' . $this->token . '/setWebhook';
    $logger->info(wc_print_r($url, true));
    $args     = [
      'timeout'     => 5,
      'redirection' => 1,
      'httpversion' => '1.0',
      'blocking'    => true,
      'headers'     => ['Content-Type' => 'application/x-www-form-urlencoded'],
      'body'        => ['url' => home_url('/?wc-api=woo-telegram')],
    ];
    $response = wp_remote_post($url, $args);
    if (is_wp_error($response)) {
        $error_message = $response->get_error_message();
        $logger->info(wc_print_r($error_message, true));
    }
}

Здесь мы подключаем встроенный в WooCommerce логер, чтобы фиксировать ошибки в логах. Телеграм метод setWebhook принимает несколько аргументов, но нам нужен только обязательный url в который мы прописываем наш адрес.

Проведем еще немного рефакторинга и добавим проверок в результате которых наш код примет примерно такой вид.

<?php

if (!defined('ABSPATH')) {
    exit;
}

class Telegram extends WC_Integration
{

    const API_TELEGRAM = 'https://api.telegram.org/bot';

    public $registerWebhook;

    public $token;

    public $chatId;

    public $userId;

    public function __construct()
    {
        $current_user = wp_get_current_user();
        $this->userId = $current_user->ID;

        $this->id                 = "woo-telegram";
        $this->method_title       = "Телеграм бот для WooCommerce";
        $this->method_description = "Плагин связывает WooCommerce c Telegram ботом.";
        $this->init_form_fields();
        $this->init_settings();

        $this->token  = $this->settings['token'];
        $this->chatId = get_user_meta($this->userId, 'telegram', true);
        $this->registerWebhook = $this->checkExistsWebhook($this->token);
        add_action('woocommerce_api_woo-telegram',
          [$this, 'WooTelegramResponse']);
        add_action("woocommerce_update_options_integration_" . $this->id,
          [$this, "process_admin_options"]);
    }

    public function init_form_fields()
    {
        $this->form_fields = [
          "token" => [
            "title"       => "Токен Telegram",
            "description" => "Введите token полученный от BotFather",
            "type"        => "text",
            "class"       => "tm-token",
            "desc_tip"    => true,
            "default"     => get_option("token"),
          ],
        ];
    }

    public function process_admin_options()
    {
        $result      = parent::process_admin_options();
        $this->token = $this->settings['token'];
        $this->setTelegramWebhook();
        $this->registerWebhook = $this->checkExistsWebhook($this->token);
        return $result;
    }

    function admin_options()
    {
        $hash = md5('telegram2019' . $this->userId);

        echo '<table class="form-table">';
        echo $this->generate_settings_html($this->form_fields, false);
        echo '</table>';
        if (!$this->registerWebhook) {
            echo '<a href="#" class="button-secondary" id="webhook">WebHook не зарегистрирован</a>';
        }
        if (empty($this->chatId)) {
            echo "<p> Найдите нашего бота @woo_telegram_bot нажмите Начать и напишите ему команду /key=$this->userId&auth=$hash долждитесь ответа бота и перегрузите эту страницу </p>";
        } else {
            echo "<p>Номер telegram чата : $this->chatId </p>";
        }
        $this->display_errors();
    }

    public function validate_text_field($key, $value)
    {
        if ($key == 'token') {
            if (!$this->checkToken($value)) {
                $this->add_error('Токен не существует');
            }
        }
        return parent::validate_text_field($key, $value);
    }

    private function setTelegramWebhook()
    {
        $logger = wc_get_logger();
        $url    = self::API_TELEGRAM . $this->token . '/setWebhook';
        $logger->info(wc_print_r($url, true));
        $args     = [
          'timeout'     => 5,
          'redirection' => 1,
          'httpversion' => '1.0',
          'blocking'    => true,
          'headers'     => ['Content-Type' => 'application/x-www-form-urlencoded'],
          'body'        => ['url' => home_url('/?wc-api=woo-telegram')],
        ];
        $response = wp_remote_post($url, $args);
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            $logger->info(wc_print_r($error_message, true));
        }
    }

    private function checkExistsWebhook(string $token)
    {
        $logger = wc_get_logger();
        $url    = self::API_TELEGRAM . $token . '/getWebhookInfo';
        $logger->info(wc_print_r($url, true));
        $response = wp_remote_get($url);
        $body     = wp_remote_retrieve_body($response);
        if (!empty($body)) {
            try {
                $data = json_decode($body, true);
                if (!empty($data['result']['url'])) {
                    return true;
                }
            } catch (Exception $e) {
            }
        }
        return false;
    }

    private function checkToken(string $token)
    {
        $url      = self::API_TELEGRAM . $token . '/getMe';
        $response = wp_remote_get($url);
        $body     = wp_remote_retrieve_body($response);
        if (!empty($body)) {
            try {
                $data = json_decode($body, true);
                if (!empty($data['result']['username'])) {
                    return true;
                }
            } catch (Exception $e) {
            }
        }
        return false;
    }

    public function WooTelegramResponse()
    {
        global $woocommerce;
        $data   = file_get_contents("php://input");
        $logger = wc_get_logger();
        try {
            $result = $this->decodePost($data);
            $userId = $this->parseText($result['text']);
            if (update_user_meta($userId, 'telegram', $result['chatId'])) {
                $eol  = PHP_EOL;
                $text = 'Добро пожаловать в WooCommerce.' . $eol;
                $text .= 'На странице плагина Вы должны увидеть номер Вашего чата ' . $result['chatId'] . $eol;
                $text .= 'Спасибо !';
                $this->sendMessageToTelegram($text, $result['chatId'],
                  $this->token);
            } else {
                if ($chatId = get_user_meta($userId, 'telegram', true)) {
                    $text = 'Вы уже зарегистрированы в WooCommerce.' . PHP_EOL;
                    $text .= 'Спасибо за то, что Вы с нами';
                    $this->sendMessageToTelegram($text, $chatId, $this->token);
                }
            }
        } catch (Exception $e) {
            $logger->info(wc_print_r($e->getMessage(), true));
        }
    }

    /**
     * @param string $text
     *
     * @return bool|mixed
     * @throws \Exception
     */
    private function parseText(string $text)
    {
        $input = [];
        parse_str($text, $input);
        $userId = empty($input['key']) ? false : $input['key'];
        $hash   = empty($input['auth']) ? false : $input['auth'];
        if ($userId && $hash && $hash == md5('telegram2019' . $userId)) {
            return $userId;
        }
        throw new Exception('Не найден пользователь или не совпал секрет !');
    }

    /**
     * @param string $post
     *
     * @return array
     * @throws \Exception
     */
    private function decodePost(string $post): array
    {
        $data   = json_decode($post, true);
        $text   = empty($data['message']['text']) ? false : $data['message']['text'];
        $text   = substr($text, 1);
        $chatId = empty($data['message']['chat']['id']) ? false : $data['message']['chat']['id'];
        if ($text && $chatId) {
            return [
              'text'   => $text,
              'chatId' => $chatId,
            ];
        }
        throw new Exception('Не хватает аргументов text или chatId');
    }


    public function sendMessageToTelegram(
      string $text,
      string $chatId,
      string $token
    ): void {
        $url      = self::API_TELEGRAM . $token . '/sendMessage';
        $args     = [
          'timeout'     => 5,
          'redirection' => 1,
          'httpversion' => '1.0',
          'blocking'    => true,
          'headers'     => ['Content-Type' => 'application/x-www-form-urlencoded'],
          'body'        => ['text' => $text, 'chat_id' => $chatId],
        ];
        $response = wp_remote_post($url, $args);
        $logger   = wc_get_logger();
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            $logger->info(wc_print_r($error_message, true));
        }
    }

}

В итоге сейчас любой Админ или Менеджер магазина , может зайти на страницу плагина получить свой код регистрации в нашем телеграм боте. В следующей части интеграции WooCommerce и Телеграм мы напишем с Вами реализацию отправки заказа в нашему телеграм боту для отправки всем зарегистрированным пользователям.

6 комментариев к “WooCommerce и Телеграм. Часть 2.

  1. Здравстуйте ! Подскажите , откуда брать id $this->chatId = get_user_meta($this->userId, ‘telegram’, true); для этой строки ?

    1. ни откуда не надо брать.
      в методе public function WooTelegramResponse()
      мы записываем update_user_meta($userId, ‘telegram’, $result[‘chatId’]) нужную нам информацию в БД

  2. Это все огонь. А где скачать можно уже готовый к работе плагин. 😇

    1. К сожалению готового нету )))

    2. Добавил готовый код плагина во все части описания.

  3. […] писать Telegram bot для Woocommerce. В первой и второй части мы сделали каркас плагина, зарегистрировали […]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *