zomboy7/telegram-bot-bundle
zomboy7/telegram-bot-bundle is a PHP/Symfony bundle for building Telegram bots. It integrates the Telegram Bot API with configurable services, update and webhook handling, and a clean structure for adding commands and bot logic.
This is a Symfony bundle, not a Laravel package. The composer name (zomboy7/telegram-bot-bundle) and the repo (x-oscarr/TelegramBotBundle) both point at a Symfony-style bundle: it registers services through a DI extension, ships a DependencyInjection/*Extension.php, and expects AppKernel-era registration. If you are working in a Laravel app, read this section as "what to expect inside the vendor directory," then jump to Gotchas and Tips for the Laravel-side alternatives.
For a Symfony project, the minimal path is:
composer require zomboy7/telegram-bot-bundle
composer.json (check the symfony/* version constraints — that single line tells you whether this is a Symfony 2/3 bundle or something modern), then DependencyInjection/Configuration.php. That file is the authoritative schema for every config key; the README is frequently stale on these older bundles.app/AppKernel.php (Symfony ≤3) or config/bundles.php (Symfony 4+). If there is no src/ + autoload psr-4 mapping for a modern namespace, assume Symfony 3 at the newest.telegram_bot: / telegram: — confirm against Configuration.php, not the README).# app/config/config.yml — key name must match Configuration.php
telegram_bot:
token: "%telegram_token%"
If there is no test suite and no CI badge, treat the first hour as reconnaissance: read services.yml, list the public services with bin/console debug:container | grep -i telegram, and only then write code.
Outbound-only first, then inbound. The reliable half of a Telegram integration is sending. Wire a console command that sends a message to your own chat ID and run it. Once that works, you have proven the token, the HTTP client, and the service wiring — everything after that is webhook plumbing.
Pattern: service injection over static calls.
class NotifyCommand extends Command
{
public function __construct(private TelegramBot $bot) { parent::__construct(); }
protected function execute(InputInterface $in, OutputInterface $out): int
{
$this->bot->sendMessage($in->getArgument('chat_id'), $in->getArgument('text'));
return Command::SUCCESS;
}
}
Pattern: webhook controller as a thin adapter. Put the update parsing in the controller and push domain work into a service. Telegram retries on non-2xx and on slow responses, so the controller must return 200 fast and never do heavy work inline.
public function webhookAction(Request $request): Response
{
$update = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
$this->messageDispatcher->handle($update); // queue this if it does I/O
return new Response('', 200);
}
Pattern: route the update by type, not by text. A switch on $update['message'] ?? $update['callback_query'] ?? null in one dispatcher service saves you from a nest of if (isset($update['message']['text'])) checks scattered across controllers.
Pattern: set the webhook from a console command, not by hand. Telegram's setWebhook needs the exact public HTTPS URL plus an optional secret_token; a command makes it reproducible across environments and lets you gate it behind --env=prod.
Integration tip: in a Laravel app, do not try to force this bundle into the framework. Use irazasyed/telegram-bot-sdk or defstudio/telegraph for the same job — both are Laravel-native, actively maintained, and give you a facade plus config publishing. The bundle's only advantage is that it already exists in your vendor/ if a legacy Symfony service depends on it.
composer.json and vendor the source into your own repo if the project is long-lived.debug:container and into the Symfony profiler. Always source the token from an environment variable or a parameter file excluded from version control, and never commit app/config/parameters.yml with a live token.getUpdates polling loop, it will conflict with a configured webhook — Telegram returns 409 Conflict on getUpdates while a webhook is set. Call deleteWebhook before switching modes, and remember that getUpdates blocks: a naive loop in a controller will time out your PHP-FPM workers.setWebhook time unless you explicitly pass an upload/certificate argument.file_get_contents over cURL. If the bundle shells out via file_get_contents() for its HTTP calls, it ignores proxy settings, has no timeout control, and throws warnings instead of exceptions. Wrap every call in try/catch and set default_socket_timeout, or swap the transport for a proper client.token or timeout value appears to do nothing, run bin/console config:dump-reference <bundle_alias> — if that command errors, the bundle has no reference config and you are flying blind.final or has a private constructor — either makes decoration impossible and forces a fork.message_id and chat_id of every outbound send, and always check Telegram's ok field — HTTP 200 with {"ok":false,"description":"..."} is the single most common false-positive in bot integrations.How can I help you explore Laravel packages today?