usmonaliyev/laravel-telegram-notifier
Send Telegram notifications from your Laravel application with this lightweight notifier package. Configure a bot token and chat ID, then dispatch alerts, logs, or user updates through Laravel's notification system or the simple notifier API using the Telegram Bot API.
Install and wire it up
composer require usmonaliyev/laravel-telegram-notifier
php artisan vendor:publish --tag=telegram-notifier-config
Laravel 11+ users: the service provider is auto-discovered; no config/app.php edits.
Configure credentials
Add to .env:
TELEGRAM_BOT_TOKEN=123456:ABC...
TELEGRAM_CHAT_ID=-1001234567890
Get the token from @BotFather. The chat ID: message the bot, then hit https://api.telegram.org/bot<TOKEN>/getUpdates and read message.chat.id. Group chat IDs are negative — that trips up almost everyone on day one.
Where to look first
config/telegram-notifier.php — confirms which env keys are read, whether a queue connection or timeout is exposed.vendor/usmonaliyev/laravel-telegram-notifier/src/ — it's a small source tree; skim the Facade and the main service class to see exactly what methods exist.First use case — send a test message
php artisan tinker
>>> TelegramNotifier::send('Deploy to staging complete');
If that lands in your chat, the package is working. From there, the highest-value first integration is exception reporting (see next section).
Exception reporting (Laravel 11/12) — bootstrap/app.php:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (Throwable $e) {
if ($e instanceof \App\Exceptions\CriticalException
|| app()->environment('production')) {
try {
TelegramNotifier::send(
"🚨 ".get_class($e).": ".$e->getMessage()
."\n".$_SERVER['REQUEST_URI'] ?? ''
);
} catch (Throwable $inner) {
// never let the notifier mask the original exception
}
}
});
})
Laravel 10 and below: override report() in app/Exceptions/Handler.php with the same guard.
Dedicated service wrapper — the pattern that survives package swaps. Never call the facade from controllers:
final class OpsAlert
{
public function send(string $message, string $level = 'info'): void
{
if (! config('services.telegram.enabled')) return;
try {
TelegramNotifier::send("[{$level}] {$message}");
} catch (Throwable $e) {
report($e);
}
}
}
The enabled flag is the single most useful addition you'll make — lets you silence alerts in local/CI without touching config.
Queued notifications — alerts inside request handlers add latency. Either dispatch a job:
dispatch(function () use ($message) {
TelegramNotifier::send($message);
})->onQueue('alerts');
or, if the package exposes a queue connection in its config, use that. Check the config file; if there's no queue support, the job wrapper above is the workaround.
Scheduled daily digest — more useful than per-event alerts:
Schedule::call(fn () => TelegramNotifier::send(
"Daily: {$orders} orders, {$errors} errors"
))->dailyAt('09:00');
Logging-channel integration — if the package registers a telegram log driver (check config/logging.php after publish), you can fan out:
Log::channel('stack')->error('Payment gateway down');
// where 'stack' includes 'telegram'
This is the cleanest integration if supported — you get levels, context, and stack fan-out for free.
-100… is a supergroup; -… (shorter) is a legacy group. Copy it from getUpdates, do not type it.parse_mode, user-supplied content with _, *, [ will break rendering. Either escape it or use plain text. Symptoms: 400 Bad Request: can't parse entities.php artisan config:cache is required after .env changes in production. A stale TELEGRAM_BOT_TOKEN is the #1 "it works locally" cause.try/catch and report() the inner exception at minimum; otherwise you'll believe alerts are firing when they aren't.$tries = 3 and backoff().TelegramNotifier::swap($fake) (works with Laravel facades) or bind an interface in your service provider. Don't let the facade leak into feature tests.^1.0 (or whatever the current tag is) and keep a fallback path. The OpsAlert wrapper above means swapping to laravel-notification-channels/telegram or a raw Http::post is a one-file change.How can I help you explore Laravel packages today?