Skip to content

About

A Telegram bot on webhooks that sleeps between messages, so it runs on the Free plan. Starter template for velixir.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Telegram bot

Deploy on velixir

A Telegram bot you can run on the Free plan. Telegram delivers each message to the app as an HTTPS request, so the bot sleeps while the chat is quiet and wakes up for the next message. Python, with three small dependencies.

Deploy it on velixir, paste in a token from @BotFather, and you have a working bot in a few minutes.

It answers /start, /help and /id, and repeats anything else you send it in a private chat. That is deliberately little. It is the plumbing you would otherwise write first, with a clear place to put your own commands.

What it does

  • Registers its own webhook. On every boot it tells Telegram where to deliver updates: https://<your-app>.velixir.run/telegram, or your own domain.
  • Checks every update. Telegram sends a secret header with each delivery. The app derives that secret from the bot token, compares it in constant time, and rejects anything without it, so nobody else can post fake messages to the URL.
  • Answers commands from a small command table that is easy to extend.
  • Never crash-loops on bad configuration. With no token it serves a page with the BotFather steps. A wrong token, Telegram being unreachable, a reply that fails: each is logged and shown on the status page, and the app keeps running.
  • A status page at / with the bot's username, the webhook state, what Telegram itself reports about deliveries, and the last error. It never shows the token.
  • /healthz for the platform health check.

Why webhooks, and why they suit the Free plan

A Telegram bot either keeps asking Telegram for new messages (long polling) or has Telegram send them over (a webhook). Polling needs a process that never stops. A webhook needs nothing at all between messages.

velixir's Free plan puts an app to sleep after 15 minutes without HTTP traffic and wakes it on the next request. For this bot every message is a request, so it sleeps when the chat goes quiet and wakes when someone writes. It also uses little memory (about 30 MB under load), so 256 MB is plenty.

One thing to expect: the first message after a sleep is answered a few seconds later than the rest, while the app starts. If a delivery fails in that window, Telegram tries it again, so the message is answered late rather than lost. If you want instant replies around the clock, move the app to Hobby, which never sleeps. The code stays the same.

Setting it up on velixir

  1. Create a bot. In Telegram, open @BotFather, send /newbot and choose a name and a username ending in bot. BotFather replies with a token like 123456789:AAH....
  2. Deploy this template with the button above.
  3. Add the token. On the app's Environment tab, add TELEGRAM_BOT_TOKEN with the token as its value, and tick Treat as secret.
  4. Redeploy, with the Redeploy button on the live release in the Deploys tab.

Open the app's URL: the status page should say the webhook is registered. Then send your bot /start.

Until the token is set, the app serves those same steps as its home page rather than crashing, and /healthz stays green.

Using your own domain

By default Telegram delivers to https://<slug>.velixir.run/telegram. To use your own domain:

  1. Add the domain on the app's Domains tab and set up DNS as it describes. Wait until it shows as verified: Telegram only delivers over HTTPS with a valid certificate.
  2. On the Environment tab, set PUBLIC_URL to the address, such as https://bot.example.com (no path).
  3. Redeploy. On boot the bot registers the new address with Telegram.

The velixir.run address keeps serving the status page, but a bot has only one webhook, so from then on every update arrives through your domain.

Running it locally

python -m venv .venv
. .venv/bin/activate            # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python bot.py

Then open http://localhost:8080. With no token set, you get the setup page.

To receive messages on your own machine, Telegram needs an HTTPS address that reaches it. Run a tunnel (such as cloudflared tunnel --url http://localhost:8080), then:

TELEGRAM_BOT_TOKEN=... PUBLIC_URL=https://<your-tunnel-address> python bot.py

Use a second bot for this. A bot has exactly one webhook, so a local run with your live bot's token takes the webhook away from the deployed app until that app next boots. BotFather will make you a test bot in a minute.

Deploying

velixir deploy

Adding a command

Commands live in the COMMANDS table near the top of bot.py. Each one is an async function that gets the message and the text after the command, and returns the reply as Telegram HTML:

import random

async def dice(message: dict, args: str) -> str:
    return f"You rolled a {random.randint(1, 6)}."

COMMANDS["dice"] = ("Roll a die", dice)

It shows up in /help straight away, and in Telegram's / menu after the next deploy. Pass anything that came from a user through html.escape before it goes into a reply.

To react to more than text messages (the buttons of an inline keyboard, edited messages), add the update type to ALLOWED_UPDATES and handle it in handle_update. Telegram only sends the types you ask for, which on the Free plan also means fewer wake-ups.

Things to know

  • Nothing is stored. The counters on the status page live in memory and start again on every restart, and the container's disk is wiped on every deploy. For anything the bot should remember (subscribers, settings, a to-do list), bind a managed Postgres and keep it there.
  • One webhook per bot. Whichever copy of the bot registered last gets the messages, which is why local development belongs on a separate test bot. If another copy takes over, the status page says so.
  • The secret comes from the token. It is an HMAC of the token, so it stays the same across restarts and replicas with nothing to configure. Revoke the token in BotFather, redeploy with the new one, and the secret changes with it.
  • Groups. In a group Telegram only shows a bot the commands meant for it (privacy mode), and this bot only repeats messages in private chats. Use /setprivacy in BotFather if your bot needs to read the whole conversation.
  • Telegram's rate limits apply. It asks bots to stay around one message a second per chat, 20 a minute per group and 30 a second overall. A bot that broadcasts needs a queue.
  • Outbound traffic is web only. Calls to other HTTPS APIs work. SMTP does not, so send email through an HTTP API such as Resend or Postmark.

Configuration

Variable Required Meaning
TELEGRAM_BOT_TOKEN yes The token from @BotFather. Without it, the app serves its setup page.
PUBLIC_URL no Your own domain, such as https://bot.example.com. Defaults to https://<slug>.velixir.run.
TELEGRAM_API_BASE no Where the Bot API lives. Defaults to https://api.telegram.org. Point it at a mock server to test.

Routes

Method Path Does
GET / The status page, or the setup steps when no token is set
POST /telegram Where Telegram delivers updates. Anything without the right secret header gets a 401
GET /healthz 200 while the process runs, with the bot's state in the body

Licence

MIT.

About

A Telegram bot on webhooks that sleeps between messages, so it runs on the Free plan. Starter template for velixir.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages