Создание Telegram бота на PHP #5: работа с хуками

от автора

В новом уроке мы с вами поговорим о настройке хуков и напишем свой первый обработчик команд.

В первом уроке я вам рассказывал что такое хуки, давайте повторим:

Hooks (Хуки) — это способ общения с программой, по средствам отправки данных от сервера — клиенту. То есть при определённых изменениях в программе, сервер (приложение) будет отправлять данные на указанный URL скрипта клиента.

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

Полный список всех записей курса находится на сайте https://prog-time.ru/course_cat/telegram-bot-basic/ или в публикациях на Хабр https://habr.com/ru/users/Prog-Time/posts/

Для регистрации хука нужно выполнить 2 правила:

  • разместить скрипт на виртуальный сервер (хостинг)

  • домен на который будут отправляться запросы, должен иметь SSL сертификат и работать через HTTPS соединение

Если ваш хостинг соответствует данным требованиям, то давайте займёмся регистрацией хука для Telegram бота.

Регистрация хука для Telegram бота

Для регистрации хука нам нужно отправить запрос с методом setWebhook(), которому в качестве параметра url мы должны передать ссылку на скрипт обработчик. В моём случае это просто php скрипт.

Вот пример запроса:

$token = "5340791844:AAEXXDduvInvQrlykV91USOQSevrPVU";  $getQuery = array(      "url" => "https://prog-time.ru/tg_script/index.php", ); $ch = curl_init("https://api.telegram.org/bot". $token ."/setWebhook?" . http_build_query($getQuery)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, false);  $resultQuery = curl_exec($ch); curl_close($ch);  echo $resultQuery;

Если запрос прошёл успешно, то вы получите следующий ответ:

{   "ok": true,   "result": true,   "description": "Webhook was set" }

Теперь давайте проверим работу нашего обработчика. Сообщения приходят POST-запросом, с типом application/json. Получить его в PHP можно следующим образом:

$data = file_get_contents('php://input'); $data = json_decode($data, true);

Разбор параметров передаваемых через Hooks

Давайте теперь посмотрим что приходит на наш скрипт при отправке простого текстового сообщения боту.

Здесь есть небольшая проблема! Скрипты будут выполняться в рандомный момент и если мы не запишем данные, то они пропадут в пустоту. Для записи ответа вы можете использовать БД или как я, просто записать массив в файл txt.

Для записи строки я буду использовать дополнительную, самописную функцию writeLogFile()

Моя функция принимает 2 параметра:

  • первый параметр, строка для записи. В нашем случае это JSON строка.

  • второй параметр используется для очистки файла и перезаписи. Если данный параметр имеет значение false, то в файл дописывается информация.

function writeLogFile($string, $clear = false){     $log_file_name = __DIR__."/message.txt";     if($clear == false) { $now = date("Y-m-d H:i:s"); file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);     }     else { file_put_contents($log_file_name, '');         file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);     } }

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

function writeLogFile($string, $clear = false){     $log_file_name = __DIR__."/message.txt";     if($clear == false) { $now = date("Y-m-d H:i:s"); file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);     }     else { file_put_contents($log_file_name, '');         file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);     } }  $data = file_get_contents('php://input'); writeLogFile($data, true);

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

Теперь выведем полученную информацию на страницу

echo file_get_contents(__DIR__."/message.txt");

Вот что мы получаем. Это объект в котором записана информация о созданном сообщение, мы видим данные о пользователе, данные о чате, дата отправления и текст сообщения.

{   "update_id": 803290892,   "message": {     "message_id": 41,     "from": {       "id": 1424646511,       "is_bot": false,       "first_name": "Илья",       "last_name": "Лящук",       "username": "iliyalyachuk",       "language_code": "ru"     },     "chat": {       "id": 1424646511,       "first_name": "Илья",       "last_name": "Лящук",       "username": "iliyalyachuk",       "type": "private"     },     "date": 1659098034,     "text": "Новое тестовое сообщение"   } }

Данные при нажатие на кнопку в чате

Если пользователь нажал на кнопку, то на скрипт также будет отправлен запрос с данными о пользователе и о кнопке.

Отличительной особенностью таких запросов является то что главный ключ message заменяется на callback_query, а сам массив message будет находиться внутри.

Получить код кнопки на которую было произведено нажатие, можно из callback_query -> data.

{   "update_id": 803290921,   "callback_query": {     "id": "6118810175780540321",     "from": {       "id": 1424646511,       "is_bot": false,       "first_name": "Илья",       "last_name": "Лящук",       "username": "iliyalyachuk",       "language_code": "ru"     },     "message": {       "message_id": 113,       "from": {         "id": 5340791844,         "is_bot": true,         "first_name": "test_prog_time",         "username": "test_prog_time_bot"       },       "chat": {         "id": 1424646511,         "first_name": "Илья",         "last_name": "Лящук",         "username": "iliyalyachuk",         "type": "private"       },       "date": 1659335238,       "text": "Тестовое сообщение",       "reply_markup": {         "inline_keyboard": [           [             {               "text": "YOUR BUTTON LABEL TEXT",               "callback_data": "test_123"             }           ]         ]       }     },     "chat_instance": "4661722712167232747",     "data": "test_123"   } }

Данные при отправке изображения

Теперь давайте посмотри данные которые приходят при отправке изображения в чат, от пользователя.

{   "update_id": 803290893,   "message": {     "message_id": 42,     "from": {       "id": 1424646511,       "is_bot": false,       "first_name": "Илья",       "last_name": "Лящук",       "username": "iliyalyachuk",       "language_code": "ru"     },     "chat": {       "id": 1424646511,       "first_name": "Илья",       "last_name": "Лящук",       "username": "iliyalyachuk",       "type": "private"     },     "date": 1659099213,     "photo": [       {         "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAANzAAMpBA",         "file_unique_id": "AQADub0xG53kIUt4",         "file_size": 1863,         "width": 90,         "height": 90       },       {         "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAANtAAMpBA",         "file_unique_id": "AQADub0xG53kIUty",         "file_size": 30064,         "width": 320,         "height": 320       },       {         "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN5AAMpBA",         "file_unique_id": "AQADub0xG53kIUt-",         "file_size": 133230,         "width": 880,         "height": 880       },       {         "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN4AAMpBA",         "file_unique_id": "AQADub0xG53kIUt9",         "file_size": 138716,         "width": 800,         "height": 800       }     ]   } }

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

Полный код для сохранения будет выглядеть так:

/* токен */ $token = "5340791844:AAEXXDduvInvQrlWHRXykV91USOQSevrPVU";  /* массив с параметрами запроса */ $getQuery = array(     "file_id" => "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN5AAMpBA", ); $ch = curl_init("https://api.telegram.org/bot". $token ."/getFile?" . http_build_query($getQuery)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, false);  $resultQuery = curl_exec($ch); curl_close($ch);  /* записываем ответ в формате PHP массива */ $arrDataResult = json_decode($resultQuery, true);  /* записываем URL необходимого изображения */ $fileUrl = $arrDataResult["result"]["file_path"];  /* формируем полный URL до файла */ $photoPathTG = "https://api.telegram.org/file/bot". $token ."/" . $fileUrl;  /* забираем название файла */ $arrFilePath = explode("/", $fileUrl); $newFilerPath = __DIR__ . "/img/" . $arrFilePath[1];  /* сохраняем файл на сервер */ file_put_contents($newFilerPath , file_get_contents($photoPathTG));

Скрипт для ответа на запросы через Хук

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

Поехали…

Токен бота запишем в константу TG_TOKEN

define("TG_TOKEN", "5340791844:AAEXXDdu324vInvQrlWHyk8V91USOQSevrPVU");

Для удобства я создал специальные функции для отправки типовых запросов на сервер Telegram. Созданные функции принимают в качестве первого аргумента массив с параметрами запроса.

/* для отправки текстовых сообщений */ function TG_sendMessage($getQuery) {     $ch = curl_init("https://api.telegram.org/bot". TG_TOKEN ."/sendMessage?" . http_build_query($getQuery));     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);     curl_setopt($ch, CURLOPT_HEADER, false);     $res = curl_exec($ch);     curl_close($ch);      return $res; }  /* для отправки изображений */ function TG_sendPhoto($arrayQuery) {     $ch = curl_init('https://api.telegram.org/bot'. TG_TOKEN .'/sendPhoto');     curl_setopt($ch, CURLOPT_POST, 1);     curl_setopt($ch, CURLOPT_POSTFIELDS, $arrayQuery);     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);     curl_setopt($ch, CURLOPT_HEADER, false);     $res = curl_exec($ch);     curl_close($ch);      return $res; }  /* для получения данных о файле */ function TG_getFile($arrayQuery) {     $ch = curl_init("https://api.telegram.org/bot". TG_TOKEN ."/getFile?" . http_build_query($arrayQuery));     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);     curl_setopt($ch, CURLOPT_HEADER, false);     $res = curl_exec($ch);     curl_close($ch);      return $res; }

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

function list_files($path) {     if ($path[mb_strlen($path) - 1] != '/') { $path .= '/';     }       $files = array();     $dh = opendir($path);     while (false !== ($file = readdir($dh))) { if ($file != '.' && $file != '..' && !is_dir($path.$file) && $file[0] != '.') {     $files[] = $file; }     }       closedir($dh);     return $files; }

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

В переменные $textMessage записывает текст сообщения, а в переменную $chatId записываем id чата.

$data = file_get_contents('php://input');  $arrDataAnswer = json_decode($data, true); $textMessage = mb_strtolower($arrDataAnswer["message"]["text"]); $chatId = $arrDataAnswer["message"]["chat"]["id"];

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

Здесь желательно прописать более сложный обработчик для проверки типа файла, но сейчас, чтобы не затягивать видео, я просто буду проверять наличие файла в сообщение.

if(!empty($arrDataAnswer["message"]["photo"])) {     $documentData = array_pop($arrDataAnswer["message"]["photo"]); } else if(!empty($arrDataAnswer["message"]["document"])) {     $documentData = array_pop($arrDataAnswer["message"]["document"]); }

Далее мы прописываем проверку на текст сообщения и в случае нужного текста отправляем ответное сообщение.

Если пользователь отправил «Привет», то мы в ответ отправляем сообщение «Привет! Есть фото для меня?». Данное сообщение отправляется с помощью ранее созданной функции TG_sendMessage().

if($textMessage == 'привет') {     $textMessage_bot = "Привет! Есть фото для меня";      $arrayQuery = array( 'chat_id' => 1424646511, 'text'=> $textMessage_bot, 'parse_mode'=> "html",     );     TG_sendMessage($arrayQuery); }

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

else if($textMessage == 'хочу фото') {     $textMessage_bot = "Вот, держи!";      $listFile = list_files(__DIR__ . "/img/");      $max = count($listFile) - 1;     $randIdFile = rand(0, $max);      $filePath = __DIR__ . "/img/" . $listFile[$randIdFile];      $arrayQuery = array(          'chat_id' => $chatId,   "photo" => new CURLFile($filePath),   "caption" => "Вот твоё фото!"     );     TG_sendPhoto($arrayQuery);  } 

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

if(!empty($documentData)) {      $arrayQuery = array( "file_id" => $documentData["file_id"],     );     $resultQuery = TG_getFile($arrayQuery);      /* записываем ответ в формате PHP массива */     $arrDataResult = json_decode($resultQuery, true);      /* записываем URL необходимого изображения */     $fileUrl = $arrDataResult["result"]["file_path"];      /* формируем полный URL до файла */     $photoPathTG = "https://api.telegram.org/file/bot". TG_TOKEN ."/" . $fileUrl;      /* забираем название файла */     $arrFilePath = explode("/", $fileUrl);     $newFilerPath = __DIR__ . "/img/" . $arrFilePath[1];      /* сохраняем файл на сервер */     file_put_contents($newFilerPath , file_get_contents($photoPathTG));      $arrayQuery = array( 'chat_id' => 1424646511, 'text' => "Отличное фото! Я его, пожалуй, сохраню", 'parse_mode' => "html",     );     TG_sendMessage($arrayQuery);  }

Ну и на последок, давайте пропишем ещё 2 условия. Первое условие будет отправлять кнопку в чат, а второе условие будет проверять нажатие на кнопку и отправлять дополнительное сообщение.

Запрос для отправки кнопок создаём аналогично запросу со словом «Привет». По запросу мы будем отправлять 2 кнопки с callback_data — but_1 и but_2.

if($textMessage == 'отправь кнопки') {     $textMessage_bot = "Вот твои кнопки! Нажимай";      $arrayQuery = array( 'chat_id' => 1424646511, 'text'=> $textMessage_bot, 'parse_mode'=> "html", 'reply_markup' => json_encode(array(     'inline_keyboard' => array( array(     array( 'text' => 'Кнопка 1', 'callback_data' => 'but_1',     ),      array( 'text' => 'Кнопка 2', 'callback_data' => 'but_2',     ) ),     ), )),     );       TG_sendMessage($arrayQuery); }

Теперь давайте пропишем проверку нажатия на кнопки. Здесь нам нужно записать в переменную $dataBut код нашей кнопки, чтобы по нему в дальнейшем делать проверку. В переменную $textMessage и $chatId мы так же записываем текст сообщения и id пользователя, только в этот раз достаём эти данные из массива с ключом callback_query.

Ниже проверяем код нажатой кнопки и отправляем простое текстовое сообщение в ответ.

if($arrDataAnswer["callback_query"]) {     $dataBut = $arrDataAnswer["callback_query"]["data"];     $textMessage = mb_strtolower($arrDataAnswer["callback_query"]["message"]["text"]);     $chatId = $arrDataAnswer["callback_query"]["message"]["chat"]["id"];      if($dataBut == "but_1") { $arrayQuery = array(     'chat_id' => 1424646511,     'text' => "Ты нажал на 'КНОПКА 1'",     'parse_mode' => "html", ); TG_sendMessage($arrayQuery);     }     else if($dataBut == "but_2") { $arrayQuery = array(     'chat_id' => 1424646511,     'text' => "Ты нажал на 'КНОПКА 2'",     'parse_mode' => "html", ); TG_sendMessage($arrayQuery);     } }

Подведём итоги! В новом уроке, мы с вами научились обрабатывать запросы от Телеграма к серверу и прописали свой простой обработчик. Аналогичным образом вы можете прописать ответы на любые команды.

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/697016/


Комментарии

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

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