Как написать игрового 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

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

Файл 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 хранятся классы по работе как с приватным чатом, так и с супергруппой. Это не только удобно, как я говорил выше, но и даёт возможность отключить деятельность бота в одном из режимов, если мы соберёмся добавлять в него функциональность.

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

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

0
24 комментария
Написать комментарий...
Michael Smith
Мы ограничились composer, библиотекой telegram-bot-sdk

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

Ответить
Развернуть ветку
Библиотека Программиста
Автор

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

Ответить
Развернуть ветку
Michael Smith
Например, в ней не добавили возможность закреплять сообщения и ограничивать пользователя на сутки

С этими действиями понятно, там параметры скалярные и в ответ приходит только true или false. Если бы все методы были такими, то библиотека и не нужна была бы, то же самое что напрямую слать.
Попробуйте сделать вызовы новых методов, где в параметрах есть объекты или в ответах от сервера есть объекты, не реализованные в библиотеке. Например sendInvoice или sendMediaGroup.

Ответить
Развернуть ветку
Библиотека Программиста
Автор

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

Ответить
Развернуть ветку
Michael Smith
В крайнем случае можно сбоку написать свой функционал, обычная практика.

Это да, с той библиотекой я тоже многое сам дописывал. Там например нету типов CachedDocument, CachedPhoto и других, нужных для инлайн режима бота.
Полных либ я еще не видел, чтобы прям весь API был реализован.

Ответить
Развернуть ветку
Библиотека Программиста
Автор

Отличный повод написать свою реализацию и войти в топы гитхаба.

Ответить
Развернуть ветку
Библиотека Программиста
Автор

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

Ответить
Развернуть ветку
Michael Smith
Передавать ассоциативный массив, как это реализовано у нас, намного понятнее и удобнее.

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

Ответить
Развернуть ветку
Библиотека Программиста
Автор

С этим соглашусь.

Ответить
Развернуть ветку
Александр Дайверов

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

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Алексей Заузин

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

Ответить
Развернуть ветку
Sergey Zenzinov

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

Ответить
Развернуть ветку
Sergey Zenzinov

PS Я ради интереса написал бота, который полностью жил в базе MSSQL. Просто потому что могу :) (на самом деле, потому что понял, что мне больше ничего не нужно)

Ответить
Развернуть ветку
Библиотека Программиста
Автор

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

Ответить
Развернуть ветку
Sergey Zenzinov

но php в любом случае "не очень" :) https://habr.com/ru/post/142140/

Ответить
Развернуть ветку
Michael Smith

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

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

Ответить
Развернуть ветку
Библиотека Программиста
Автор

Непонятно, чем именно php уступает другим языкам для этой задачи.

Ответить
Развернуть ветку
Michael Smith

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

Ответить
Развернуть ветку
Библиотека Программиста
Автор

Всё так, php продолжают не любить по старой привычке. Адекватных аргументов против него становится все меньше с каждым новым релизом языка. У php много хороших инструментов и библиотек, хорошие фреймворки (Symfony, Slim) и проч.

Ответить
Развернуть ветку
Denis Shiryaev

Спасибо, а какие еще телеграм боты вы писали? Есть какой-то список?

Ответить
Развернуть ветку
Библиотека Программиста
Автор

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

Ответить
Развернуть ветку
Denis Shiryaev

А какие еще темы вы могли бы осветить в /dev?

Желательно, не для начального уровня, уже детальнее и глубже, тут довольно много разработчиков

Ответить
Развернуть ветку
Vadim Kafkiansky

Ну, мы ещё делали наш корпоративный сокращатель ссылок по типу bit.ly на фреймворке Symfony. Можем написать, как это работает, если кому интересно. Ещё различные сервисы для внутренних нужд. Планируется также кое-что большое от нашей библиотеки, о чём, возможно, потом мы захотим рассказать.

Ответить
Развернуть ветку
Vadim Kafkiansky

@Библиотека Программиста, неплохо

Ответить
Развернуть ветку
21 комментарий
Раскрывать всегда