m4n50n/telegram-bot-bundle
A Symfony bundle for building Telegram bots with a fluent, Laravel-style API. It wraps the Telegram Bot API in a clean service layer, offering command routing, webhook and polling updates, keyboards, and message helpers so you can ship bots quickly with familiar, testable PHP code.
First, the framing: this is a Symfony bundle, not a Laravel package. Laravel has no bundle kernel, so composer require m4n50n/telegram-bot-bundle will not register routes, services, or Artisan commands for you. There are two realistic ways a Laravel developer touches this repo:
longman/telegram-bot) directly and copy the bundle's DI wiring into a service provider.Minimal path for case 1:
composer require m4n50n/telegram-bot-bundle
Then in config/bundles.php:
M4n50n\TelegramBotBundle\TelegramBotBundle::class => ['all' => true],
Configure the bot token via env (TELEGRAM_BOT_TOKEN) in config/packages/telegram_bot.yaml, then use the polling entry point that ships with the bundle to prove connectivity with a plain /start echo.
Minimal path for case 2 (Laravel):
composer require longman/telegram-bot
Create config/telegram.php with token, username, webhook_secret, register a singleton binding for Longman\TelegramBot\Telegram in AppServiceProvider, and add a single POST /telegram/webhook route. That is the "hello world" — one update in, one sendMessage out.
Where to look first in this repo:
src/DependencyInjection/Configuration.php and ...Extension.php — this is the canonical list of what the bundle makes configurable (token, commands paths, limits, logging). Treat it as your spec when re-implementing in Laravel.src/Resources/config/services.* — shows how Telegram is wired, which is exactly the constructor wiring you need for a Laravel singleton.Your first real use case should be a webhook receiver that acknowledges fast and dispatches a job, not polling. Polling is fine for a spike, but it fights PHP-FPM and queue workers.
Keep the bundle in a small Symfony app (or a single-process CLI worker). Laravel talks to it over HTTP or a shared queue (Redis/SQS). This is the least surprising use of the package and isolates its low maintenance risk (18 stars, 0 dependents, last release Feb 2024) to one deployable.
/api/telegram/inbound) so your Laravel app owns user state, auth, and business logic.Mirror the bundle's config surface so behavior stays familiar if you ever migrate:
// AppServiceProvider::register()
$this->app->singleton(Telegram::class, function ($app) {
$config = $app['config']['telegram'];
$telegram = new Telegram($config['token'], $config['username']);
$telegram->addCommandsPaths($config['commands_paths']);
$telegram->enableLimiter(); // if you want the built-in per-chat limiter
return $telegram;
});
Then a controller that only does routing + dispatch:
public function __invoke(Request $request, Telegram $telegram)
{
$update = Update::createFromJson($request->getContent());
ProcessTelegramUpdate::dispatch($update->toJson());
return response()->noContent();
}
And a queued job that owns the actual command handling (/start, /help, callback queries). Use the library's Command classes, CommandBus, and Conversation primitives — or your own match statement if the bot is small. The bundle's value here is as a reference implementation; longman/telegram-bot is the substance.
message, callback_query, edited_message, and channel_post. Inline keyboard callbacks are the most common source of "why did my handler not fire" bugs.Conversation needs a persistent driver; in Laravel back it with cache or a telegram_conversations table keyed by chat_id.update_id. Telegram retries. Store processed update_ids (cache with TTL is fine) and short-circuit duplicates before doing anything with side effects.dispatch().Update::createFromJson(). This exercises your real parsing path without hitting the API. Mock the Request layer for outbound assertions.extra.laravel.providers, no vendor:publish, no config merge. Every "install" step in the README assumes config/bundles.php. Budget an hour to translate DI tags into Laravel container bindings — the tags are the part people miss.longman/telegram-bot explicitly. The library had breaking API changes across majors, and this bundle's constraint is a snapshot from Feb 2024. If Composer resolves a newer major, expect Request/Response signature drift.setWebhook and polling are mutually exclusive. Running the polling command while a webhook is set silently produces no updates (or doubles). deleteWebhook before polling; getWebhookInfo is your first debugging call when updates stop arriving."ok": false. Naive $response->successful() checks pass while the bot is broken. Always inspect the JSON ok/error_code/description, and log them — the library surfaces these but only if you look.secret_token on setWebhook and validate the X-Telegram-Bot-Api-Secret-Token header in your controller. HTTPS with a valid cert is non-negotiable.429 with retry_after. Respect it; don't retry immediately.max_execution_time, memory limits, and the lack of a supervisor are the usual killers. If you poll, do it in a php artisan worker under supervisor/systemd, not a request.getFile then download from https://api.telegram.org/file/bot<token>/<file_path>. Check file_size against the 20MB bot download limit before trying.telegram.commands config array. Custom SystemCommands are the cleanest hook for /start-style bootstrapping.symfony/* constraints against your Symfony version and its longman/telegram-bot constraint against the API you intend to write against. This is the single most likely blocker you'll hit on day one.How can I help you explore Laravel packages today?