Как написать игрового Telegram-бота в домашних условиях

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

Однажды мы решили, что нам нужно сделать бота. Бот должен был давать логические задачи в групповом чате в Telegram «Развиваем логику», закреплять их на то время, пока её решают, не давать новую, пока ответ кого-то из участников не наберёт десять (потом снизили до пяти) плюсов, а также вывешивать топ наиболее успешных решателей задачек.

Как написать игрового Telegram-бота в домашних условиях

Идея не приходит одна

Задача довольно простая, подумали мы, особенно учитывая волну популярности ботов и количество материала в сети по этой теме. Однако материала не очень качественного: кто-то не использует композер, а кто-то даже пишет весь код в одном файле-обработчике, полагая, очевидно, будто количество файлов влияет на скорость работы кода.

Сначала бот должен был просто давать задачи, причём их можно было пропускать, постоянно вызывая одну и ту же команду. Это первая проблема, которую мы стали решать. Выход нашёлся почти сразу: сохранение состояния бота (а точнее, текущей команды) в базе. Другими словами, получая команду /get, бот даёт задачу и сохраняет команду в базе.

В следующий раз, перед тем как отреагировать на сообщения в чате, бот сравнивает команду, которую получил прямо сейчас, с последней командой в базе. Если они совпадают, бот никак не реагирует и вдобавок удаляет команду пользователя, чтобы она не мусорила чат. Согласитесь, это выглядит как хорошее решение.

После запуска бота в работу мы обнаружили ещё одну проблему: хэштеги не очень удобны для поиска. Ведь задача может уйти далеко наверх, а листать до неё или забивать в поиске — достаточно неинтересное занятие, чтобы стравливать его пользователю. Поэтому мы решили сразу же закреплять задачу, которую даёт бот. Причём закрепляет как раз он сам, а не мы (и да, делает это без уведомлений).

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

Поэтому мы подумали, что коллективное решение намного справедливее решения одного человека, и дали пользователям выбирать самим, помечая плюсом сообщение, с которым они согласны.

Какое решение быстрее наберёт пять плюсов, то и выбирается ботом как лучшее. Это хорошее решение не только с точки зрения справедливости, но и автоматизации: нам больше не придётся следить за чатом и читать все ответы.

Подготовка к созданию

Дело осталось за малым: написать бота. Мы ограничились Composer, библиотекой telegram-bot-sdk и symfony/dotenv для парсинга .env-файла. Весь код приводить не буду: он большой. Посмотреть на то, что получилось, можно по ссылке.

Composer — это стандарт при разработке на PHP. Он позволяет скачивать сторонние библиотеки на проект и предоставляет удобный механизм по автозагрузке классов. Вся работа с Composer происходит через консоль и в файле composer.json. Обычно он выглядит так:

// composer.json { "require": { "irazasyed/telegram-bot-sdk": "3.*@dev", "symfony/dotenv": "^4.2" }, "autoload": { "psr-4": { "App\\": "src/" } } }

Если вы разрабатываете не на фреймворке, то во множестве случаев создаёте composer.json самостоятельно и заполняете секцию autoload, которая загружает ваши классы по правилу psr-4, о котором можно найти много информации в интернете.

Далее вы выполняете команду composer install, и автозагрузка начинает работать. Также не забудьте про библиотеки, которые нужно установить, для этого выполните в терминале в папке с проектом следующие две команды:

composer require irazasyed/telegram-bot-sdk composer require symfony/dotenv

И тогда ваш composer.json станет похож на тот, что я показывал выше.

Mr. Bot

Сразу же продемонстрирую готовую структуру проекта:

Как написать игрового Telegram-бота в домашних условиях

Файл app.php является точкой входа в наше приложение, на который мы вешаем веб-хук (это значит, что бот не будет постоянно опрашивать сервер на наличие обновлений; он их будет получать только тогда, когда они будут). Вот как он выглядит:

// app.php <?php use App\Bot\CommonChatHandler; use App\Bot\PrivateChatHandler; use App\Handler; use App\Storage\DB; use Telegram\Bot\Api; require __DIR__ .'/vendor/autoload.php'; $settings = require __DIR__ . '/config/settings.php'; $api = new Api($settings['token']); $bot = new Handler($api); $db = new DB($settings['db']); if ($bot->getChatType() === "private") { (new PrivateChatHandler($bot, $db))->start(); } elseif ($bot->getChatType() === "supergroup") { (new CommonChatHandler($bot, $db))->start(); }

Ничего необычного, создаём объекты всех нужных нам классов и делаем проверку на то, в каком чате мы находимся — приватном или супергруппе. Да, разделить код на достаточно независимые части является хорошей практикой. К тому же легче рефакторить и добавлять код, когда проблемы того потребуют.

Мы любим чистый код, поэтому настройки храним в файле .env, что позволит любому пользователю ввести свои, и бот будет работать (разумеется, после создания всех нужных таблиц).

// .env DB_DSN=mysql:host=changeme;dbname=changeme DB_USERNAME=changeme DB_PASSWORD=changeme BOT_ADMIN=changeme TOKEN=changeme

Однако получить переменные окружения из .env можно только в том случае, если вы спарсите этот файл. Делается это крайне просто:

// config/settings.php <?php use Symfony\Component\Dotenv\Dotenv; $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__ . '/../.env'); return [ 'db' => [ 'dsn' => getenv('DB_DSN'), 'username' => getenv('DB_USERNAME'), 'password' => getenv('DB_PASSWORD') ], 'token' => getenv('TOKEN') ];

Это тот самый файл, который мы включили в app.php. Там мы просто по ключу достаём нужные нам настройки:

$settings = require __DIR__ . '/config/settings.php'; $settings['token']; $settings['db'];

В папках Admin и User хранятся классы по работе как с приватным чатом, так и с супергруппой. Это не только удобно, как я говорил выше, но и даёт возможность отключить деятельность бота в одном из режимов, если мы соберёмся добавлять в него функциональность.

Больше томить вас кодом не буду, повторю только, что теперь он в свободном доступе.

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

1313
24 комментария

Мы ограничились composer, библиотекой telegram-bot-sdktelegram-bot-sdk можно сказать заброшенный проект, они 2 года не добавляли новые фичи из Telegram Bot API, а там много что появилось.
Вот эта либа получше https://github.com/TelegramBot/Api но и там не все фичи реализованы, многое сам дописывал. Полных библиотек наверное и нету.

4
Ответить

Это совсем не значит, что если там чего-то нет, это нельзя использовать. Например, в ней не добавили возможность закреплять сообщения и ограничивать пользователя на сутки. Но мы всё равно вызываем эти методы так, будто они там есть (хоть phpstorm на это и ругается), и они работают. Можете скачать и убедиться сами.

1
Ответить

Возможно, там и больше функционала, но вот это точно никуда не годится:
$bot->sendMessage($chatId, $messageText, null, false, null, $keyboard);
Передавать ассоциативный массив, как это реализовано у нас, намного понятнее и удобнее. А с этой библиотекой придётся мусорить переменными по всему коду, если не хотите оставлять null, false или true магическими переменными, о назначении которых вы забудете на следующий день.

1
Ответить

"Возможно, кто-то о нас слышал." и "Мы — популярное интернет-издание по программированию. "
Шуточка за 300

1
Ответить

Почему был выбран именно PHP?
Для решения задачи написания бота это очень плохо подходящая технология.

Ответить

Да какая разница вообще, на чем писать? API телеграма подробнейше документировано, надо просто правильно формировать запросы на удобном для себя языке. Все равно все самое сложное - это логика самого бота.

1
Ответить