Надання адміністративних операцій, таких як налаштування кінцевих точок вебхуків або очищення черг оновлень, через загальнодоступні вебмаршрути створює непотрібний ризик для безпеки. Для розкриття вебмаршрутів потрібне складне проміжне програмне забезпечення для автентифікації, щоб несанкціоновані треті сторони не перенаправляли вебхук на шкідливий сервер або не наводнювали кінцеві точки. Виконання адміністративних завдань через консольний інтерфейс Yii2 усуває цю поверхню атаки, використовує аргументи командного рядка та легко інтегрується в конвеєри розгортання та системні планувальники (наприклад, таймери crontab або systemd).
\nУ цьому посібнику показано, як створити спеціальний консольний контролер Yii2 для управління основними операціями API бота Telegram: проведення перевірок працездатності API (getMe), реєстрація та скасування реєстрації вебхуків (setWebhook, deleteWebhook) і виконання ротації журналів пакетної бази даних для оброблених журналів оновлень. Цей робочий процес не залежить від залежностей стороннього Telegram SDK, натомість використовуються незмінні виклики cURL для забезпечення повного контролю над обробкою тайм-ауту, перевіркою статусу HTTP та аналізом помилок.
Архітектурні обов'язки
\nКонсольний контролер обробляє лише адміністративні процедури: 1. Прямий зв'язок із кінцевими точками HTTPS API Telegram з використанням ізольованої транспортної логіки. 2. Отримання параметрів оточення (Токен бота, URL вебхуків, WEBHOOK_SECRET_TOKEN). 3. Безпечне видалення прострочених записів журналу в обмежених транзакціях бази даних, щоб уникнути блокування в таблицях із високою пропускною здатністю.
Він не обробляє аналіз вхідних оновлень у реальному часі або управління життєвим циклом HTTP-запитів, які залишаються у віданні вебконтролерів або асинхронних споживачів черг.
\nРеалізація консольного контролера
\nСтворіть командний файл CLI за адресою commands/TelegramController.php. Цей клас успадковується від yii\\console\\Controller і надає загальнодоступні методи, зіставлені з підкомандами CLI.
<?php
namespace app\commands;
use Yii;
use yii\console\Controller;
use yii\console\ExitCode;
use yii\db\Query;
class TelegramController extends Controller
{
public string $token = '';
public string $webhookUrl = '';
public string $secretToken = '';
public function init(): void
{
parent::init();
// Retrieve credentials from environment or Yii params
$this->token = (string) (getenv('TELEGRAM_BOT_TOKEN') ?: Yii::$app->params['telegramBotToken'] ?? '');
$this->webhookUrl = (string) (getenv('TELEGRAM_WEBHOOK_URL') ?: Yii::$app->params['telegramWebhookUrl'] ?? '');
$this->secretToken = (string) (getenv('TELEGRAM_SECRET_TOKEN') ?: Yii::$app->params['telegramSecretToken'] ?? '');
}
/**
* Health-check: Calls getMe to verify bot token validity.
*/
public function actionHealthCheck(): int
{
$this->stdout("Checking Telegram Bot API Connectivity...\n");
$response = $this->sendApiRequest('getMe');
if (!$response['ok']) {
$this->stderr("ERROR: Failed to connect. Reason: " . ($response['description'] ?? 'Unknown error') . "\n");
return ExitCode::UNSPECIFIED_ERROR;
}
$bot = $response['result'];
$this->stdout(sprintf("SUCCESS: Connected as @%s (ID: %d)\n", $bot['username'], $bot['id']));
return ExitCode::OK;
}
/**
* Register the public HTTP webhook endpoint with Telegram API.
*/
public function actionSetWebhook(?string $url = null, bool $dropPending = false): int
{
$targetUrl = $url ?? $this->webhookUrl;
if (empty($targetUrl)) {
$this->stderr("ERROR: Webhook URL is missing. Provide it via argument or config.\n");
return ExitCode::DATAERR;
}
$payload = [
'url' => $targetUrl,
'drop_pending_updates' => $dropPending,
'allowed_updates' => ['message', 'callback_query', 'my_chat_member'],
];
if (!empty($this->secretToken)) {
$payload['secret_token'] = $this->secretToken;
}
$this->stdout("Registering Webhook URL: {$targetUrl}...\n");
$response = $this->sendApiRequest('setWebhook', $payload);
if (!$response['ok']) {
$this->stderr("ERROR: setWebhook failed: " . ($response['description'] ?? 'Unknown') . "\n");
return ExitCode::UNSPECIFIED_ERROR;
}
$this->stdout("SUCCESS: Webhook successfully registered.\n");
return ExitCode::OK;
}
/**
* Remove registered webhook from Telegram API.
*/
public function actionDeleteWebhook(bool $dropPending = false): int
{
$payload = [
'drop_pending_updates' => $dropPending,
];
$this->stdout("Deleting Webhook registration...\n");
$response = $this->sendApiRequest('deleteWebhook', $payload);
if (!$response['ok']) {
$this->stderr("ERROR: deleteWebhook failed: " . ($response['description'] ?? 'Unknown') . "\n");
return ExitCode::UNSPECIFIED_ERROR;
}
$this->stdout("SUCCESS: Webhook deleted successfully.\n");
return ExitCode::OK;
}
/**
* Purge update logs older than N days in small chunks.
*/
public function actionRotateLogs(int $days = 14, int $batchSize = 1000): int
{
if ($days < 1) {
$this->stderr("ERROR: Days must be an integer greater than 0.\n");
return ExitCode::DATAERR;
}
$cutoffDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
$this->stdout("Rotating updates logged before {$cutoffDate}...\n");
$db = Yii::$app->db;
$totalDeleted = 0;
do {
// Delete in batches to prevent long table locks
$deletedCount = $db->createCommand()
->delete('telegram_update_log', 'created_at < :cutoff', [':cutoff' => $cutoffDate])
->execute();
// Note: If using SQL databases without LIMIT support in DELETE queries,
// delete via subquery matching primary keys:
/*
$subQuery = (new Query())
->select('id')
->from('telegram_update_log')
->where(['<', 'created_at', $cutoffDate])
->limit($batchSize);
$deletedCount = $db->createCommand()
->delete('telegram_update_log', ['id' => $subQuery])
->execute();
*/
$totalDeleted += $deletedCount;
$this->stdout("Deleted chunk of {$deletedCount} logs...\n");
} while ($deletedCount >= $batchSize);
$this->stdout("Log rotation complete. Total records removed: {$totalDeleted}\n");
return ExitCode::OK;
}
/**
* Execute direct cURL request to Telegram Bot API with error handling.
*/
private function sendApiRequest(string $method, array $params = []): array
{
if (empty($this->token)) {
return [
'ok' => false,
'description' => 'Bot token is empty. Ensure TELEGRAM_BOT_TOKEN environment variable is set.',
];
}
$url = "https://api.telegram.org/bot{$this->token}/{$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,
]);
$rawResponse = curl_exec($ch);
$curlErrno = curl_errno($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($curlErrno !== 0) {
return [
'ok' => false,
'description' => "cURL Error ({$curlErrno}): {$curlError}",
];
}
$decoded = json_decode((string) $rawResponse, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return [
'ok' => false,
'description' => 'Failed to parse JSON response. HTTP Code: ' . $httpCode,
];
}
return $decoded;
}
}\nВиконання команд через інтерфейс командного рядка
\nЩоб виконати ці дії на сервері, використовуйте двійковий файл початкового завантаження Yii CLI за замовчуванням Yii з кореня вашого репозиторію застосунків.
# Verify bot account connectivity
php yii telegram/health-check
# Register public webhook endpoint
php yii telegram/set-webhook "https://example.com/telegram/webhook"
# Register webhook and drop any backlog of pending updates
php yii telegram/set-webhook "https://example.com/telegram/webhook" 1
# Remove webhook registration
php yii telegram/delete-webhook
# Purge updates logged more than 30 days ago
php yii telegram/rotate-logs 30\nКонфігурація застосунку
\nПереконайтеся, що ваші файли конфігурації безпечно передають облікові дані в консольне середовище, не фіксуючи конфіденційні сирі рядки в репозиторії. Змініть config/params.php або config/console.php для отримання параметрів конфігурації під час виконання.
// config/params.php
return [
'telegramBotToken' => getenv('TELEGRAM_BOT_TOKEN') ?: '',
'telegramWebhookUrl' => getenv('TELEGRAM_WEBHOOK_URL') ?: '',
'telegramSecretToken' => getenv('TELEGRAM_SECRET_TOKEN') ?: '',
];\nСекретні токени Webhook та ізоляція оновлення
\nПід час встановлення вебхука за допомогою actionSetWebhook передача рядка секретний токен (від 1 до 256 символів з використанням (?(?=[^a-z]*[a-z])) призводить до того, що Telegram додає заголовок X-Telegram-Bot-Api-Secret-Token для кожного вхідного HTTP-запиту, надісланого на ваш URL вебхука.
У вашому вебконтролері кінцевих точок (наприклад, controllers/TelegramController.php) ви повинні перевірити цей заголовок перед читанням корисного навантаження запиту:
public function actionWebhook()
{
$receivedSecret = Yii::$app->request->getHeaders()->get('X-Telegram-Bot-Api-Secret-Token');
$expectedSecret = Yii::$app->params['telegramSecretToken'];
if (empty($expectedSecret) || !hash_equals($expectedSecret, (string) $receivedSecret)) {
Yii::$app->response->statusCode = 403;
return 'Unauthorized';
}
// Parse update payload...
}\nАвтоматична ротація журналу за допомогою системного планувальника
\nПід час обробки оновлень боти з високим трафіком зберігають необроблене введення JSON у таблиці бази даних для аудиту та асинхронної обробки. З часом такі таблиці, як telegram_update_log, накопичують мільйони рядків, що призводить до зниження швидкості індексування та високого використання сховища.
Виконання ротації журналу всередині системного cron запобігає роздуванню бази даних. Додайте команду до crontab вашого сервера (crontab -e):
# Purge logs older than 14 days every night at 02:00 AM
0 2 * * * /usr/bin/php /var/www/html/yii telegram/rotate-logs 14 > /dev/null 2>&1\nВикористання фрагментованих пакетних видалень усередині actionRotateLogs запобігає тривалим блокуванням транзакцій у таблицях бази даних, дозволяючи одночасним запитам на запис від вхідних вебхуків завершуватися без тайм-ауту.
Створення великомасштабних інтеграцій часто вимагає розширення робочих процесів управління API для обробки структурованих корисних навантажень, перевірки автентичності мінідодатків та автоматизованих конвеєрів розгортання. Якщо вам потрібна спеціальна підтримка розробки або власні архітектурні рішення для платформ Telegram, зверніться до BotCreator — студії, яка розробляє ботів Telegram/мінідодатки. Для отримання детальної інформації про низькорівневі конструкції корисного навантаження ознайомтеся з офіційною документацією за адресою
"