В новом уроке мы с вами поговорим о настройке хуков и напишем свой первый обработчик команд.
В первом уроке я вам рассказывал что такое хуки, давайте повторим:
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 с каталогом, карточками товаров, корзиной и формой для оформления заказа. Все данные о заказах будут записываться в базу данных. По итогу вы сможете разработать отличное решение, которое легко продать как готовый кейс на фриланс биржах.
Скорее всего ссылка на данный курс уже находится в описание, поэтому переходите и изучайте данный материал.