Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Telegraph Laravel Package

defstudio/telegraph

Telegraph is a Laravel package for building Telegram bots with a fluent, testable API. It handles keyboards, media, conversations, webhooks, and polls, so you can ship interactive bots quickly using familiar Laravel patterns.

View on GitHub
Deep Wiki
Context7

Getting Started

Telegraph is the practical Laravel wrapper for the Telegram Bot API: it gives you an eloquent-ish fluent builder for outgoing messages, a webhook endpoint + handler for incoming updates, and DB-backed models for bots and chats.

  1. Install and publish:
composer require defstudio/telegraph
php artisan vendor:publish --tag="telegraph-migrations"
php artisan vendor:publish --tag="telegraph-config"
php artisan migrate
  1. Register a bot (stores the token encrypted, keyed to your bot record):
php artisan telegraph:new-bot
  1. Point Telegram at your app:
php artisan telegraph:set-webhook
# inspect / debug with the webhook-info command shipped by your version
  1. First real use case — the 5-minute win is deploy/ops notifications:
use DefStudio\Telegraph\Facades\Telegraph;

Telegraph::message('🚀 Deployed ' . config('app.env'))->send();

Where to look first, in this order:

  • config/telegraph.php — token/bot resolution, webhook URL, queue, HTTP timeouts, logging channel.
  • src/Models/TelegraphBot.php and TelegraphChat.php — how bots/chats persist and how you address a chat.
  • src/Telegraph.php + the Telegraph facade — the fluent API surface (message(), photo(), keyboard(), edit(), delete(), chatAction()).
  • The webhook handler class referenced in config — that's your inbound entry point.

Implementation Patterns

Outbound: always go through a chat record. Keep TelegraphChat rows for anything you message repeatedly (users, groups, alert channels), then:

$chat->message("Build #{$build->id} failed")->markdown()->send();

This gives you a stable chat_id you can look up by your own domain model instead of scattering IDs in config.

Rich messages, keyboards, edits. The builder composes cleanly — formatting (->markdown(), ->html()), media (->photo(), ->document(), ->location()), and keyboards/buttons for approvals:

$chat->message('Deploy to prod?')
    ->keyboard([[$approve, $reject]])   // Button::make('Approve')->action('approve')
    ->send();

Editing after a button press (->edit()) is the standard pattern for "click to acknowledge" UIs; delete for cleanup.

Queue the sends. Synchronous send() blocks your request/job on a Telegram round-trip. Use the package's queued dispatch (or wrap in your own job) for anything user-facing or fan-out. Ops script: one message, sync is fine. Notification fan-out: queue, always.

Inbound: webhook → idempotent handler. Telegram retries on any non-2xx or timeout, so key your handling on the update payload (update_id) and make handlers replay-safe. Persist whatever the bot needs from the update (chat, user, message intent) before doing slow work, and return 200 fast. Heavy processing belongs in a job dispatched from the handler.

Multi-bot. Bots live in the DB, so adding a second bot is an artisan command, not a deploy. Switch per-request with the bot scoping on the facade; this is the pattern for "public bot + internal ops bot" in one app.

Custom request URL (new in 1.73.0). Requests can now carry an explicit full URL instead of the generated one. Two immediate wins:

// self-hosted / proxied Bot API endpoint, or pinning an absolute webhook URL
Telegraph::message('hello')->url('https://bot-api.internal/...')->send();

Use it for local Bot API servers, egress proxies, and when your webhook URL can't be inferred from APP_URL (containers, blue/green hosts, multi-domain apps). Keep the override in config and read it from one place.

Deferred chat bot (new in 1.73.0). Bot/chat resolution can now be deferred rather than resolved eagerly. Day-to-day, this means cheap request construction — build the message, let the bot bind at send time — and fewer DB hits in flows that build several candidate messages but only send one. Check the PR for exact semantics before restructuring existing code; the safe migration is: build as you always did, resolve explicitly anywhere you read bot metadata ($bot->token, $bot->name) before send().

Testing. Use the package's fake so tests never hit the network, and assert on the outgoing payload (assertSent / assertSentData-style helpers). For webhook handlers, post a fixture update array to the webhook route and assert the DB/job side effects — don't try to unit-test the fluent builder.

Gotchas and Tips

  • APP_KEY rotation kills stored bots. Tokens are encrypted with your app key. Rotating it means re-entering every bot token. Treat bot rows as secrets in backups.
  • Webhooks need public HTTPS. php artisan serve won't cut it — use a tunnel (Expose/ngrok/Valet share) and re-run telegraph:set-webhook every time the tunnel URL changes. This is the #1 "why is nothing arriving" cause.
  • Silent ok: false. Telegram returns HTTP 200 with ok: false for many logical errors (bad chat_id, unescaped Markdown, file too large). Turn on the package's debug logging channel early; don't assume a 200 means delivered.
  • Markdown escaping. Unescaped _, *, [ from user data breaks entities and the whole message fails. Prefer ->html() with e(), or escape interpolated values.
  • Rate limits. ~30 msg/s globally, ~20/min per group. Fan-out loops will get 429 with retry_after; Telegraph surfaces these as exceptions. Throttle your jobs and back off — don't retry immediately.
  • 4096-char message cap. Split long output (logs, stack traces) before sending, or the send fails.
  • Deferred resolution moves errors later (1.73.0). If the bot is resolved lazily, a misconfigured/missing bot now blows up at send() rather than at build time. Anything that inspects bot metadata mid-pipeline should force resolution first; tests should assert on the send, not the construction.
  • Custom URLs must be absolute. A per-request URL override without scheme/host will fail at the HTTP client layer with a confusing error. Normalize it in config (rtrim, validate with filter_var), and remember the override applies only to that request chain — don't assume it sticks for subsequent sends.
  • Queue workers and model serialization. Queued sends serialize the bot/chat; use SerializesModels semantics and make sure a worker is actually running. A "message never arrived" bug is often a stalled worker, not the API.
  • Idempotency keys. Store update_id (unique index) — Telegram will redeliver on any hiccup, and duplicate "Approve deploy" actions are expensive.
  • Extension points worth knowing: the publishable config (HTTP client/timeouts, webhook path, queue connection), the Bot API base URL setting for self-hosted servers (pairs well with the new per-request URL override), and the models themselves — subclassing TelegraphChat to add your own relations is the cleanest way to integrate with existing domain models.
  • Local debugging shortcut: telegraph:webhook-info tells you what Telegram thinks your URL is — check that before debugging your handler.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo