Запускаем бота

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

Сегодня мы этого бота запустим с простейшими командами /start и /help.

Напоминаю, что мы используем библиотеку longman/telegram-bot, которая предлагает очень удобный метод для написания собственных команд боту.

Команда - это файл, имя которого заканчивается на Command и расширяет абстрактный класс Longman\TelegramBot\Commands\Command - ровно также, как контроллеры Vesp расширяют основной абстрактный контроллер.

Создаём директорию /core/src/Commands и в ней файл StartCommand.php:

<?php

namespace App\Commands;

use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Entities\ServerResponse;

class StartCommand extends UserCommand
{
    protected $name = 'start';
    protected $description = 'Запуск бота';
    protected $usage = '/start';

    public function execute(): ServerResponse
    {
        $user = $this->getMessage()->getFrom();
        $data = [
            'Привет, ' . ($user->getFirstName()) . '!',
            'Это тренировочный бот, написанный в целях обучения на https://bezumkin.ru/sections/vesp-telegram.',
            'На данный момент бот отвечает раз в минуту. Используй /help, чтобы увидеть все доступные команды.',
        ];

        return $this->replyToChat(implode(PHP_EOL . PHP_EOL, $data));
    }
}

Теперь нам нужно указать нашему боту место с новыми командами. Делаем это в предусмотрительно заданном сервисе core/src/Services/Telegram.php прямо в конструкторе:

    public function __construct()
    {
        parent::__construct(getenv('BOT_API_KEY'), getenv('BOT_USERNAME'));
        // Наши команды
        $this->addCommandsPath(BASE_DIR . 'core/src/Commands');
    }

Константа BASE_DIR объявляется в файле core/bootstrap.php и означает корень всего проекта, так что можно смело её везде использовать.

Тут нужно небольшое лирическое отступление.

Как работают боты в Телеграм

Когда вы пишете что-то боту, ваше сообщение уходит на сервер Телеграма, и дальше он уже смотрит в настройки бота. Если у него есть назначенный адрес приёма сообщение (webhook), то сервис пытается переслать ваше сообщение на этот адрес.

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

Если ваш сервер ответит кодом 200, значит всё ок, вы получили сообщение. Телеграм больше ничего от вас не ждёт, и дальше ваш бот должен отправить своё сообщение серверу Телеграм, а тот его перешлёт вам в чат.

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

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

Обработка сообщений

Для консольных скриптов, котороми обычно и являются команды cron, у Vesp предусмотрена директория cli, то бишь Command Line Interface.

Создаём core/cli/get-updates.php

<?php

require dirname(__DIR__) . '/bootstrap.php';

try {
    $telegram = new \App\Services\Telegram();
    $telegram->useGetUpdatesWithoutDatabase();
    $telegram->handleGetUpdates();
} catch (Throwable $e) {
    echo $e->getMessage();
}

Базу данных мы использовать не будем, поэтому здесь только загрузка нашего сервиса Telegram, без Eloquent.

Команда handleGetUpdates получит все отправленные боту сообщения, сопоставит их с командами, которые в нём прописаны, и отправит ответы юзерам на сервер Телеграм.

Этот файл можно запускать в консоли вручную или добавить в менджер задач проекта.

Для добавления есть готовый файл core/cli/cron.php, куда мы пишем:

$scheduler->php(__DIR__ . '/get-updates.php', null, [], 'get_updates')
    ->everyMinute()
    ->inForeground()
    ->onlyOne();

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

Осталось только добавить менеджер в crontab. У себя на MacOS я делаю это через EDITOR=nano crontab -e в консоле.

Внутри пишем что-то вроде этого, образая внимание на пути к файлам:

* * * * *	/opt/homebrew/bin/php ~/ВашиПроекты/ExampleBot/core/cli/cron.php

Теперь команда /start уже должна работать.

Но при использовании /help, увы, ничего не будет, потому что мы такой команды не добавляли.

Исправляемся в файле core/src/Commands/HelpCommand.php:

<?php

namespace App\Commands;

use Longman\TelegramBot\Commands\UserCommand;
use Longman\TelegramBot\Entities\ServerResponse;

class HelpCommand extends UserCommand
{
    protected $name = 'help';
    protected $description = 'Вывод сообщения со списком команд';
    protected $usage = '/help';

    public function execute(): ServerResponse
    {
        $data = [
            'Вот все доступные команды:',
            '',
        ];

        /** @var UserCommand[] $commands */
        $commands = $this->telegram->getCommandsList();
        foreach ($commands as $command) {
            if ($command->showInHelp() && $command->getUsage()) {
                $data[] = $command->getUsage() . ' ' . $command->getDescription();
            }
        }

        return $this->replyToChat(implode(PHP_EOL, $data));
    }
}

Как видно из кода, здесь мы получаем все доступные команды и выводим их описание пользователю.

Заключение

Вот и всё, бот @VespExampleBot уже работает, можно подключаться к нему в Телеграме и проверять.

Только учтите, что на данный момент он отвечает только раз в минуту, потому что выполняется на моём домашнем компьютере через cron.

Текущий исходный код можно посмотреть вот здесь. Продолжение скоро!

Следующая заметка →
Начинаем общение
Комментарии (4)
Сергей Лелеко
01.03.2022 18:16

Получается под каждую команду свой скрипт и соотвественно своя логика отдельно, ну в целом наверное это и правильно

bezumkinВасилий Наумкин
01.03.2022 18:21

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

Точно так же как в контроллерах Vesp - лично мне такой подход очень нравится.

Сергей Лелеко
01.03.2022 18:26

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

bezumkinВасилий Наумкин
01.03.2022 18:32

Я делал одного бота на botman/botman, но из-за своей универсальности конкретно с Телеграм на нём работать мне не понравилось.

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

bezumkin
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце &quot;Vesp/Core&quot; ты пишешь про &quot;новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...