Skip to content

Middleware

Middleware allows you to execute code before or after processing an update. This is useful for authorization, logging, state checking, and other tasks.

All middleware must implement the TelegramRouteMiddlewareInterface interface:

use HybridGram\Core\Middleware\TelegramRouteMiddlewareInterface;
use Phptg\BotApi\Type\Update\Update;
class MyMiddleware implements TelegramRouteMiddlewareInterface
{
public function handle(Update $update, \Closure $next): mixed
{
// Code before processing
$result = $next($update);
// Code after processing
return $result;
}
}

Automatically authorizes users through Telegram Guard:

use HybridGram\Http\Middlewares\AuthTelegramRouteMiddleware;
TelegramRouter::forBot('main')
->onCommand('/profile', function(CommandData $data) {
// User is already authorized
$user = Auth::user();
})
->middleware(AuthTelegramRouteMiddleware::class);

Sets state after handler execution:

use HybridGram\Http\Middlewares\SetStateTelegramRouteMiddleware;
TelegramRouter::onCommand('/start', function(CommandData $data) {
// ...
})
->middleware(new SetStateTelegramRouteMiddleware(
newState: 'main_menu',
useUserState: false, // Use chat state (default)
ttl: 3600, // Lifetime in seconds (optional)
data: ['step' => 1] // Additional data (optional)
));

Checks if chat/user is in a specific state:

use HybridGram\Http\Middlewares\CheckStateTelegramRouteMiddleware;
TelegramRouter::onTextMessage(function(TextMessageData $data) {
// This route triggers only if chat is in 'awaiting_input' state
})
->middleware(new CheckStateTelegramRouteMiddleware(
requiredStates: ['awaiting_input'],
useUserState: false, // Check chat state
exceptMode: false // false = only if IN state, true = only if NOT in state
));

Example with state exclusion:

// Route triggers only if NOT in 'processing' or 'awaiting' states
TelegramRouter::onCommand('/cancel', function(CommandData $data) {
// ...
})
->middleware(new CheckStateTelegramRouteMiddleware(
requiredStates: ['processing', 'awaiting'],
exceptMode: true // Exclusion mode
));

Limits request frequency from user:

use HybridGram\Http\Middlewares\RateLimitTelegramRouteMiddleware;
TelegramRouter::onTextMessage(function(TextMessageData $data) {
// ...
})
->middleware(new RateLimitTelegramRouteMiddleware(
maxAttempts: 10,
decayMinutes: 1
));

Automatically sets Laravel locale based on Telegram user language:

use HybridGram\Http\Middlewares\SetLocaleTelegramRouteMiddleware;
TelegramRouter::forBot('main')
->onCommand('/start', function(CommandData $data) {
// Locale is already set based on user language
return __('welcome_message');
})
->middleware(new SetLocaleTelegramRouteMiddleware(
supportedLocales: ['en', 'ru', 'uk', 'pt'],
fallbackLocale: 'en'
));

You can provide custom logic for determining the locale using the userLocale parameter:

Using a Closure:

use HybridGram\Http\Middlewares\SetLocaleTelegramRouteMiddleware;
use Phptg\BotApi\Type\Update\Update;
// Determine locale from user database settings
TelegramRouter::forBot('main')
->onCommand('/start', function(CommandData $data) {
return __('welcome_message');
})
->middleware(new SetLocaleTelegramRouteMiddleware(
supportedLocales: ['en', 'ru', 'uk', 'pt'],
fallbackLocale: 'en',
userLocale: function(Update $update): ?string {
$user = UpdateHelper::getUserFromUpdate($update);
if (!$user) {
return null;
}
// Fetch locale from your database
$userModel = User::where('telegram_id', $user->id)->first();
return $userModel?->preferred_locale;
}
));

Using a static string:

// Force a specific locale for all users in this route
TelegramRouter::onCommand('/en_only', function(CommandData $data) {
// Always in English
})
->middleware(new SetLocaleTelegramRouteMiddleware(
userLocale: 'en'
));
TelegramRouter::onCommand('/admin', function(CommandData $data) {
// ...
})
->middleware(AuthTelegramRouteMiddleware::class);
TelegramRouter::onCommand('/admin', function(CommandData $data) {
// ...
})
->middleware([
AuthTelegramRouteMiddleware::class,
new RateLimitTelegramRouteMiddleware(maxAttempts: 5, decayMinutes: 1),
]);
TelegramRouter::group([
'botId' => 'main',
'middlewares' => [
AuthTelegramRouteMiddleware::class,
LoggingTelegramRouteMiddleware::class,
],
], function($router) {
$router->onCommand('/admin', function(CommandData $data) {
// Both middleware will be applied
});
$router->onCommand('/settings', function(CommandData $data) {
// Both middleware will be applied
});
});
<?php
namespace App\Telegram\Middleware;
use HybridGram\Core\Middleware\TelegramRouteMiddlewareInterface;
use HybridGram\Core\UpdateHelper;
use Phptg\BotApi\Type\Update\Update;
class AdminMiddleware implements TelegramRouteMiddlewareInterface
{
public function handle(Update $update, \Closure $next): mixed
{
$user = UpdateHelper::getUserFromUpdate($update);
if (!$user || !$this->isAdmin($user->id)) {
$telegram = app(\HybridGram\Telegram\TelegramBotApi::class);
$chat = UpdateHelper::getChatFromUpdate($update);
if ($chat) {
$telegram->sendMessage(
$chat->id,
'❌ You do not have permission to execute this command'
);
}
return null; // Stop execution
}
return $next($update);
}
private function isAdmin(int $userId): bool
{
// Your validation logic
return in_array($userId, config('telegram.admins', []));
}
}

Usage:

use App\Telegram\Middleware\AdminMiddleware;
TelegramRouter::onCommand('/admin', function(CommandData $data) {
// Access only for admins
})
->middleware(AdminMiddleware::class);

You can register global middleware in TelegramServiceProvider:

// In boot() method of your ServiceProvider
public function boot(): void
{
$middlewareManager = app(\HybridGram\Core\Middleware\MiddlewareManager::class);
$middlewareManager->addGlobalMiddleware(
LoggingTelegramRouteMiddleware::class
);
}

Global middleware applies to all routes.

Middleware executes in the following order:

  1. Global middleware (in registration order)
  2. Route group middleware
  3. Specific route middleware

Each middleware can:

  • Continue execution (return $next($update))
  • Stop execution (return null)
  • Modify data (pass modified Update)