In the process of developing and maintaining Telegram bots on the Yii2 framework, routine administrative tasks quickly become a bottleneck. Switching a bot from a local development environment (where Long Polling is more commonly used) to a production server (Webhook), checking connection status, debugging the API, and clearing incoming update logs—all of this requires automation. Calling Bot API methods manually via a browser or third-party clients is insecure and inconvenient.
The optimal solution is to move these operations into a Yii2 console command (yii telegram/*). This allows you to automate deployment (deploy pipelines), quickly perform diagnostics directly on the server, and set up regular maintenance tasks via Cron.
Console Command Architecture and Secure cURL Client
To integrate with Telegram Bot API, we will write our own lightweight cURL client inside the console controller. Using the standard file_get_contents in production is highly discouraged: it does not allow flexible timeout configuration, does not handle HTTP error codes correctly, and is vulnerable to blocking during network delays.
Let's create the file commands/TelegramController.php. We will retrieve Токен бота from environment variables or the configuration file Yii::$app->params to avoid hardcoding sensitive data in the repository.
<?php
namespace app\commands;
use Yii;
use yii\console\Controller;
use yii\console\ExitCode;
use yii\helpers\Console;
class TelegramController extends Controller
{
private ?string $token = null;
/**
* Инициализация контроллера и валидация токена
*/
public function init()
{
parent::init();
// Загружаем токен из env или параметров Yii2
$this->token = getenv('TELEGRAM_BOT_TOKEN') ?: (Yii::$app->params['telegramBotToken'] ?? null);
if (empty($this->token)) {
$this->stderr("Ошибка: Токен Telegram-бот а не сконфигурирован.\n", Console::FG_RED);
exit(ExitCode::CONFIG);
}
}
/**
* Универсальный метод отправки запросов к Telegram Bot API через cURL
*/
protected function sendRequest(string $method, array $params = []): array
{
$url = "https://api.telegram.org/bot{$this->token}/{$method}";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new \RuntimeException("Ошибка cURL: " . $curlError);
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("Ошибка декодирования JSON: " . json_last_error_msg());
}
if ($httpCode !== 200 || !isset($data['ok']) || $data['ok'] !== true) {
$description = $data['description'] ?? 'Неизвестная ошибка';
$errorCode = $data['error_code'] ?? $httpCode;
throw new \RuntimeException("Telegram API Error [{$errorCode}]: {$description}");
}
return $data['result'];
}
}Bot Health-Check: getMe and getWebhookInfo
The first thing needed when diagnosing a bot on a server is to find out its current status. The actionStatus command will perform two consecutive requests: getMe to verify the token and getWebhookInfo to check the current state of the webhook.
Let's add the following code to our controller:
/**
* Проверка состояния бота и текущих настроек Webhook
*/
public function actionStatus()
{
try {
$this->stdout("Запрос getMe... ", Console::FG_YELLOW);
$me = $this->sendRequest('getMe');
$this->stdout("OK\n", Console::FG_GREEN);
$this->stdout("Бот: @{$me['username']} (ID: {$me['id']})\n", Console::FG_BOLD);
$this->stdout("Запрос getWebhookInfo... ", Console::FG_YELLOW);
$webhook = $this->sendRequest('getWebhookInfo');
$this->stdout("OK\n", Console::FG_GREEN);
if (empty($webhook['url'])) {
$this->stdout("Статус Webhook: НЕ УСТАНОВЛЕН (бот работает в режиме Long Polling)\n", Console::FG_CYAN);
} else {
$this->stdout("URL вебхука: {$webhook['url']}\n", Console::FG_GREEN);
$this->stdout("Ожидающие апдейты (pending): " . ($webhook['pending_update_count'] ?? 0) . "\n");
$this->stdout("Макс. соединений: " . ($webhook['max_connections'] ?? 'по умолчанию') . "\n");
if (!empty($webhook['last_error_date'])) {
$errorTime = date('Y-m-d H:i:s', $webhook['last_error_date']);
$this->stdout("Последняя ошибка ({$errorTime}): {$webhook['last_error_message']}\n", Console::FG_RED);
}
}
return ExitCode::OK;
} catch (\Exception $e) {
$this->stderr("Ошибка диагностики: " . $e->getMessage() . "\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}
}Webhook Management: Secure setWebhook with secret_token and Deletion
When setting up a webhook, it is critical to protect your controller's endpoint from unauthorized requests. Starting with API version 6.1, Telegram supports the secret_token parameter. This token is passed in the X-Telegram-Bot-Api-Secret-Token header with every incoming webhook. If the header does not match the value generated during setup, your controller should immediately return a 403 Forbidden HTTP response.
Let's implement the webhook setup and deletion methods in the console command. During setup, we will automatically generate a cryptographically secure token.
/**
* Установка Webhook для бота с генерацией secret_token
* @param string $url Полный HTTPS URL вашего вебхука
*/
public function actionSet(string $url)
{
if (!filter_var($url, FILTER_VALIDATE_URL) || !str_starts_with($url, 'https://')) {
$this->stderr("Ошибка: URL должен использовать безопасный протокол HTTPS.\n", Console::FG_RED);
return ExitCode::DATAERR;
}
// Генерируем случайный secret_token (16-32 символа)
$secretToken = bin2hex(random_bytes(16));
try {
$this->stdout("Установка вебхука на URL: {$url}... ", Console::FG_YELLOW);
$this->sendRequest('setWebhook', [
'url' => $url,
'secret_token' => $secretToken,
'max_connections' => 40,
'allowed_updates' => ['message', 'callback_query', 'my_chat_member']
]);
$this->stdout("Успешно!\n", Console::FG_GREEN);
$this->stdout("\nВНИМАНИЕ! Сохраните сгенерированный secret_token в .env или params.local.php:\n", Console::FG_YELLOW);
$this->stdout("TELEGRAM_SECRET_TOKEN={$secretToken}\n\n", Console::FG_BOLD);
return ExitCode::OK;
} catch (\Exception $e) {
$this->stderr("Ошибка установки вебхука: " . $e->getMessage() . "\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}
}
/**
* Удаление вебхука и перевод бота в режим Long Polling
* @param bool $dropUpdates Сбросить ли все накопившиеся обновления в очереди Telegram
*/
public function actionDelete(bool $dropUpdates = false)
{
try {
$this->stdout("Удаление вебхука... ", Console::FG_YELLOW);
$this->sendRequest('deleteWebhook', [
'drop_pending_updates' => $dropUpdates
]);
$this->stdout("Успешно удален!\n", Console::FG_GREEN);
return ExitCode::OK;
} catch (\Exception $e) {
$this->stderr("Ошибка удаления: " . $e->getMessage() . "\n\