Sign inSign up

viperadnan/telegram-forwarder-bot

By viperadnan

•Updated about 1 month ago

Forward Telegram messages between chats using pure Telegram Bot API and Webhooks

Image
1

4.5K

viperadnan/telegram-forwarder-bot repository overview

⁠telegram-forwarder-bot

Forwards messages from one chat to many, each destination with its own filters and caption rules — configured from a Telegram Mini App.

Build Container image grammY Svelte Drizzle ORM Image size Repo size License


⁠Features

  • One source, many destinations. Each destination is configured independently — its own filters, mode and caption rules.
  • Copy or forward. Plus protected content, silent delivery and button removal.
  • Filters that compose. Allow and block lists over keywords, patterns, media type or sender. Blocking always wins.
  • Caption transforms. Prepend, append, find and replace, strip links or mentions.
  • Formatting survives. Entity offsets are recomputed around every edit, not dropped.
  • Albums stay albums. Grouped media arrives grouped, not split into separate messages.
  • Linear-time patterns. Regex runs on RE2, so a user pattern cannot hang the bot.
  • Native chat picker. Telegram's own chat list, so there are no ids to type.
  • Self-service cloning. Telegram creates the bot, or paste a BotFather token. Tokens are never stored.

⁠Commands

Command
/setPick source and destination from a list
/set (source) (destination)Add a forward without the picker
/rem (source) (destination)Remove one destination, or all with /rem (source)
/get (source)List a source's destinations, or everything with /get
/settingsOpen the Mini App
/cancelStop a half-finished /set
/set_owner (user_id)Transfer ownership
/helpHow it works

/set, /get and /rem are owner-only.

Anywhere a chat is expected you can pass a chat id, an @username or a t.me link — including a message link, which yields the chat it belongs to. Invite links carry no id and cannot be used. /set_owner takes a numeric user id: Telegram resolves a @username only for public chats, never for a person.


⁠Mini App

Reachable from the chat menu button, /settings or /help. Requires WEBHOOK_HOST.

  • Status — pause a destination without deleting it
  • Delivery — copy or forward, protected, silent, remove buttons
  • Filters — allow-only and never-forward, each on its own screen
  • Caption — remove, prepend, append, find and replace, strip links or mentions

Rules are validated as you type, against the same schema the server saves with. Lookbehind and backreferences are unsupported by RE2 and rejected. Text that is matched against is trimmed, so an invisible trailing space cannot silently stop a rule matching. Prepended and appended text keeps its own newlines and goes on its own line, so a signature needs no blank line typed in front of it.

Only the owner can open it. Everyone else lands on a page for setting up their own bot, created by Telegram or from a BotFather token. Pasting the token of a bot already running here hands it back, which is how you recover a bot whose ownership you lost.

⁠Bot Management Mode

With it on, a stranger taps Create your own bot, names it, and Telegram creates it. Off, only the BotFather route is offered.

Enable it on the bot that does the creating; the bots it creates do not need it:

  1. Open BotFather⁠ and send /mybots
  2. Pick the bot → Bot Settings → Bot Management Mode → Enable
  3. Restart this instance — the flag is read at startup

Owners of a created bot also get Replace token and Delete setup under Settings → Owner, both acting through the bot that created it.

⁠Open button

Telegram shows an Open button for bots with a Main Mini App. No Bot API method sets it, so each bot's owner does it once in BotFather:

/mybots → pick the bot → Bot Settings → Configure Mini App → Enable Mini App

Then give it https://<WEBHOOK_HOST>/app/settings?bot=<bot_id>. Keep the ?bot= — a Main Mini App launched from a profile carries no start parameter, so the query string is what tells the page which bot it is configuring.


⁠Configuration

Copy .env.sample⁠ to .env.

Variable
BOT_TOKENRequired. From BotFather⁠
DATABASE_URLRequired. PostgreSQL connection string, the source of truth
WEBHOOK_HOSTRequired. Public HTTPS URL of this server
REDIS_URIShares the cache between instances and across restarts. One long-running container does not need it — the in-process cache is faster
CACHE_TTL_SECONDSDefault 3600. Writes invalidate what they affect, so this only bounds staleness from direct SQL or unshared instances
CACHE_MAX_ENTRIESIn-process cache cap, default 10000
DIRECT_DATABASE_URLNon-pooled connection for migrations. Set it when DATABASE_URL points at a transaction pooler
DATABASE_POOL_MAXDefault 10
LOG_LEVELDefault debug, or info when NODE_ENV=production

⁠Running

bun install
bun run build      # Mini App, then the server bundle
bun start

Development: bun run dev for the server, bun run dev:web for the Mini App, bun run verify for lint, typecheck and tests.

⁠Docker
docker build -t telegram-forwarder-bot .
docker run -d --env-file .env -p 3000:3000 telegram-forwarder-bot
⁠Behind a tunnel

The webhook needs a public HTTPS URL. Cloudflare Tunnel⁠ gives you one:

cloudflared tunnel --url http://localhost:3000

Set WEBHOOK_HOST to the URL it prints.


⁠Database

Migrations use the drizzle-kit CLI, a dev dependency, so run them from a checkout rather than the production image:

bun install
bun run db:migrate

After editing src/db/schema.ts, regenerate with bun run db:generate and apply. bun run db:studio opens a browser UI over the database.

Migrate before deploying the code that needs it, and roll back in the reverse order — revert the deploy, then the schema.

Upgrading from a Redis-backed version? Backfill once — it reads Redis, writes Postgres and deletes nothing:

REDIS_URI=<old-redis-uri> bun run migrate:redis

Chats imported this way have placeholder names until you tap Refresh chat names in the Mini App.

⁠Upgrading from bot-name modifiers

~ and | in a bot's name used to set protected content and caption stripping for every route. They no longer do anything — both are per-destination settings, set in the Mini App. Drop the characters from the name and turn the settings on for the destinations that want them.


⁠Contributing

Pull requests welcome. Open an issue first for anything substantial.

⁠License

GPL-3.0-or-later — see LICENSE⁠.

Tag summary

Content type

Image

Digest

sha256:7d499a449…

Size

64 MB

Last updated

about 1 month ago

docker pull viperadnan/telegram-forwarder-bot