Переклад цього гайду зʼявиться незабаром. Поки що текст доступний російською — перемкніться на RU або читайте нижче. Відкрити російську версію

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