Консольная команда Yii2 для Telegram: управление webhook, health-check getMe и очистка логов

При эксплуатации Telegram-бота в продакшене регулярные административные задачи — такие как переключение вебхуков при деплое, проверка доступности API и ротация накопившихся записей в базе данных — не должны выполняться вручную через Postman или браузер. Для этого в фреймворке Yii2 идеально подходит консольный контроллер (Yii Console Controller).

В этом руководстве мы разработаем полноценную CLI-команду для автоматизации обслуживания Telegram-бота, задействовав штатные механизмы Yii2 и чистый cURL для работы с Telegram Bot API.

Зачем необходим CLI-инструмент обслуживания бота

Использование консольного интерфейса решения задач администрирования Telegram-бота дает несколько ключевых преимуществ:

  • Безопасность: административные методы (например, setWebhook) не доступны из внешнего веба и запускаются только авторизованными пользователями сервера или CI/CD пайплайнами.
  • Автоматизация: ротацию логов и проверки здоровья (health-check) можно легко повесить на системный crontab.
  • Управление конфигурацией: параметры secret_token, домены и настройки лимитов берутся строго из конфигурации приложения и переменных окружения (getenv).

Базовый консольный контроллер и cURL-клиент

Создадим консольный контроллер commands/TelegramController.php. В нем предусмотрим приватный метод для выполнения HTTP-запросов к Telegram Bot API с помощью cURL с проверкой HTTP-кодов статуса, ошибок JSON и поля ok в ответе.

<?php

namespace app\commands;

use Yii;
use yii\console\Controller;
use yii\console\ExitCode;
use yii\helpers\Console;

class TelegramController extends Controller
{
private string $botToken;
private string $secretToken;

public function init()
{
parent::init();
$this->botToken = (string) (Yii::$app->params['telegram_bot_token'] ?? getenv('TELEGRAM_BOT_TOKEN'));
$this->secretToken = (string) (Yii::$app->params['telegram_secret_token'] ?? getenv('TELEGRAM_SECRET_TOKEN'));

if (empty($this->botToken)) {
$this->stderr("Ошибка: TELEGRAM_BOT_TOKEN не задан в конфигурации.\n", Console::FG_RED);
exit(ExitCode::CONFIG);
}
}

/**
* Прямой HTTP-запрос к Telegram Bot API через cURL
*/
private function sendApiRequest(string $method, array $params = []): ?array
{
$url = "https://api.telegram.org/bot{$this->botToken}/{$method}";

$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5]);

$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($response === false) {
$this->stderr("cURL Error [{$method}]: {$curlError}\n", Console::FG_RED);
return null;
}

$decoded = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->stderr("JSON Decode Error [{$method}]: " . json_last_error_msg() . "\n", Console::FG_RED);
return null;
}

if ($httpCode !== 200 || empty($decoded['ok'])) {
$description = $decoded['description'] ?? 'Unknown error';
$this->stderr("Telegram API Error [{$method}] (HTTP {$httpCode}): {$description}\n", Console::FG_RED);
return null;
}

return $decoded['result'];
}
}

Реализация методов setWebhook и deleteWebhook

Для корректной работы вебхука необходимо обязательно использовать параметр secret_token (от 1 до 256 символов, допускаются символы A-Z, a-z, 0-9, _ и -). Этот токен передается в заголовке X-Telegram-Bot-Api-Secret-Token при каждом входящем запросе от Telegram, что позволяет контроллеру валидировать источник.

Добавим в контроллер действия для регистрации и удаления вебхука:

    /**
* Установка Webhook для бота
* Пример: php yii telegram/set-webhook https://example.com/telegram/webhook
*/
public function actionSetWebhook(string $url): int
{
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->stderr("Ошибка: Некорректный URL '{$url}'\n", Console::FG_RED);
return ExitCode::DATAERR;
}

$params = [
'url' => $url,
'max_connections' => 40,
'allowed_updates' => ['message', 'callback_query', 'my_chat_member']];

if (!empty($this->secretToken)) {
$params['secret_token'] = $this->secretToken;
}

$this->stdout("Отправка запроса setWebhook на {$url}...\n");
$result = $this->sendApiRequest('setWebhook', $params);

if ($result === true) {
$this->stdout("Webhook успешно установлен!\n", Console::FG_GREEN);
return ExitCode::OK;
}

return ExitCode::UNSPECIFIED_ERROR;
}

/**
* Удаление Webhook с опциональной очисткой зависших апдейтов
* Пример: php yii telegram/delete-webhook 1
*/
public function actionDeleteWebhook(bool $dropPendingUpdates = false): int
{
$params = [
'drop_pending_updates' => $dropPendingUpdates];

$this->stdout("Удаление Webhook...\n");
$result = $this->sendApiRequest('deleteWebhook', $params);

if ($result === true) {
$this->stdout("Webhook успешно удален.\n", Console::FG_GREEN);
return ExitCode::OK;
}

return ExitCode::UNSPECIFIED_ERROR;
}

Health-check бота: проверка getMe и getWebhookInfo

Для мониторинга состояния бота создадим команду actionHealth. Она опрашивает метод getMe (проверка валидности токена и связи с серверами Telegram) и метод getWebhookInfo (проверка статуса доставки апдейтов, очереди необработанных сообщений и возможных ошибок доставки).

    /**
* Проверка здоровья бота и состояния Webhook
* Пример: php yii telegram/health
*/
public function actionHealth(): int
{
$this->stdout("=== Проверка Health-check Telegram API ===\n", Console::BOLD);

// 1. Проверка getMe
$botInfo = $this->sendApiRequest('getMe');
if (!$botInfo) {
$this->stderr("[FAIL] Не удалось получить данные бота через getMe\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}

$this->stdout("[OK] Бот авторизован: @{$botInfo['username']} (ID: {$botInfo['id']})\n", Console::FG_GREEN);

// 2. Проверка getWebhookInfo
$webhookInfo = $this->sendApiRequest('getWebhookInfo');
if (!$webhookInfo) {
$this->stderr("[FAIL] Не удалось запросить getWebhookInfo\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}

$url = $webhookInfo['url'] ?: 'не установлен';
$pendingCount = $webhookInfo['pending_update_count'] ?? 0;
$lastErrorDate = $webhookInfo['last_error_date'] ?? null;
$lastErrorMessage = $webhookInfo['last_error_message'] ?? null;

$this->stdout("Текущий URL: {$url}\n");
$this->stdout("Зависших апдейтов (Pending updates): {$pendingCount}\n");

if ($lastErrorDate) {
$dateStr = date('Y-m-d H:i:s', $lastErrorDate);
$this->stderr("[WARNING] Последняя ошибка отправки ({$dateStr}): {$lastErrorMessage}\n", Console::FG_YELLOW);
} else {
$this->stdout("[OK] Ошибок доставки вебхука не зафиксировано\n", Console::FG_GREEN);
}

return ExitCode::OK;
}

Ротация логов апдейтов и очистка таблицы дедупликации

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

    /**
* Ротация логов входящих апдейтов и дедупликации в БД
* Пример: php yii telegram/rotate-logs 14
*/
public function actionRotateLogs(int $days = 14): int
{
if ($days < 1) {
$this->stderr("Количество дней должно быть не менее 1.\n", Console::FG_RED);
return ExitCode::DATAERR;
}

$thresholdDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
$this->stdout("Удаление логов Telegram старше {$thresholdDate}...\n");

try {
$deletedRows = Yii::$app->db->createCommand()
->delete('telegram_update_log', ['<', 'created_at', $thresholdDate])
->execute();

$this->stdout("Успешно удалено записей: {$deletedRows}\n", Console::FG_GREEN);
return ExitCode::OK;
} catch (\Exception $e) {
$this->stderr("Ошибка при очистке БД: " . $e->getMessage() . "\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}
}

Настройка системного планировщика Crontab

Для автоматического поддержания чистоты БД и периодической проверки вебхука добавьте следующие строки в файл crontab на вашем сервере:

# Ежедневная очистка логов Telegram старше 14 дней в 03:00 ночи
0 3 * * * /usr/bin/php /path/to/your/project/yii telegram/rotate-logs 14 >> /dev/null 2>&1

# Каждые 15 минут проверка работоспособности (Health-check)
*/15 * * * * /usr/bin/php /path/to/your/project/yii telegram/health >> /var/log/telegram_health.log 2>&1

Типичные ошибки при эксплуатации консольных команд

  • Захардкоженные токены: Никогда не оставляйте токен бота в теле файла контроллера. Используйте Yii::$app->params или переменные среды getenv().
  • Использование file_get_contents: Для консольных команд всегда применяйте cURL с явными таймаутами (CURLOPT_TIMEOUT, CURLOPT_CONNECTTIMEOUT), чтобы CLI-процесс не зависал при сбоях сети.
  • Отсутствие обработки HTTP 429: Если команда совершает массовые запросы, учитывайте лимиты Telegram Bot API и обрабатывайте заголовки/параметры retry_after.

Если вам требуется разработка надежных Telegram-ботов или Mini Apps с продуманной архитектурой и интеграцией в CRM, обратитесь к специалистам BotCreator.

Новые статьи — в Telegram

Разбираем, что автоматизировать в бизнесе и как это работает на практике. Без спама.