Привет, HABR! В этой статье постараюсь показать маленький пример, использования клавиатуры под сообщением в Telegram. То есть мы с помощью бота будем отправлять пост в наш канал с использованием языка программирования PHP. Telegram BOT API неплохо документирован, но всё же остаются вопросы у новичков, как это всё собрать чтобы работало.
InlineKeyboardMarkup — этот объект представляет встроенную клавиатуру, которая появляется под соответствующим сообщением.
При публикации сообщение с использованием бота под сами сообщением вывести 5 кнопок (Нравиться, Ха-ха, Ух ты!, Сочувствую, Возмутительно) в виде смайликов. И цель их отображать количество людей, которые нажали на определённую кнопку.
В сети уже много готовых библиотек на PHP по работе с Telegram BOT API. Для нашей задачи они не понадобятся. Итак приступаем.
При нажатии на определённую кнопку добавить счётчик в неё. Пользователь может нажать только на одну из 5-кнопок. В БД мы будем хранить ID сообщения и ID пользователей, которые уже отреагировали на сообщение.
Как видим по коду он очень просто и мы тут просто отправляем «Hello World!» сообщение и к нему добавляем 5 кнопок.
Нам также нужно установить Webhook для бота. Это нужно для того, чтобы указать боту куда (http://site.ru/callback.php) отправлять результат обработки кнопок. Это очень просто сделать, в браузер соберите ссылку такого формата:
В ответ получите json примерно такого содержания:
Вот и всё, такой маленький пример получился, заранее извините, если код написан безобразно. Можно расширить этот код до нужной функциональности, это уже зависит от вашей задачи. Моя задача была показать маленький пример, и я надеюсь у меня это получилось. Спасибо за внимание, встретимся в комментариях.
InlineKeyboardMarkup — этот объект представляет встроенную клавиатуру, которая появляется под соответствующим сообщением.
Что мы хотим сделать?
При публикации сообщение с использованием бота под сами сообщением вывести 5 кнопок (Нравиться, Ха-ха, Ух ты!, Сочувствую, Возмутительно) в виде смайликов. И цель их отображать количество людей, которые нажали на определённую кнопку.
В сети уже много готовых библиотек на PHP по работе с Telegram BOT API. Для нашей задачи они не понадобятся. Итак приступаем.
Нам понадобится
- Создать telegram канал;
- Создать бота;
- Добавить бота в telegram канал;
- Хостинг на котором будет лежать файл обработки логики кнопок;
- База данных в которой будем хранить id пользователей, которые нажал на кнопку.
Логика
При нажатии на определённую кнопку добавить счётчик в неё. Пользователь может нажать только на одну из 5-кнопок. В БД мы будем хранить ID сообщения и ID пользователей, которые уже отреагировали на сообщение.
Структура файлов
- send.php — отправляем сообщение в Telegram канал
- callback.php — обрабатываем клики кнопок получены из Telegram канала
Содержание файла send.php
<?php
define( 'TOKEN', '<token>' );
define( 'CHAT_ID', '<chat_id>' ); // @name_chat
define( 'API_URL', 'https://api.telegram.org/bot' . TOKEN . '/' );
function request($method, $params = array()) {
if ( (!empty($params) ) {
$url = API_URL . $method . "?" . http_build_query($params);
} else {
$url = API_URL . $method;
}
return json_decode(file_get_contents($url), JSON_OBJECT_AS_ARRAY);
}
$keyboard = array(
array(
array('text'=>':like:','callback_data'=>'{"action":"like","count":0,"text":":like:"}'),
array('text'=>':joy:','callback_data'=>'{"action":"joy","count":0,"text":":joy:"}'),
array('text'=>':hushed:','callback_data'=>'{"action":"hushed","count":0,"text":":hushed:"}'),
array('text'=>':cry:','callback_data'=>'{"action":"cry","count":0,"text":":cry:"}'),
array('text'=>':rage:','callback_data'=>'{"action":"rage","count":0,"text":":rage:"}')
)
);
request("sendMessage", array(
'chat_id' => CHAT_ID,
'text' => "hello world!",
'disable_web_page_preview' => false,
'reply_markup' => json_encode(array('inline_keyboard' => $keyboard))
));
Как видим по коду он очень просто и мы тут просто отправляем «Hello World!» сообщение и к нему добавляем 5 кнопок.
Нам также нужно установить Webhook для бота. Это нужно для того, чтобы указать боту куда (http://site.ru/callback.php) отправлять результат обработки кнопок. Это очень просто сделать, в браузер соберите ссылку такого формата:
https://api.telegram.org/bot{my_bot_token}/setWebhook?url={url_to_send_updates_to}
В ответ получите json примерно такого содержания:
{"ok":true,"result":true,"description":"Webhook was set"}
Содержание файла callback.php
<?php
define( 'TOKEN', '<token>' );
define( 'CHAT_ID', '<chat_id>' ); // @name_chat
define( 'API_URL', 'https://api.telegram.org/bot' . TOKEN . '/' );
function request($method, $params = array()) {
if ( (!empty($params) ) {
$url = API_URL . $method . "?" . http_build_query($params);
} else {
$url = API_URL . $method;
}
return json_decode(file_get_contents($url), JSON_OBJECT_AS_ARRAY);
}
function editMessageReplyMarkup($params){
//В этом цикле мы изменяем сами кнопки, а именно текст кнопки и значение параметра callback_data
foreach ( $params['inline_keyboard'][0] as $key => $value ) {
$data_for = json_decode($value->callback_data, true); // изначально у нас callback_data храниться в виде json-а, декатируем в массив
if ( $params['data']['action'] == $data_for['action'] ) { // определяем, на какую именно кнопку нажал пользователь под сообщением
$data_for['count']++; //плюсуем единичку
$value->text = $data_for['text'] . " " . $data_for['count']; // Изменяем текст кнопки смайлик + количество лайков
}
$value->callback_data = json_encode($data_for); // callback_data кнопки кодируем в json
$params['inline_keyboard'][0][$key] = (array)$value; // изменяем кнопку на новую
}
//Изменяем кнопки к сообщению
request("editMessageReplyMarkup", array(
'chat_id' => CHAT_ID,
'message_id' => $params['message_id'],
'reply_markup' => json_encode(array('inline_keyboard' => $params['inline_keyboard'])),
));
//Выводим сообщение в чат
request("answerCallbackQuery", array(
'callback_query_id' => $params['callback_query_id'],
'text' => "Спасибо! Вы поставили " . $params['data']['text'],
));
}
$result = json_decode(file_get_contents('php://input')); // получаем результат нажатия кнопки
$inline_keyboard = $result->callback_query->message->reply_markup->inline_keyboard; // текущее состояние кнопок при нажатии на одну из 5 кнопок
$data = json_decode($result->callback_query->data, true); // получаем значение с кнопки, а именно с параметра callback_data нажатой кнопки
$message_id = $result->callback_query->message->message_id; // ID сообщения в чате
$callback_query_id = $result->callback_query->id; //ID полученного результата
$user_id = $result->callback_query->from->id; // ID пользователя
$db_message = $db->super_query("SELECT * FROM bot_like WHERE message_id={$message_id}"); //Ищем в БД ID сообщения
/*
Я использую библиотеку ($db = new db;) от CMS DLE для работы с БД.
super_query - этот метод возвращает первую найденную запись в виде массива
*/
if ( $db_message === null ) {
// Если не нашли в БД ID сообщения, записываем в БД текущий ID сообщения и ID пользователя, который отреагировал (нажал на одну из 5 кнопок) на сообщение.
$db->query("INSERT INTO " . PREFIX . "_posting_tg_bot_like (message_id, users) VALUES ('{$message_id}', '{$user_id}')");
editMessageReplyMarkup(array(
'inline_keyboard' => $inline_keyboard,
'data' => $data,
'message_id' => $message_id,
'callback_query_id' => $callback_query_id
));
} else {
//Если в БД нашли сообщение
$users = explode(",", $db_message['users']);
// Если нашли в БД пользователя, выводим сообщение "Вы уже нажали на одну из 5 кнопок"
if( in_array($user_id, $users) ) {
request("answerCallbackQuery", array(
'callback_query_id' => $callback_query_id,
'text' => "Вы уже отреагировали на новость",
));
}
// Если не нашли ID в БД изменяем одну из 5 кнопок и добавляем ID пользователя
else {
editMessageReplyMarkup(array(
'inline_keyboard' => $inline_keyboard,
'data' => $data,
'message_id' => $message_id,
'callback_query_id' => $callback_query_id
));
array_push($users, $user_id);
$users = implode(',', $users);
$db->query("UPDATE bot_like SET users='{$users}' WHERE message_id='{$message_id}'");
}
}
Вот и всё, такой маленький пример получился, заранее извините, если код написан безобразно. Можно расширить этот код до нужной функциональности, это уже зависит от вашей задачи. Моя задача была показать маленький пример, и я надеюсь у меня это получилось. Спасибо за внимание, встретимся в комментариях.
FanatPHP
Код действительно получился так себе. Несколько замечаний, если позволите.
Вы не совсем правильно понимаете смысл константы JSON_OBJECT_AS_ARRAY. Во второй аргумент этой функции надо передавать булево значение, в данном случае —
true
, а не константу.Если у вас нет глобального обработчика ошибок, то результат file_get_contents() желательно проверить вручную, и остановить выполнение. Иначе при ошибке запроса в логи будет сыпаться миллион совершенно ненужных сопутствующих ошибок.
Форматирование кода — это не мелочь. Очень трудно бывает понять, "кто на ком стоял". Скобки скачут в разные стороны, чтобы понять подчиненность операторов, мне пришлось скопировать код в редактор, который автоматически форматирует код.
Хранение данных через запятую — это совершенно детсадовская, но при этом очень грубая ошибка, которая показывает, увы, полное незнание основ реляционных баз данных. Перечисления должны храниться отдельно, каждое в своей ячейке. И, соответственно, вот это вот
if( in_array($user_id, $users) )
— это испанский стыд. БД придумана для того, чтобы искать нужные нам данные. А не возвращать гору мусора, в которой потом надо будет копаться вручную. Запрос должен сразу запрашивать нужное значение, т.е.Соответственно, логика приложения сразу станет значительно проще, безо всех этих вложенных if и повторений повторений:
Ну и последнее. Знаки вопроса в SQL запросах выше не просто так. Они называются плейсхолдерами или параметрами. И 2022 году запрос к БД из РНР должен выглядеть именно так (ну или, для педантов, может как :mid, :uid). И здесь мы переходим к следующему пункту:
$db->query()
и$db->super_query()
— это, извините, позор.Я понимаю, что вам, возможно, привычна эта библиотека. Но всё-таки, надо хотя бы немного интересоваться такими вещами, как безопасность кода вообще, и защита от SQL инъекций в частности. Надо забыть про эту "библиотеку от CMS DLE" и научиться использовать что-то более безопасное, хотя бы PDO.
Сразу скажу, не стоит оправдываться такими вещами, как "это внутренние идентификаторы Телеграма, а в них инъекции быть не может" — это, извините, детский лепет. По двум причинам:
В целом, я совсем не против статей даже на такие тривиальные темы, на которые есть сотня готовых ответов на Тостере. Статьи нужны для любого уровня. Но к таким статьям должны тем более применяться более строгие требования, потому что иначе нет смысла её размещать — плохого кода в интернете и так навалом.