Telegram SDK Integration in Laravel
This guide shows how to add Telegram Bot API support to a Laravel application without pulling in third‑party SDKs like nutgram or irazasyed. It uses Laravel’s native HTTP client, a webhook endpoint, and a queue for asynchronous message processing.
Why this approach?
- No magic – all logic lives in plain PHP/Laravel code.
-
Idempotent – each
update_idis tracked so duplicate messages don’t cause duplicates. - Testable – you can inject fake Telegram updates during testing.
- Scalable – queue jobs keep your request lifecycle decoupled from the HTTP response time.
1. Configuration
Store your bot credentials in environment variables (never hard‑coded).
# .env
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrSTU
TELEGRAM_CHAT_ID=12345
In config/telegram.php (optional helper):
// config/telegram.php
return [
'token' => env('TELEGRAM_BOT_TOKEN'),
'chat_id' => env('TELEGRAM_CHAT_ID'),
];
2. Webhook Route
Define a route that receives POST data from Telegram:
// routes/web.php
use Illuminate\HttpRequest;
use App\HttpMiddleware elegramMiddleware;
Route::post('/webhook', function (HttpRequest $request) {
// Verify the secret token if you added one
if (!hash_equals(env('TELEGRAM_SECRET'), $request->header('X-Telegram-Bot-Api-Secret'))) {
return response()->json(['error' => 'Unauthorized'], 401);
}
// Parse the incoming JSON
$payload = json_decode($request->all(), true);
// Validate basic shape
if (!isset($payload['message'])) {
return response()->json(['error' => 'Missing message'], 400);
}
// Dispatch the update to a queue
\\(function()\)
->processUpdate($payload)
->onQueue('telegram_updates');
return response()->json(['status' => 'received']);
});
3. Middleware & Initialization
A simple middleware ensures every request goes through Telegram-specific validation or logging.
// app/Http/Middleware/TelegramMiddleware.php
class TelegramMiddleware extends HttpMiddleware
{
public function handle(Request $request, Closure $next)
{
// You could log, rate-limit, or attach context here
$context = ['bot_token' => env('TELEGRAM_BOT_TOKEN')];
return $next();
}
}
Register it early in Kernel.php:
// App/Kernel.php
protected $middlewareGroups = [
'web' => [
\Illuminate\HttpMiddleware
equests(\Illuminate\Http
equests::only(\String::toArray(__MIME_TYPE))),
\TelegramMiddleware::class,
],
];
4. Queue Job – Processing Updates
The core logic runs inside a background job. Each update is checked for duplicates using its update_id.
// app/Jobs/TelegramUpdateJob.php
use Illuminateoundationsusackground::BackgroundJob;
use Illuminate
otifications
otification;
use App
otifications elegramNotification;
class TelegramUpdateJob extends BackgroundJob
{
protected $routeName = 'telegram.update';
public function handle(array $data)
{
$updateId = $data['update_id'];
$chatId = $data['chat_id'] ?? null;
$text = $data['message']['text'] ?? '';
$type = $data['message']['type'] ?? null;
// Idempotency check – skip if already processed
if ($this->alreadyProcessed($updateId)) {
return;
}
try {
// Create a notification (or push message directly)
telegramNotification::create($chatId, $text, $type)->send();
$this->markAsCompleted();
} catch (\Exception $e) {
// Log but do not retry automatically – let the caller decide
logger::error('Failed to process Telegram update #{$updateId}', $e);
// Optionally requeue or move to a dead-letter queue
}
}
private function alreadyProcessed(string $updateId): bool
{
// Store processed IDs in Redis, DB, or a file.
// For simplicity, a static array works for demo purposes.
return isset($processedUpdates[$updateId]);
}
// Persist processed IDs (implement according to your storage strategy)
public function recordProcessed(string $updateId): void
{
$processedUpdates[$updateId] = true;
}
}
Storing Processed IDs
Use a lightweight store. Here’s a simple in‑memory array for demonstration (swap for Redis/DB in production):
// config/telegram.php (add)
'processed_ids' => ['redis://localhost:6379/0'] // or [] for pure PHP
Then replace $this->alreadyProcessed() with a Redis SETNX call.
5. Testing with Fake Updates
Unit‑test the job by feeding it synthetic Telegram payloads. Use PHPUnit.
// tests/Feature/TelegramUpdateJobTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Log;
use Tests\TestCase;
class TelegramUpdateJobTest extends Tests\TestCase
{
use RefreshDatabase;
/** @test */
public function job_processes_a_text_message()
{
$job = new TelegramUpdateJob();
$payload = [
'update_id' => '123456789',
'chat_id' => '987654321',
'message' => [
'type' => 'text',
'text' => 'Hello from Laravel!',
],
'message_type' => 'edit', // optional
];
$job->handle($payload);
// Verify the notification was attempted
$this->assertNotNull(telegramNotification::find('987654321'));
}
/** @test */
public function job_skips_duplicate_update()
{
$job = new TelegramUpdateJob();
$payload = [
'update_id' => '111222333',
'chat_id' => '987654321',
'message' => [
'type' => 'text',
'text' => 'Duplicate message',
],
];
// First run – should succeed
$job->handle($payload);
// Second run with same ID – should be skipped
$job->handle($payload);
// No exception thrown
$this->assertTrue(true);
}
}
Run the tests:
php artisan test --filter=TelegramUpdateJobTest
6. Production Notes
| Concern | Recommendation |
|---|---|
| Secret verification | Compare the X-Telegram-Bot-Api-Secret header against a server-side hash. |
| Rate limiting | Throttle incoming webhook calls per IP or per chat to avoid abuse. |
| Storage | Replace the in‑memory $processed_ids array with Redis SETNX or a database table for durability across restarts. |
| Error handling | Log failures and optionally alert on high failure rates. Consider moving failed updates to a separate queue for manual inspection. |
| HTTPS | Always serve the webhook over HTTPS. Telegram requires it. |
| Minimum version | Ensure your Laravel version supports the latest HTTP client features (Illuminate\Http\Client). |
7. Quick Start Summary
- Add
TELEGRAM_BOT_TOKENandTELEGRAM_CHAT_IDto.env. - Define the
/webhookroute and verify the secret header. - Create a
TelegramUpdateJobthat checks for duplicates and dispatches notifications. - Register the job as a background task (Laravel handles queuing automatically).
- Write feature tests that feed synthetic Telegram updates to the job.
With this pattern you have a lightweight, testable, and production-ready Telegram integration that stays within the Laravel ecosystem.
For more details on building Telegram bots and Mini Apps, see BotCreator.
Top comments (0)