PHP і фреймворки
Laravel
Laravel позволяет интегрировать Telegram Bot API двумя способами: через встроенный HTTP-клиент (обёртку над Guzzle) или готовые пакеты вроде irazasyed/telegram-bot-sdk. Ниже — нативный вариант без лишних зависимостей, максимально близкий к подходу из статьи HTTP-клиент на PHP. Мы вынесем сетевую логику в сервис-класс, а контроллер оставим только для парсинга update и вызова бизнес-логики.
Сервис-класс для исходящих запросов
Создайте app/Services/TelegramBot.php. В конструкторе читаем токен из конфига (он берётся из getenv('TELEGRAM_BOT_TOKEN') через config/services.php). Метод call() — универсальная обёртка: делает POST к api.telegram.org/bot<token>/<method>, проверяет HTTP-код, json_last_error и поле ok в ответе. При ошибке выбрасывает RuntimeException с понятным сообщением.
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
class TelegramBot
{
protected string $token;
protected string $baseUrl;
public function __construct(?string $token = null)
{
$this->token = $token ?? config('services.telegram.token');
$this->baseUrl = "https://api.telegram.org/bot{$this->token}";
}
/**
* Универсальный вызов метода Bot API.
* @throws \RuntimeException при сетевой ошибке или ok=false
*/
public function call(string $method, array $params = []): array
{
try {
$response = Http::timeout(15)
->retry(3, 100)
->post("{$this->baseUrl}/{$method}", $params)
->throw()
->json();
} catch (ConnectionException $e) {
throw new \RuntimeException("Telegram API connection failed: " . $e->getMessage());
} catch (RequestException $e) {
$body = $e->response?->json();
$desc = $body['description'] ?? $e->getMessage();
throw new \RuntimeException("Telegram API error: {$desc}");
}
if (empty($response['ok'])) {
throw new \RuntimeException("Telegram API returned ok=false: " . ($response['description'] ?? 'unknown'));
}
return $response['result'];
}
public function sendMessage(int|string $chatId, string $text, array $options = []): array
{
return $this->call('sendMessage', array_merge([
'chat_id' => $chatId,
'text' => $text,
'parse_mode' => 'HTML',
], $options));
}
public function answerCallbackQuery(string $callbackQueryId, ?string $text = null, bool $showAlert = false): array
{
return $this->call('answerCallbackQuery', [
'callback_query_id' => $callbackQueryId,
'text' => $text,
'show_alert' => $showAlert,
]);
}
}
Пример ответа API и как его посмотреть
Метод sendMessage возвращает объект Message. В коде можно проверить результат так:
<?php
$result = $bot->sendMessage(123456789, "Тестовое сообщение");
// Для отладки в консоли (php artisan tinker или job):
var_dump($result);
// Или красиво:
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
Типичный JSON-ответ (поле result):
{
"message_id": 42,
"date": 1700000000,
"chat": {
"id": 123456789,
"type": "private",
"first_name": "Ivan"
},
"text": "Тестовое сообщение"
}
Что смотреть: message_id — нужен для последующего редактирования/удаления (методы editMessageText, deleteMessage); chat.id — подтверждает, что сообщение ушло именно туда, куда планировали; date — unix-время отправки по серверу Telegram.
Конфигурация токена
Токен не хардкодим. В config/services.php читаем из окружения:
// config/services.php
return [
// ...
'telegram' => [
'token' => env('TELEGRAM_BOT_TOKEN'),
],
];
# .env
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
Проверить, что токен подхватился, можно в Tinker:
php artisan tinker
> config('services.telegram.token')
Контроллер для входящих обновлений (Webhook)
Создайте app/Http/Controllers/TelegramWebhookController.php. Laravel автоматически декодирует JSON-тело в массив $request->all(). В главе Long polling мы делали функцию processUpdate — здесь логика та же, только внутри контроллера. Важно: всегда возвращать 200 OK с {"ok":true} в течение 1–2 секунд, иначе Telegram будет повторять доставку.
<?php
namespace App\Http\Controllers;
use App\Services\TelegramBot;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class TelegramWebhookController extends Controller
{
public function __invoke(Request $request, TelegramBot $bot): JsonResponse
{
$update = $request->all();
// Защита от пустых/некорректных запросов
if (!isset($update['update_id'])) {
return response()->json(['ok' => true]);
}
// --- Сообщения ---
if (isset($update['message'])) {
$msg = $update['message'];
$chatId = $msg['chat']['id'];
$text = $msg['text'] ?? '';
// В главе "Команды /start, /help" мы делали parseCommand — используем тот же подход
if (str_starts_with($text, '/start')) {
$bot->sendMessage($chatId, "Привет! Я бот на Laravel.\nНапиши /help для списка команд.");
} elseif ($text === '/help') {
$bot->sendMessage($chatId, "Доступные команды: /start, /help");
}
}
// --- Callback queries (inline-кнопки) ---
if (isset($update['callback_query'])) {
$cq = $update['callback_query'];
// В главе "callback_query" мы разбирали answerCallbackQuery — здесь вызываем метод сервиса
$bot->answerCallbackQuery($cq['id'], "Нажата кнопка: {$cq['data']}");
}
return response()->json(['ok' => true]);
}
}
Маршрут и регистрация Webhook
В routes/web.php (или api.php) добавьте маршрут. Параметр {token} в URL — простая защита от случайных запросов; в контроллере его можно проверить против config('services.telegram.token').
use App\Http\Controllers\TelegramWebhookController;
Route::post('/telegram/webhook/{token}', TelegramWebhookController::class)
->where('token', '.*');
Установите webhook разово — через Artisan-команду (удобно для деплоя) или в Tinker. Команда использует наш сервис TelegramBot и метод setWebhook.
<?php
namespace App\Console\Commands;
use App\Services\TelegramBot;
use Illuminate\Console\Command;
class SetupTelegramWebhook extends Command
{
protected $signature = 'app:setup-telegram-webhook';
protected $description = 'Set Telegram webhook URL';
public function handle(TelegramBot $bot): int
{
$url = config('app.url') . '/telegram/webhook/' . config('services.telegram.token');
$result = $bot->call('setWebhook', [
'url' => $url,
'drop_pending_updates' => true, // чистим старые обновления при деплое
'allowed_updates' => ['message', 'callback_query'], // только нужные типы
]);
// Результат setWebhook — просто true при успехе
$this->info("Webhook set: " . ($result === true ? 'ok' : 'failed'));
// Проверить текущий вебхук можно так:
$info = $bot->call('getWebhookInfo');
$this->line('Current webhook: ' . json_encode($info, JSON_PRETTY_PRINT));
return self::SUCCESS;
}
}
Запуск: php artisan app:setup-telegram-webhook. В выводе увидите JSON от getWebhookInfo — проверьте поля url, pending_update_count, last_error_date/last_error_message.
Использование сервиса в приложении (Jobs, Listeners, Controllers)
Сервис автоматически резолвится через контейнер. Для отправки уведомлений из фоновых задач оформите Job с implements ShouldQueue — Laravel сам сделает ретраи при исключениях из TelegramBot::call().
<?php
namespace App\Jobs;
use App\Services\TelegramBot;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class NotifyUser implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
protected int|string $chatId,
protected string $message
) {}
public function handle(TelegramBot $bot): void
{
$bot->sendMessage($this->chatId, $this->message);
}
}
// Диспатч из любого места:
// NotifyUser::dispatch(123456789, 'Ваш заказ #123 принят!');
Настройте ретраи в config/queue.php (retry_after) и запускайте воркера с флагом --tries=3. При сетевой ошибке или ok=false Job упадёт в failed_jobs после исчерпания попыток.
Отладка: как посмотреть, что пришло от Telegram
В разработке удобно логировать весь update. Добавьте в начало контроллера:
<?php
// ... внутри __invoke
\Log::channel('telegram')->info('Incoming update', [
'update_id' => $update['update_id'] ?? null,
'payload' => $update,
]);
В config/logging.php добавьте канал:
// config/logging.php
'channels' => [
// ...
'telegram' => [
'driver' => 'daily',
'path' => storage_path('logs/telegram.log'),
'level' => 'info',
'days' => 14,
],
],
Теперь tail -f storage/logs/telegram-*.log покажет структуру каждого update — удобно для понимания полей message, callback_query, inline_query и др.
Дальше: БД и состояния (FSM)