Managing a Telegram bot in production requires dedicated administrative tooling. Relying on manual cURL requests or web-based administration panels to register webhooks, check bot status, or clean up debug logs introduces security risks and operational friction.
This tutorial shows how to build a Yii2 console command (commands/TelegramController.php) to handle essential bot maintenance tasks: verifying bot credentials (getMe), registering webhooks with secure tokens (setWebhook), removing webhooks (deleteWebhook), and rotating local update logs.
We do not cover the webhook controller itself, queue processing, or database storage for incoming updates.
Configuration Setup
First, configure your Telegram bot credentials. Avoid hardcoding tokens. Instead, use environment variables or Yii2's application parameters. Add these values to your config/params.php or load them via getenv() in your configuration files:
<?php
return [
'telegram' => [
'token' => getenv('TELEGRAM_BOT_TOKEN'),
'secret_token' => getenv('TELEGRAM_SECRET_TOKEN'),
],
];
*Note: The secret_token is an arbitrary string (1-256 characters, containing A-Z, a-z, 0-9, _, and -) sent by Telegram in the X-Telegram-Bot-Api-Secret-Token header. This allows your webhook controller to verify that incoming requests originate from Telegram.*
The Console Controller
Create the file commands/TelegramController.php. This controller extends yii\console\Controller and implements the maintenance actions.
<?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 = '';
private string $secretToken = '';
public function init()
{
parent::init();
$this->token = (string) (getenv('TELEGRAM_BOT_TOKEN') ?: Yii::$app->params['telegram']['token'] ?? '');
$this->secretToken = (string) (getenv('TELEGRAM_SECRET_TOKEN') ?: Yii::$app->params['telegram']['secret_token'] ?? '');
if (empty($this->token)) {
$this->stderr("Error: Telegram bot token is not configured.\n", Console::FG_RED);
Yii::$app->end(ExitCode::CONFIG);
}
}
/**
* Health check: Retrieve bot information from Telegram.
*/
public function actionGetMe(): int
{
$this->stdout("Checking bot identity...\n", Console::FG_YELLOW);
$result = $this->sendRequest('getMe');
if ($result) {
$this->stdout("Success! Bot Username: @{$result['username']} (ID: {$result['id']})\n", Console::FG_GREEN);
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Register the webhook URL with Telegram.
*/
public function actionSetWebhook(string $url): int
{
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->stderr("Error: Invalid URL provided.\n", Console::FG_RED);
return ExitCode::DATAERR;
}
$this->stdout("Setting webhook to: {$url}...\n", Console::FG_YELLOW);
$params = ['url' => $url];
if (!empty($this->secretToken)) {
$params['secret_token'] = $this->secretToken;
}
$result = $this->sendRequest('setWebhook', $params);
if ($result) {
$this->stdout("Webhook successfully configured.\n", Console::FG_GREEN);
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Delete the registered webhook.
*/
public function actionDeleteWebhook(): int
{
$this->stdout("Deleting webhook...\n", Console::FG_YELLOW);
$result = $this->sendRequest('deleteWebhook');
if ($result) {
$this->stdout("Webhook successfully deleted.\n", Console::FG_GREEN);
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Rotate daily update log files older than N days.
*/
public function actionRotateLogs(int $days = 7): int
{
$logDir = Yii::getAlias('@runtime/logs');
$this->stdout("Scanning for Telegram update logs older than {$days} days in {$logDir}...\n", Console::FG_YELLOW);
if (!is_dir($logDir)) {
$this->stderr("Error: Log directory does not exist.\n", Console::FG_RED);
return ExitCode::IOERR;
}
$pattern = $logDir . DIRECTORY_SEPARATOR . 'telegram-updates-*.json';
$files = glob($pattern);
$now = time();
$deletedCount = 0;
foreach ($files as $file) {
if (!is_file($file)) {
continue;
}
$mtime = filemtime($file);
$ageDays = ($now - $mtime) / 86400;
if ($ageDays > $days) {
if (unlink($file)) {
$this->stdout("Deleted: " . basename($file) . " (Age: " . round($ageDays, 1) . " days)\n", Console::FG_GREY);
$deletedCount++;
} else {
$this->stderr("Failed to delete: " . basename($file) . "\n", Console::FG_RED);
}
}
}
$this->stdout("Log rotation complete. Deleted {$deletedCount} file(s).\n", Console::FG_GREEN);
return ExitCode::OK;
}
/**
* Internal helper to execute Telegram Bot API requests.
*/
private function sendRequest(string $method, array $params = []): ?array
{
$url = "https://api.telegram.org/bot{$this->token}/{$method}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($response === false) {
$this->stderr("cURL Error: {$curlError}\n", Console::FG_RED);
return null;
}
if ($httpCode !== 200) {
$this->stderr("HTTP Error: Received status code {$httpCode}\n", Console::FG_RED);
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->stderr("JSON Decode Error: " . json_last_error_msg() . "\n", Console::FG_RED);
return null;
}
if (!isset($data['ok']) || !$data['ok']) {
$description = $data['description'] ?? 'Unknown error';
$this->stderr("Telegram API Error: {$description}\n", Console::FG_RED);
return null;
}
return $data['result'];
}
}
Usage Instructions
Once the controller is in place, you can run these commands directly from your terminal inside the project root directory.
#### 1. Verify Bot Credentials Run the health check to verify that your token is valid and that the application can reach the Telegram API:
php yii telegram/get-me
#### 2. Register Webhook To register your webhook endpoint, pass the public HTTPS URL of your controller action:
php yii telegram/set-webhook "https://yourdomain.com/telegram/webhook"
#### 3. Delete Webhook If you need to switch back to long polling (getUpdates) during local debugging, remove the webhook:
php yii telegram/delete-webhook
#### 4. Rotate Update Logs Assuming your webhook controller writes raw incoming payloads to daily files like @runtime/logs/telegram-updates-2023-10-27.json, you can clean up files older than 14 days:
php yii telegram/rotate-logs 14
Production Considerations
To automate log rotation, configure a system cron job on your staging or production server to run the command daily:
0 2 * * * /usr/bin/php /var/www/my-bot/yii telegram/rotate-logs 7 > /dev/null 2>&1
This keeps your disk usage predictable without requiring external logrotate configurations for application-specific JSON payloads.
For more details on managing webhooks and handling incoming payloads, refer to the official Telegram Bot API documentation at https://botservice.biz/telegram-bot-api.
BotCreator — studio that ships Telegram bots / Mini Apps.