Appearance
SDK Choice & Structure Review
A health check of telegram-bot-essentials/essence's structure, plus an evaluation of whether irazasyed/telegram-bot-sdk was the right foundation for the vshop Telegram bot platform (essence + billing/wallet/affiliates/announcements/settings/gateway-card/gateway-zibal on top).
Point-in-time review — two findings are now fixed
This page is a snapshot from before v0.12.0. The two concrete issues in Bus / handler architecture below — encodeCallback() truncating silently, and bus registration re-running on every request — were both fixed in v0.12.0: encodeCallback() now throws CallbackDataTooLong, and handlers register once per worker boot in TelegramBotServiceProvider (see Architecture → Handler Registration). Recommendation #3 (an abstraction seam over Telegram\Bot\*) is still open.
Verdict, up front
irazasyed/telegram-bot-sdk was a reasonable choice and remains defensible today. It's the most Laravel-native option among the PHP libraries surveyed (ServiceProvider, multi-bot BotsManager, config-driven), it's actively maintained, and it's permissive enough to fork — which the project already did (Elyar0/telegram-bot-sdk@feature/local-file-path-download) to support local Bot API server file downloads.
It is deliberately thin — a raw object/HTTP wrapper with no conversation, routing, or multi-tenancy concepts. That's exactly why tbe-essence had to be built: a bus/routing/state framework layered on top. The real risk isn't "wrong SDK", it's that 34 of ~90 src/ files in essence import Telegram\Bot\* directly, with no adapter boundary — see SDK Coupling below.
Structural Health Check
SDK coupling vs abstraction
Api, Update, BaseObject, Command, Keyboard, InputFile, TelegramSDKException etc. are used directly across Http/Controllers, Models, Services, every CallbackQueries/StateAnswers handler, and TelegramResponse. There is no TelegramApiContract-style interface between tbe-essence and the SDK.
- This coupling is deep, but to a well-aligned dependency —
irazasyed's multi-bot, Laravel-container-friendly design is whattbe-essence's own architecture mirrors. - It has already been forked once (local-file-path-download), which proves the SDK is mutable when needed — but a fork must be manually kept in sync with upstream; there's no auto-update path, so drift risk grows over time.
- Cost of this coupling only becomes real if: the SDK goes unmaintained, Telegram's API grows faster than a single-maintainer fork can track, or a clearly better-fit alternative emerges. See Migration Cost.
Bus / handler architecture
The Commands → ReplyKeys → StateAnswers priority chain, plus separate CallbackQuery and InlineQuery buses (see Architecture), is a solid, already-proven-extensible design — every companion package (billing, wallet, affiliates, announcements, settings, gateway-card, gateway-zibal) hooks in via addCallbackQuery() / addStateAnswer() / addReplyKey() from its own service provider, with no changes needed to essence itself.
Two concrete issues found:
encodeCallback()silently truncates on overflow. Insrc/helpers.php:phpif (strlen($result) > 64) { return 'LONG_CALLBACK_DATA'; // TODO: handle this case }When a callback payload exceeds Telegram's 64-byte
callback_datalimit, this ships a button with the literal stringLONG_CALLBACK_DATAas its callback data instead of failing loudly. The docs already describe the correct workaround (stash payload inStateData, pass only the ID) — but nothing enforces it. This is a live footgun for any new Feature that forgets it.Bus registration is rebuilt on every webhook request, inside an Octane-persistent singleton.
TelegramWebhookController::initializeOptions()rescansapp/Telegram/**directories and re-registers every Command/CallbackQuery/StateAnswer/ReplyKey class on every invocation. The buses (ReplyKeyBus,StateAnswerBus,CallbackQueryBus,InlineQueryBus) are registered assingleton()inTelegramBotServiceProvider, meaning under Octane+RoadRunner (this project's deployment) the same bus instance survives across requests on a worker, and gets redundantly re-populated every single time. Notably,Webhook::classwas deliberately madescoped()instead, with an explicit comment reasoning about Octane worker persistence — proof the team already understands the issue class, just didn't apply it here. Not a correctness bug (arrays are keyed by type/text, so re-registration overwrites idempotently) but it's pure repeated filesystem-scan + reflection-instantiation tax, in exactly the deployment mode meant to eliminate that kind of tax.
Multi-tenancy
Stancl Tenancy usage is idiomatic: Bot extends BaseTenant, BotUser uses BelongsToTenant, tenancy middleware is given top priority, and the webhook route resolves tenants by path. No issues found.
MessageMeta / StateData
Clean separation of concerns — MessageMeta tracks which Telegram message to lock/revert/update, StateData holds arbitrary JSON payload for multi-step forms, decoupled specifically so callback_data can stay within the 64-byte limit by referencing a StateData ID instead of carrying the payload inline. Well-designed, matches the project's own State & StateData docs.
Recommendations
| # | Finding | Recommendation |
|---|---|---|
| 1 | encodeCallback() returns 'LONG_CALLBACK_DATA' silently on overflow | Throw a dedicated exception (fail loud in dev/staging), or auto-route oversized payloads through StateData transparently instead of leaving it a manual convention |
| 2 | Bus registration rescans + re-registers every request under Octane singletons | Load once per worker boot (guard with a boolean flag on the singleton, or move registration into boot() triggered once) instead of per-request File::allFiles() + reflection |
| 3 | No abstraction boundary over Telegram\Bot\* (34 files) | Introduce a thin interface for the highest-traffic call sites (wHook()->api() surface, Update/Keyboard construction) incrementally — not urgent today, but do it before Mini App work adds new API surface, so that addition has a seam to land on rather than more direct SDK coupling |
SDK Comparison
Evaluated against all 20 PHP libraries listed on core.telegram.org/bots/samples.
| Library | Stars | Maintained? | Laravel fit | Framework vs raw wrapper | Mini-App/newer-API | Verdict |
|---|---|---|---|---|---|---|
| irazasyed/telegram-bot-sdk (current) | 3306 | Active | Built-in | Raw wrapper + light structure (Commands, Objects, Keyboard) | No | Baseline — thin, stable, unopinionated; exactly why tbe-essence had to build its own bus/routing/state layer on top |
| Nutgram | 740 | Active | Via nutgram/laravel pkg | Framework: conversations, middleware, groups/scopes | Not framework-native | Strongest architectural alternative — has out-of-the-box what tbe-essence built by hand |
| Telegraph | 817 | Active | Native Laravel package | Fluent wrapper, no state/conversation system | None | Nicer DX for one-off sends, no answer to tbe-essence's bus needs |
| LaraGram | 129 | Active | Standalone Laravel-like framework (not atop Laravel) | Full framework: Step Manager, Conversations, multi-bot, MTProto | Yes — TMA (Mini Apps) is core | Best Mini-App story on the list, but replaces Laravel itself — non-starter for an existing Laravel app |
| laragram (Mirmuxsin) | 42 | Semi-active | Laravel package | Thin wrapper | Unlikely | Too low-signal for production swap |
| TeleBot | 306 | Active | Laravel adapter pkg | OOP + functional handlers, BotManager for multi-bot | Not documented | Multi-bot support notable — 2nd-tier candidate, less battle-tested |
| php-telegram-bot/core | 4024 | Stalled ("on hold") | None | Raw wrapper + plugin/conversation system, single-bot | No | Most stars but explicitly paused — disqualified |
| unreal4u/telegram-api | 785 | Archived | None | Async raw wrapper | No | Dead — disqualified |
| NovaGram | 187 | Stale (2023) | None | OOP wrapper, built-in Conversations | No | Unmaintained, no Laravel — disqualified |
| klev-o/telegram-bot-api | 46 | Stale | None | Simple OOP wrapper | No | Too small |
| vjik/telegram-bot-api | 128 | Active | None (Yii ecosystem) | Raw wrapper | No | Not Laravel-oriented |
| TelegramBot/Api | 1189 | Active | None | Raw wrapper | No | No framework features, no Laravel |
| tgWebValid | 68 | Active | N/A | Not a bot SDK — Mini App/Login Widget validator only | Yes (sole purpose) | Complementary tool, not a replacement SDK |
| TuriBot | 122 | Active | None | Simple wrapper | No | Low signal |
| kuvardin/TelegramBotsApi | 17 | Active | None | Raw wrapper | No | Too small |
| telegram-bot-php/core | 61 | Stale | None | Simple wrapper | No | Low signal |
| TgBotLib | 5 | Stale | None (ncc ecosystem) | Raw wrapper | No | Too small/niche |
| NeleBotX | 28 | Dead | None | Framework attempt | No | Abandoned |
| BPT | 9 | Dead | None | Simple wrapper | No | Abandoned |
| BotAPI (TelegramSDK) | 26 | Stale | None | Raw wrapper | No | Too small |
Top realistic alternatives
- Nutgram — the clearest architectural peer to what
tbe-essencebuilt manually: native conversation/state-machine system, middleware, scope/group handlers, plus an official Laravel package. Whereirazasyed/telegram-bot-sdkgave nothing but raw API objects (forcing the custom bus/StateAnswer/CallbackQuery machinery), Nutgram would have provided much of that scaffolding pre-built — at the cost of migrating the whole webhook/routing model to its middleware paradigm. - TeleBot — smaller and newer, but its
BotManagergives first-class multi-bot switching, which maps directly onto vshop's multi-tenant-bot requirement (essence had to build tenant resolution itself via Stancl instead). Worth watching, far less battle-tested (306 stars). - LaraGram — best native Mini Apps story of anything surveyed, but it's a standalone Laravel-style framework, not a package for an existing Laravel app. A "watch" item, not a migration candidate.
Everything else is either a raw wrapper with no framework ambitions (matching or weaker than irazasyed), abandoned/archived (php-telegram-bot/core, unreal4u, NovaGram, NeleBotX, BPT), or too low-signal (<50 stars, stale) to trust for a production SaaS platform.
How tbe-essence Itself Compares
Setting the low-level SDK question aside, tbe-essence's own design holds up well against the field surveyed above:
- A real MVC-ish split. Feature (view — builds
TelegramResponse+ keyboards) → CallbackQuery/StateAnswer (controller — handles input, orchestrates) → Eloquent Model. Most raw wrappers (php-telegram-bot, unreal4u, TelegramBot/Api, klev-o, TuriBot, ...) impose no structure at all — you get API objects and nothing else. Only Nutgram and LaraGram impose comparable structure, but through their own middleware/conversation DSL rather than a pattern Laravel developers already know. - Package/plugin model via service providers.
billing,user-wallet,affiliates,announcements,gateway-card,gateway-zibalall hook into the shared buses (addCallbackQuery()/addStateAnswer()/addReplyKey()) from their ownboot(), with zero changes toessencecore. This is straight Laravel idiom — arguably tighter Laravel integration than Nutgram gets from its bolt-onnutgram/laravelpackage. - Event bus reuses Laravel's own event system (
BotCommandHandled,BotCallbackQueryHandled, etc. — see Events & Listeners) instead of inventing a new middleware/hook vocabulary. Lower learning curve for anyone already familiar with Laravel events. - Multi-tenancy is the standout differentiator. None of the 20 libraries surveyed —not Nutgram, not TeleBot's
BotManager, not LaraGram— natively solve "N independently-configured bots, each with tenant-scoped Eloquent data, in one app." TeleBot switches which bot is active; it doesn't isolate data per bot the wayBelongsToTenant+ Stancl does here. - The 64-byte
callback_dataconstraint is solved deliberately, not discovered by accident.encodeCallback()/encodeAnswerState()plus theStateDataoverflow path (see State & StateData) handle a sharp, easy-to-miss Telegram limit that a lot of naive bot code gets bitten by.
Honest caveat: a large share of this — bus routing, conversation/state machine, per-flow handler classes — is what Nutgram ships out of the box. tbe-essence effectively rebuilt roughly 70% of Nutgram's feature set by hand, on top of a thinner base SDK. That effort wasn't wasted — it bought Laravel-native idiom and multi-tenancy Nutgram doesn't have — but it is real, in-house-carried maintenance burden that an upstream framework would otherwise absorb.
Migration Cost Estimate
Not currently justified — no forcing function (no maintenance abandonment upstream, no urgent bug) — but for the record:
- 34 files in
tbe-essenceimportTelegram\Bot\*directly; vshop's ownapp/Telegram/tree likely adds a comparable count (not separately audited here). - Highest-cost surfaces to replace:
Webhook::api()(theApiinstance itself),Update/BaseObjectused throughoutCallbackQuery/StateAnswer/Featureclasses to parse incoming payloads,Keyboard/Buttonfor outgoing markup,InputFilefor uploads, theCommandbase class for slash commands, and the customdownloadFile()/ local-file-path fork behavior, which would need reimplementing against any new client. - Character of the migration: high blast radius, low individual complexity — many mechanical touch points, not deep logic rewrites. Multi-week effort as a big-bang rewrite for a solo maintainer; much cheaper if the interface boundary (Recommendation #3) is built first and the swap happens behind it incrementally.
Future Feature Fit (Mini Apps & beyond)
Telegram Mini Apps / WebApp validation mostly lives at the HTTP-endpoint layer — validating the initData HMAC — which is largely independent of whichever Bot API wrapper handles webhooks. This means Mini App support is more "add a validation middleware" than "need a different core SDK" — a library like tgWebValid (validator-only, not a competing bot SDK) can sit alongside irazasyed/telegram-bot-sdk without conflict. Other emerging Bot API surface (Stars payments, Business accounts, etc.) will land as new Telegram\Bot\* methods/objects the same way current ones do — no structural blocker, just more surface area touching the 34 already-coupled files, which reinforces why Recommendation #3 (an abstraction seam) is worth doing proactively rather than reactively.