shaygan/telegram-bot-api-bundle
A Laravel bundle wrapping the Telegram Bot API, giving PHP developers a clean, framework-friendly way to send messages, handle updates, and build bots without raw HTTP calls. Simple configuration and service-container integration keep bot code readable and maintainable.
What this actually is: a thin Symfony bundle around longman/telegram-bot (André Longman's library). Almost everything you'll do day-to-day is really the underlying library — the bundle just handles token configuration, service wiring, and (depending on version) command registration. If a behaviour isn't documented here, check longman's docs first; the bundle's own surface area is small enough to read in one sitting.
Minimal steps
composer require shaygan/telegram-bot-api-bundle — expect the dependency resolver to pull in longman/telegram-bot. If you're on a modern Symfony (5/6/7) or PHP 8.x, this is where you'll hit your first wall (see Gotchas).app/AppKernel.php (Symfony 2/3) or config/bundles.php (Symfony 4+ if it installs at all).app/config/config.yml:shaygan_telegram_bot_api:
token: "%telegram_token%"
username: "%telegram_username%"
DependencyInjection/Configuration.php (this is the authoritative list of config keys, not the README), then Resources/config/services.yml for the actual service ID you'll inject, then vendor/longman/telegram-bot/src/Telegram.php.First use case: a webhook endpoint that echoes messages back. Create a controller, point a route at it, call setWebhook once (from a console command or a one-off script), and inside the controller pull the API service and call handle().
public function webhookAction()
{
$telegram = $this->get('shaygan_telegram_bot_api.telegram'); // confirm the ID in services.yml
try {
$telegram->handle();
} catch (\Longman\TelegramBot\Exception\TelegramException $e) {
$this->get('logger')->error($e->getMessage());
}
return new Response('OK');
}
Webhook + command classes (the intended path). The idiomatic longman workflow is one class per command under a Commands/ directory, each implementing Command, with setWebhook passing ['commands' => [...]] so Telegram autocompletes for users. Register the command path in config, and the library maps /start, /help, etc. to your classes. This keeps handlers testable and separate from HTTP plumbing.
Polling from a console command. If you can't expose HTTPS (local dev, restrictive hosting), skip webhooks and run a Symfony command with a getUpdates loop. Two things matter here: sleep(1) between empty polls, and calling EntityManager::clear() (or your ORM equivalent) every N iterations so you don't accumulate stale managed entities over a long-running process.
Webhook controller for the fast path. For a handful of stateless commands, a single controller that inspects $update->getMessage()->getText() and dispatches to your own application services is simpler than authoring Command classes. Use this when the bot is a façade over existing domain logic rather than a chat app in its own right.
Integration tip — route into your existing architecture. Don't put business logic in Command::execute(). Have the command parse and validate input, then hand off to a service (a handler, a command bus, a repository). This is what makes the bot testable and lets the same logic serve HTTP and chat.
Testing. Build a Longman\TelegramBot\Entities\Update from a fixture array and feed it to your command directly — no HTTP, no network. For the send side, inject a stubbed API client or assert against Request::getLastResponse()-style seams rather than hitting Telegram.
Multi-bot. Not a first-class feature. If you need two tokens in one app, instantiate Longman\TelegramBot\Telegram yourself in a factory service with per-bot config instead of relying on the bundle's singleton wiring.
composer require in a scratch project to see the real resolution result — don't trust the README's compatibility claims.longman/telegram-bot directly with a hand-written service definition. For a Laravel codebase specifically, the bundle gives you nothing — use the underlying library or a Laravel-native SDK instead.update_id if you care about exactly-once.setWebhook requires public HTTPS with a valid certificate (self-signed needs the certificate uploaded explicitly). Local development requires a tunnel; plan for it./foo does nothing, first check that foo is in the commands array passed to setWebhook, then that the class is discoverable at the configured command path.TelegramLog, default a logs/ directory relative to cwd) in addition to your Symfony logger. When a webhook appears to do nothing, check the library's log before your app log — it captures the raw update and API errors.split() helper for long text — use it rather than letting the API reject the send.composer update can change behaviour underneath you. Commit composer.lock and pin the bundle exactly.How can I help you explore Laravel packages today?