Looking to hire Laravel developers? Try LaraJobs

laravel-telegram-bot-router maintained by reyhanteam

Description
A Telegram bot routing system for Laravel.
Author
Last update
2026/09/01 17:06 (dev-master)
License
Downloads
75

Comments
comments powered by Disqus

Laravel Telegram Bot Router

Version 1.1.1

A Laravel package for routing Telegram bot updates with a Laravel-style routing system. Telegram routes live in routes/bot.php and are kept separate from Laravel HTTP routes.

routes/web.php  -> Laravel HTTP routes
routes/bot.php  -> Telegram bot routes

Current Features

  • ✅ Webhook
  • ✅ Polling
  • routes/bot.php
  • BOT::onCommand()
  • BOT::onText()
  • BOT::onCallbackQuery()
  • BOT::fallback()
  • BOT::onInvalid()
  • ✅ Closure handlers
  • ✅ Controller + method handlers
  • ✅ Laravel Service Container resolution
  • ✅ Controller Dependency Injection
  • ✅ Regular expression text matching
  • TelegramUpdate wrapper
  • ✅ Improved route matching
  • ✅ Route constraints
  • ✅ Command arguments
  • ✅ Route parameters
  • ✅ Route middleware
  • ✅ Global middleware
  • ✅ Middleware groups and nested groups
  • ✅ Middleware parameters
  • ✅ Middleware short-circuit
  • ✅ Conversation state
  • ✅ Per-user conversation storage
  • ✅ Conversation steps
  • ✅ Wait for next message
  • ✅ Conversation timeout
  • ✅ Conversation data

Version 1.1.5

What's included in v1.1.5

  • ✅ Better Route Matching
  • ✅ Route Constraints
  • ✅ Command Arguments
  • ✅ Route Parameters
  • ✅ Route Parameter access through TelegramUpdate
  • ✅ Multiple Route Parameters
  • ✅ Route Parameters with Constraints
  • ✅ Command Arguments are passed to Closure handlers as an array

Priority 1 — Core Telegram Routing is now complete.

Installation

composer require reyhanteam/laravel-telegram-bot-router

Publish the configuration:

php artisan vendor:publish --tag=telegram-bot-config

Publish the Telegram routes:

php artisan vendor:publish --tag=telegram-bot-routes

This creates:

routes/bot.php

Basic Routing

Use BOT in routes/bot.php:

use ReyhanTeam\TelegramBotRouter\TelegramBot as BOT;

Commands

BOT::onCommand('start', [StartController::class, 'index']);
BOT::onCommand('/help', [HelpController::class, 'index']);

The leading / is optional when registering a command.

Telegram bot usernames are ignored during matching:

/start@MyBot

matches:

BOT::onCommand('start', [StartController::class, 'index']);

Text

BOT::onText('hello', [MessageController::class, 'handle']);

Regular expressions are supported:

BOT::onText('/^hello/i', [MessageController::class, 'handle']);

Regex captures are available through $update->matches.

Callback Query

BOT::onCallbackQuery([ProfileController::class, 'show']);

Read callback data with:

$update->callbackQueryData();

Fallback and Invalid Updates

BOT::fallback(function ($update) {
    // Handle an unmatched update.
});

BOT::onInvalid(function ($update) {
    // Handle an invalid update.
});

Improved Route Matching

The router evaluates registered routes and selects the most specific matching route instead of always using the first match.

Current priority:

Exact command/text match
        ↓
Route parameter match
        ↓
Regular expression match
        ↓
Generic callback-query route

Example:

BOT::onText('/^hello/i', [MessageController::class, 'generic']);
BOT::onText('hello', [MessageController::class, 'exact']);
BOT::onText('hello {name}', [MessageController::class, 'named']);

For hello, the exact route wins.

For hello Hossein, the named-parameter route can match.

When routes have the same score, registration order is used as the tie-breaker.

Command Arguments

Text after a command is available as positional arguments.

/start Hossein

Route:

BOT::onCommand('start', [StartController::class, 'index']);

Controller:

public function index($update, array $arguments)
{
    $name = $arguments[0] ?? null;
}

The same arguments are available from TelegramUpdate:

$arguments = $update->commandArguments();

For:

/start one two three

the result is:

[
    'one',
    'two',
    'three',
]

Multiple spaces are normalized.

A Closure can receive the positional arguments:

BOT::onCommand('start', function ($update, ...$arguments) {
    $name = $arguments[0] ?? null;
});

Command arguments are positional. Named values can instead use Route Parameters.

Route Parameters

Route Parameters let a route define named placeholders with {name} syntax.

Command Parameters

BOT::onCommand('user {id}', [UserController::class, 'show']);

Telegram message:

/user 123

The router matches the route and captures:

[
    'id' => '123',
]

The value is available through TelegramUpdate:

$id = $update->routeParameter('id');

Or all parameters:

$parameters = $update->routeParameters();

Controller methods can receive named parameters through Laravel's Service Container:

public function show($update, $id)
{
    // $id === '123'
}

Multiple Parameters

BOT::onCommand('user {id} {section}', [UserController::class, 'show']);

For:

/user 123 profile

parameters are:

[
    'id' => '123',
    'section' => 'profile',
]

Text Parameters

Parameters can also be used with text routes:

BOT::onText('hello {name}', [MessageController::class, 'hello']);

For:

hello Hossein

name is captured as Hossein.

Parameters match one non-whitespace segment. Use a regular expression route when more advanced text matching is required.

Parameters and Constraints

Route Parameters work together with Route Constraints:

BOT::onCommand('user {id}', [UserController::class, 'show'])
    ->whereNumber('id');

Therefore:

/user 123    -> matched
/user 4567   -> matched
/user abc    -> not matched

Route Parameter vs Command Argument

Use Command Arguments when the command accepts general positional values:

BOT::onCommand('search', [SearchController::class, 'search']);

Use Route Parameters when a value has a specific name and belongs to the route definition:

BOT::onCommand('user {id}', [UserController::class, 'show']);

Command arguments:

/search phone android

Route parameters:

/user 123

A route can use both mechanisms. Named route parameters are exposed separately through routeParameters().

Route Constraints

Constraints validate named captures from regular-expression routes and Route Parameters.

Number

BOT::onText('/^user (?<id>[^ ]+)$/', [UserController::class, 'show'])
    ->whereNumber('id');

The same constraint works with Route Parameters:

BOT::onCommand('user {id}', [UserController::class, 'show'])
    ->whereNumber('id');

Custom Regex

->where('id', '[0-9]{4}');

Alpha

->whereAlpha('name');

Alpha Numeric

->whereAlphaNumeric('username');

Allowed Values

->whereIn('section', ['profile', 'settings']);

Multiple constraints can be chained:

BOT::onCommand('user {id} {section}', [UserController::class, 'show'])
    ->whereNumber('id')
    ->whereIn('section', ['profile', 'settings']);

If a constraint fails, the route is treated as not matched and the router can try another route or the fallback.

Controllers and Dependency Injection

Controller actions are resolved through Laravel's Service Container:

BOT::onCommand('start', [StartController::class, 'index']);

Dependencies can be injected normally:

public function index(MyService $service, $update, array $arguments)
{
    // ...
}

Route parameters are also passed by name:

public function show(MyService $service, $update, $id)
{
    // ...
}

TelegramUpdate

Useful methods:

$update->chatId();
$update->userId();
$update->messageId();
$update->text();
$update->callbackQueryData();
$update->commandArguments();
$update->routeParameters();
$update->routeParameter('id');
$update->originalUpdate();

Nested Telegram data is also available:

$update->message->chat->id;
$update->message->from->id;
$update->message->text;

Middleware

Telegram middleware follows the Laravel middleware pipeline model.

Telegram Update
      ↓
Global Middleware
      ↓
Group Middleware
      ↓
Route Middleware
      ↓
Controller / Closure

Creating Middleware

A common application location is:

app/Telegram/Middleware/

Example:

<?php

namespace App\Telegram\Middleware;

use Closure;

class AdminMiddleware
{
    private const ADMIN_CHAT_ID = 123456789;

    public function handle($update, Closure $next)
    {
        $chatId = $update->chatId();

        if ($chatId !== self::ADMIN_CHAT_ID) {
            return null;
        }

        return $next($update);
    }
}

If the check succeeds, call $next($update) to continue. If it fails, do not call $next().

Route Middleware

BOT::middleware([
    CheckUser::class,
    IsAdmin::class,
])->onCommand('admin', [AdminController::class, 'index']);

Global Middleware

BOT::globalMiddleware([
    LogTelegramUpdate::class,
    CheckBotState::class,
]);

Groups

BOT::group([
    AdminMiddleware::class,
    LogTelegramUpdate::class,
], function () {
    BOT::onCommand('users', [UserController::class, 'index']);
    BOT::onCommand('stats', [StatsController::class, 'index']);
});

Nested groups are supported.

Middleware Parameters

BOT::middleware([
    HasPermission::class . ':users.create',
])->onCommand('create-user', [UserController::class, 'create']);

Parameters are available after $next through ...$parameters.

Middleware Aliases

Middleware aliases let you register a short name for a Telegram middleware.

Register a single alias:

BOT::aliasMiddleware('admin', IsAdmin::class);

Use the alias in a route:

BOT::middleware([
    'admin',
])->onCommand('admin', [AdminController::class, 'index']);

Multiple aliases can be registered at once:

BOT::aliasMiddlewares([
    'admin' => IsAdmin::class,
    'auth' => CheckUser::class,
    'permission' => HasPermission::class,
]);

Middleware parameters are supported with aliases:

BOT::aliasMiddleware('permission', HasPermission::class);

BOT::middleware([
    'permission:users.create',
])->onCommand('create-user', [UserController::class, 'create']);

Aliases are resolved through Laravel's Service Container.

Middleware Configuration

Middleware aliases can also be registered in the package configuration.

In config/telegram-bot-router.php:

'middleware' => [
    'aliases' => [
        'admin' => App\Telegram\Middleware\IsAdmin::class,
        'auth' => App\Telegram\Middleware\CheckUser::class,
    ],
],

After configuration, use the alias directly in routes/bot.php:

BOT::middleware([
    'admin',
])->onCommand('admin', [AdminController::class, 'index']);

Configuration aliases are loaded automatically by the package Service Provider.

Middleware Contract

A middleware may implement:

use ReyhanTeam\TelegramBotRouter\Middleware\TelegramMiddlewareInterface;

The contract is optional when the class has a compatible handle() method.

Conversation / State

Conversation State lets the bot wait for the user's next message.

Example:

/register
   ↓
Ask for name
   ↓
wait for next message
   ↓
Ask for phone
   ↓
wait for next message
   ↓
Finish

Start a conversation with:

BOT::conversation('register')
    ->step([RegisterController::class, 'name'])
    ->step([RegisterController::class, 'phone'])
    ->step([RegisterController::class, 'finish'])
    ->startOnCommand('register');

Conversation state is stored separately for each Telegram chat/user.

Conversation Data

A step can return data:

return [
    'data' => [
        'name' => $update->text(),
    ],
];

The next step receives the data:

public function phone($update, array $data)
{
    $name = $data['name'] ?? null;
}

Finish

return [
    'done' => true,
];

Timeout

TELEGRAM_CONVERSATION_TTL=3600

Or per conversation:

->ttl(1800)

Conversation state uses Laravel Cache.

Webhook

Default endpoint:

POST /telegram/webhook

Register it with:

php artisan reyhan:setWebhookRoute

Polling

Start polling with:

php artisan reyhan:start-polling

Webhook and Polling use the same Telegram routing layer.

Architecture

                         Telegram
                            │
                 ┌──────────┴──────────┐
                 │                     │
              Webhook               Polling
                 │                     │
                 └──────────┬──────────┘
                            ▼
                    Telegram Router
                            │
                     Conversation Check
                            │
                 ┌──────────┴──────────┐
                 │                     │
              Active                Inactive
           Conversation                │
                 │                     ▼
                 │               Route Matching
                 │                     │
                 └──────────┬──────────┘
                            ▼
                  Global Middleware
                            │
                            ▼
                   Group Middleware
                            │
                            ▼
                   Route Middleware
                            │
                            ▼
                 Controller / Closure

Roadmap

🔴 Priority 1 — Core Telegram Routing

  • Webhook support
  • Polling support
  • routes/bot.php
  • BOT::onCommand()
  • BOT::onText()
  • BOT::onCallbackQuery()
  • BOT::fallback()
  • BOT::onInvalid()
  • Closure handlers
  • Controller + method handlers
  • Laravel Service Container controller resolution
  • Dependency Injection for controller methods
  • Regular expression matching for text routes
  • TelegramUpdate wrapper
  • Better route matching
  • Route constraints
  • Command arguments
  • Route parameters

🔴 Priority 2 — Middleware ⭐

  • Middleware pipeline
  • Route middleware
  • Global Telegram middleware
  • Laravel Container resolution
  • Middleware objects
  • Middleware short-circuit
  • Middleware execution order
  • Optional TelegramMiddlewareInterface
  • Middleware groups
  • Nested middleware groups
  • Middleware parameters
  • Named middleware aliases
  • Middleware configuration

🔴 Priority 3 — Conversation / State ⭐⭐⭐

  • Per-user conversation state
  • Conversation steps
  • Wait for next message
  • Save current step
  • Move to next step
  • Finish conversation
  • Conversation timeout
  • Conversation data
  • Laravel Cache storage
  • Controller and Closure conversation steps
  • Cancel conversation command/API
  • Input validation helpers
  • Explicit conversation middleware
  • Conversation events
  • More storage driver controls

🟠 Priority 4 — Exception and Error Handling

  • Telegram route exceptions
  • Invalid update exceptions
  • Telegram API exceptions
  • Configurable exception handler
  • Safe logging
  • Never expose bot tokens in logs

🟠 Priority 5 — Events

  • Update received event
  • Message received event
  • Command received event
  • Callback query event
  • Conversation started event
  • Conversation step event
  • Conversation finished event
  • Route matched event

🟠 Priority 6 — Rate Limiting

  • Per-user limits
  • Per-chat limits
  • Per-command limits
  • Configurable limits
  • Laravel Cache integration

🟠 Priority 7 — Telegram Route List

php artisan telegram:route:list

🟠 Priority 8 — Queue Support

  • Queue update processing
  • Queue message sending
  • Queue heavy bot tasks
  • Laravel queue integration

🟡 Priority 9 — Named Telegram Routes

BOT::onCommand('start', [StartController::class, 'index'])
    ->name('telegram.start');

🟡 Priority 10 — Telegram Route Cache

php artisan telegram:route:cache
php artisan telegram:route:clear

🟡 Priority 11 — More Telegram Update Types

  • Inline Query
  • Edited Message
  • Channel Post
  • Edited Channel Post
  • Chat Member
  • My Chat Member
  • Chat Join Request

🟡 Priority 12 — Better Callback Query Routing

  • Exact callback data matching
  • Regular expression callback matching
  • Callback route parameters
  • Named callback routes
  • Better inline keyboard integration

🟡 Priority 13 — User and Chat Conditions

  • Admin-only routes
  • User conditions
  • Private-chat conditions
  • Group-chat conditions
  • Channel conditions
  • User permission checks
  • Chat type constraints

🟡 Priority 14 — Testing Tools

  • Telegram fake
  • Fake Telegram updates
  • Route dispatch assertions
  • Message sending assertions
  • Callback assertions
  • Conversation/state tests

Design Principles

  1. Keep Telegram routes separate from Laravel HTTP routes.
  2. Use Laravel's Service Container for controllers and middleware.
  3. Keep the public routing API simple.
  4. Keep route registration separate from update processing.
  5. Support Webhook and Polling through the same routing layer.
  6. Follow PSR-4 autoloading.
  7. Keep the code clean and maintainable.
  8. Keep Telegram-specific routing logic out of routes/web.php.
  9. Build advanced features on top of the core router.