PHP і фреймворки
Yii2
В Yii2 интеграция с Telegram Bot API строится на двух элементах: компоненте приложения (для исходящих запросов) и контроллере (для входящих обновлений через Webhook). Подход повторяет паттерн из статьи HTTP-клиент на PHP — выносим сетевую логику в переиспользуемый класс, а в контроллере только парсим update и вызываем бизнес-логику.
Компонент приложения: обёртка над TelegramClient
В главе HTTP-клиент на PHP мы сделали класс TelegramClient с ретраями, таймаутами и логированием. В Yii2 его удобно обернуть в компонент, чтобы вызывать Yii::$app->telegram->sendMessage([...]) из любого места. Положите файл в components/Telegram.php:
<?php
namespace app\components;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
/**
* Компонент-обёртка над TelegramClient.
* Токен читаем из env — так безопаснее и удобнее для CI/CD.
*/
final class Telegram extends Component
{
public string $token = '';
public int $timeout = 15;
public bool $debug = false;
private \App\TelegramClient $client;
public function init(): void
{
parent::init();
if (empty($this->token)) {
throw new InvalidConfigException('Telegram token is not configured. Set TELEGRAM_BOT_TOKEN in .env');
}
$this->client = new \App\TelegramClient(
token: $this->token,
timeout: $this->timeout,
debug: $this->debug
);
}
/** Отправка сообщения — самый частый метод */
public function sendMessage(array $params): array
{
return $this->client->call('sendMessage', $params);
}
/** Универсальный вызов любого метода Bot API */
public function call(string $method, array $params = []): array
{
return $this->client->call($method, $params);
}
/** Удобный хелпер для ответа на callback_query */
public function answerCallbackQuery(string $callbackQueryId, string $text = '', bool $showAlert = false): array
{
return $this->client->call('answerCallbackQuery', [
'callback_query_id' => $callbackQueryId,
'text' => $text,
'show_alert' => $showAlert,
]);
}
}
Обратите внимание: конструктор TelegramClient принимает именованные аргументы (PHP 8.0+). Если у вас старая версия — передавайте позиционно.
Конфигурация в config/web.php
Добавьте компонент в секцию components. Токен подставляется из переменной окружения — так он не попадает в репозиторий:
// config/web.php (basic) или common/config/main.php (advanced)
return [
'components' => [
// ...
'telegram' => [
'class' => 'app\components\Telegram',
'token' => getenv('TELEGRAM_BOT_TOKEN') ?: '',
'timeout' => 10,
'debug' => YII_DEBUG, // логируем запросы в dev-режиме
],
],
];
Проверьте, что компонент загрузился: в консоли php yii telegram/test (см. ниже) или просто var_dump(Yii::$app->telegram->call('getMe')); в любом экшене.
Контроллер Webhook: приём обновлений
Telegram шлёт POST с JSON-телом. В Yii2 нужно отключить CSRF-валидацию для этого экшена — у Telegram нет ваших кук. Создайте controllers/TelegramController.php:
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\helpers\Json;
class TelegramController extends Controller
{
// Telegram не знает ваш CSRF-токен
public $enableCsrfValidation = false;
public function actionWebhook(): array
{
Yii::$app->response->format = Response::FORMAT_JSON;
$rawBody = Yii::$app->request->getRawBody();
if (empty($rawBody)) {
Yii::warning('Empty webhook payload');
return ['ok' => false, 'error' => 'Empty payload'];
}
$update = Json::decode($rawBody);
if (json_last_error() !== JSON_ERROR_NONE) {
Yii::error('Invalid JSON in webhook: ' . json_last_error_msg());
return ['ok' => false, 'error' => 'Invalid JSON'];
}
// Логируем входящий update для отладки
if (YII_DEBUG) {
Yii::info('Telegram update: ' . Json::encode($update, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
// Делегируем обработку сервису/хендлеру
$result = $this->handleUpdate($update);
// Telegram ждёт 200 OK и любой JSON — мы возвращаем статус обработки
return ['ok' => true, 'processed' => $result];
}
private function handleUpdate(array $update): bool
{
if (!isset($update['message'])) {
return false; // не сообщение — игнорируем
}
$message = $update['message'];
$chatId = $message['chat']['id'];
$text = $message['text'] ?? '';
$userId = $message['from']['id'] ?? null;
// Пример: эхо-бот с командой /start
if ($text === '/start') {
$resp = Yii::$app->telegram->sendMessage([
'chat_id' => $chatId,
'text' => "Привет! Я бот на Yii2. Твой ID: {$userId}",
'parse_mode' => 'HTML',
]);
$this->logApiResponse('sendMessage', $resp);
return true;
}
// Эхо для любого текста
if ($text !== '') {
$resp = Yii::$app->telegram->sendMessage([
'chat_id' => $chatId,
'text' => "Вы написали: " . htmlspecialchars($text),
]);
$this->logApiResponse('sendMessage', $resp);
return true;
}
return false;
}
private function logApiResponse(string $method, array $response): void
{
if (!YII_DEBUG) return;
if (($response['ok'] ?? false) === true) {
Yii::info("$method OK: " . Json::encode($response['result'] ?? []));
} else {
Yii::error("$method FAILED: " . Json::encode($response));
}
}
}
Как выглядит ответ Telegram API
После каждого вызова sendMessage (или любого метода) вы получаете массив. В дебаге смотрите:
{
"ok": true,
"result": {
"message_id": 142,
"from": {"id": 123456789, "is_bot": true, "first_name": "MyBot", "username": "mybot"},
"chat": {"id": 987654321, "first_name": "Ivan", "type": "private"},
"date": 1700000000,
"text": "Привет! Я бот на Yii2. Твой ID: 987654321"
}
}
- ok —
trueзначит успех,false— ошибка API (см.description,error_code). - result — объект
Message(для sendMessage) или другой тип в зависимости от метода. - При ошибке:
{"ok":false,"error_code":400,"description":"Bad Request: chat not found"}.
В коде выше метод logApiResponse пишет в лог Yii только result при успехе и весь ответ при ошибке — так не засоряете логи лишним.
Маршрутизация и секретный токен в URL
Как описано в Webhook, URL вебхука должен быть неугадываемым. В Yii2 настраивается через urlManager:
// config/web.php
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
// Секретная часть генерируется один раз и хранится в .env
'telegram-webhook-' . getenv('TELEGRAM_WEBHOOK_SECRET') => 'telegram/webhook',
],
],
],
Теперь публичный URL: https://yourdomain.com/telegram-webhook-SECRET123. Установите его через Bot API:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://yourdomain.com/telegram-webhook-SECRET123", "secret_token": "WEBHOOK_SECRET_FROM_ENV"}'
Параметр secret_token (не путать с частью URL) — дополнительная проверка: Telegram пришлёт заголовок X-Telegram-Bot-Api-Secret-Token, который можно проверить в контроллере перед обработкой.
Консольная команда для быстрой проверки
Удобно иметь команду, чтобы проверить токен и вебхук без браузера. Создайте commands/TelegramController.php (в console/controllers/ для basic или console/controllers/ для advanced):
<?php
namespace app\commands;
use Yii;
use yii\console\Controller;
use yii\helpers\Json;
class TelegramController extends Controller
{
public function actionTest(): int
{
$me = Yii::$app->telegram->call('getMe');
if (($me['ok'] ?? false) === true) {
$this->stdout("Bot OK: @" . ($me['result']['username'] ?? 'unknown') . "\n", Console::FG_GREEN);
return ExitCode::OK;
}
$this->stderr("getMe failed: " . Json::encode($me) . "\n", Console::FG_RED);
return ExitCode::UNSPECIFIED_ERROR;
}
public function actionWebhookInfo(): int
{
$info = Yii::$app->telegram->call('getWebhookInfo');
$this->stdout(Json::encode($info, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n");
return ExitCode::OK;
}
}
Запуск: php yii telegram/test и php yii telegram/webhook-info. Вывод getWebhookInfo покажет url, pending_update_count, last_error_date/last_error_message — главные индикаторы здоровья вебхука.
Очереди для тяжёлых задач
Если обработка обновления требует времени (внешние API, генерация отчётов), не делайте это в вебхуке — Telegram ждёт ответ максимум несколько секунд. Используйте yii2-queue (redis, db, file драйверы):
// В handleUpdate вместо прямой отправки:
Yii::$app->queue->push(new \app\jobs\ProcessTelegramUpdateJob([
'update' => $update,
]));
// Сразу возвращаем 200 OK Telegram
return ['ok' => true, 'queued' => true];
Джоб ProcessTelegramUpdateJob выполняется асинхронно, имеет доступ к Yii::$app->telegram и может делать ретраи через настройки очереди.
Дальше: Laravel