Appearance
Architecture
Current version: v0.8.23
Overview
Telegram Bot Essentials routes every webhook update through a small set of buses. Each bus maps an encoded string (or user state) to a class method with dependency injection for Eloquent models.
Telegram Update
│
▼
TelegramBotAuthentication (middleware)
• Resolve bot tenant
• Verify secret_token
• Load Update into wHook()
• Find or create BotUser
│
▼
TelegramWebhookController
│
├── fires BotUpdateReceived
│
├── message + starts with "/" → Commands → fires BotCommandHandled
│ └── /start <x> → fires BotDeepLinkReceived
├── message + keyboard text → ReplyKeyBus → fires BotReplyKeyHandled
├── message + user has state → StateAnswerBus → fires BotStateAnswerHandled
├── nothing matched → fires BotUpdateUnhandled
├── callback_query → CallbackQueryBus → fires BotCallbackQueryHandled
└── inline_query → InlineQueryBus → fires BotInlineQueryHandledSee Events & Listeners for the full event reference.
Source: src/Http/Controllers/TelegramWebhookController.php
The Webhook Context (wHook())
wHook() returns a request-scoped singleton (TelegramBotEssentials\Essence\Support\Webhook) holding:
| Property | Description |
|---|---|
api() | Telegram Api instance for the current bot |
update() | Current Telegram Update object |
bot() | Current Bot model (tenant) |
user() | Current BotUser model |
requestState() | Snapshot of bot_users.state when the user was loaded |
peerId() | Telegram user/chat ID from the update |
Use wHook() anywhere in handlers instead of passing context through every method.
The context can also be exported/imported for queued jobs via exportContext() and importContext() (src/Support/Webhook.php).
Encoding Formats
Two parallel encoding schemes exist. Both use TYPE#method with optional parameters.
Callback data (inline buttons)
Used in callback_data on inline keyboard buttons. Hard limit: 64 bytes (Telegram API).
BOTADMNS#add_admin
BOTADMNS#delete_admin?42Helpers: encodeCallback(), decodeCallback() in src/helpers.php.
Params are positional (not named) — resolved by index into handler method parameters.
Answer state (user input routing)
Stored in bot_users.state. No Telegram size limit (lives in your database).
BOTADMNS#add_admin?message_meta_id=12
PRODUCTSMANAGEMENT#add?target_field=title&state_data=7Helpers: encodeAnswerState(), decodeAnswerState() in src/helpers.php.
Params are named query-string key/value pairs — resolved by name into handler method parameters.
Building Blocks
Commands
Standard telegram-bot-sdk commands. Register in config/tbe-essence.php under commands.
Commands take priority over everything else. When a command runs, any active user state is cancelled first.
ReplyKeys
Keyboard buttons shown at the bottom of the chat. Registered via config/tbe-essence.php → keyboard and/or loadReplyKeys().
When a ReplyKey is pressed:
- Any active user state is cleared (
cancelOldProcess) - The key's
handle()runs - The previous state's
cancel()method is invoked on the StateAnswer
While a user has an active state, the keyboard shows Cancel Process instead of the normal menu.
CallbackQueries
Handle inline button presses. Each class declares:
$type— uppercase identifier used inencodeCallback()$perm— minimumBotUser.powerrequired (Rolesenum)- Methods matching callback method names (snake_case in encoding, resolved to camelCase)
Model parameters are resolved by position from callback params:
php
// callback_data: BOTADMNS#delete_admin?42
public function deleteAdmin(BotUser $botUser): voidStateAnswers
Handle text/media input while bot_users.state is set. Same structure as CallbackQueries but:
- Params are named (from query string)
$allowedFieldscontrols which message fields trigger the handler (text,photo,document)- Optional
cancel()method for cleanup when the flow is interrupted
See State & StateData for the full input-flow pattern.
Features
Static classes that return TelegramResponse objects. They build message text and inline keyboards but do not handle updates directly.
Features are called from CallbackQueries or StateAnswers:
php
BotAdminsFeature::menu()->update(); // edit current callback message
BotAdminsFeature::menu()->send(); // send new messageTelegramResponse supports:
send()— new messageupdate()— edit callback messageanswer()— toast on callback querymessageMetaModel()— attach MessageMeta to a model
Source: src/Telegram/TelegramResponse.php, src/Telegram/Features/BotAdminsFeature.php
MessageMeta
Tracks a Telegram message so it can be locked, reverted, or updated during multi-step flows.
Common operations:
makeWithCurrentMessage()— snapshot the callback messagelockAction()— replace inline keyboard with a lock buttoncontinueAction()— delete lock message and restore originalrevertAction()— edit message back to original contentupdateAndContinueAction(TelegramResponse)— replace with new content
MessageMeta morphs to an action model (e.g. StateData, an order, a product).
Source: src/Models/MessageMeta.php
StateData
Separate JSON storage for multi-step forms and cross-app payloads. See State & StateData.
Multi-Tenancy
Each Bot is a Stancl Tenancy tenant (bots table). The webhook route uses InitializeTenancyByPath middleware with {bot} as the tenant identifier (unique_id).
BelongsToTenant::$tenantIdColumn is set to bot_id, so models like BotUser are scoped per bot.
Access Control
hasAccess(?int $power) checks:
BotUser.power >= $power- User is the bot owner
- User matches
config('tbe-essence.developer.peer_id')
Roles enum (src/Enums/Roles.php):
ADMIN = 100MODERATOR = 10MEMBER = 0
Error Handling
Uncaught exceptions in the webhook are handled by exceptionHandler(). exceptionReport() additionally sends trace files and update JSON to config('tbe-essence.bug_report.telegram_chat_id') when configured.
Request Processing Priority
For messages (in order):
- Commands (
/start, etc.) - ReplyKeys (keyboard button text match)
- StateAnswers (if
bot_users.stateis set)
If none match → "request is invalid" response.
For callback queries:
- CallbackQueryBus only (state is not checked)