Telegram bot setup
Print Buddy can run its standalone bot on Telegram instead of Discord (#164). The same handlers, prompts and safety gates serve both platforms; only the transport changes. The Telegram bot is a pure bot: it has no LLM. Unmatched text gets the command list, and no [llm] setting is ever consulted for Telegram — that is deliberate, and it is how the no-LLM standalone mode from the standalone design is being proven first.
The bot talks to the Telegram Bot API by long polling, so it needs no inbound port, no public URL and no webhook. It uses only the Python standard library; there is no Telegram package to install.
1. Create the bot with BotFather
- In Telegram, open @BotFather and send
/newbot. - Pick a display name and a username ending in
bot. - BotFather replies with the bot token (it looks like
123456789:AA...). Keep it secret: anyone with the token controls the bot. If it ever leaks, revoke it with/revokein BotFather. - Optionally send
/setcommandsto BotFather and paste the commands fromprint_buddy/bot/commands.py(names use underscores, e.g.print_next). The bot also registers its command menu itself at startup, best-effort, and typed commands work either way.
2. Create the print chat and find the IDs
- Create a Telegram group for printing and add the bot to it. A private one-to-one chat with the bot works too.
- Chat ID: send a message in the group, then read it back from
https://api.telegram.org/bot<token>/getUpdates— thechat.idof the message is the chat ID. Group and supergroup IDs are negative (for example-1001234567890); a one-to-one chat ID is your own positive user ID. Tools like @userinfobot can also show it. - Your user ID: message @userinfobot, or read
from.idin the samegetUpdatesoutput. User IDs are positive numbers. Collect the ID of every person who may print, plus the approver's.
Forum groups (topics enabled) work: the bot scopes each topic like a Discord thread, so a prompt opened in one topic is answered in that topic.
3. Configure Print Buddy
# config/local.yaml
bot:
mode: standalone
platform: telegram
telegram:
chat_id: "-1001234567890"
allowed_user_ids: "424242, 515151"
approval_user_id: "424242"
services:
moonraker_url: http://192.168.1.10:7125Put the token in the environment variable [telegram] token_env names (TELEGRAM_BOT_TOKEN by default) — in the process environment or in the .env file in paths.hermes_home, mode 600. Never put the token in a config file. Every setting can also come from the environment, for example PRINT_BUDDY__TELEGRAM__CHAT_ID=-1001234567890.
Nobody allowed means nobody can use the bot: with no allowed users every message is ignored and check fails.
4. Check and run
export TELEGRAM_BOT_TOKEN=... # or put it in the data folder's .env
python3 -m print_buddy.bot check # offline: mode, token present, chat, allowed users, handlers, no Hermes
python3 -m print_buddy.bot # start pollingPrinter event posts (started, complete, failed, timelapse) come from the listener, unchanged: in standalone mode with platform: telegram it posts through the Telegram Bot API instead of Discord REST.
Pairing with moonraker-telegram-bot (ZMod companion)
If your printer runs ZMod (an AD5X, say), you may already run moonraker-telegram-bot by nlef. It is the bot ZMod itself uses — on a separate host, in place of moonraker-timelapse — and the two bots are complements: they run side by side against the same Moonraker, each with its own token.
| Job | Which bot |
|---|---|
| Prepare a print: link or file in, colour/fit questions, slicing, and a confirmation before anything prints | Print Buddy |
| Operate the running printer: status, pause, resume, cancel, emergency stop, macro buttons, power devices | moonraker-telegram-bot |
| Push notifications with camera photos while a print runs | Either — pick one notifier (below) |
| A rendered timelapse video in the chat | moonraker-telegram-bot (frames by time interval or Z-height, rendered at 15 fps by default) |
Boundaries, stated plainly:
- moonraker-telegram-bot is a separate upstream project. Print Buddy does not bundle it, does not require it, and does not support it — report its problems to its own tracker. It works with Moonraker printers only.
- Its control commands act on the printer as soon as they are tapped. Only the commands in its
[telegram_ui] require_confirmationlist ask first (by default that list covers cancel, emergency stop, shutdown, restarts and power; pause, resume and status are not in it). That is a different safety model from Print Buddy's, where a prepared job prints only after an explicit confirmation. Decide on purpose which bot may control your printer. - Print Buddy's Telegram bot stays a prep bot: it has no live printer control commands, and pairing changes nothing about that.
Two bots, two tokens — never share one
Create two bots with BotFather and give each program its own token. A Telegram bot token supports exactly one getUpdates poller; if both programs poll the same token they steal each other's updates and both misbehave. Print Buddy's token goes in TELEGRAM_BOT_TOKEN (or the [telegram] token_env you configured), the companion's in the bot_token of its own telegram.conf. Both bots can sit in the same Telegram chat.
Pick one notifier
Both bots can post print-started, progress and print-finished messages, so an unconfigured pair posts everything twice. Choose one notifier and silence the other:
- The companion notifies; Print Buddy stays quiet. Do not run Print Buddy's listener: in a standalone install, start
print-buddy-bot.servicebut notprint-buddy-listener.service. Print Buddy has no per-message notification switch — the[listener]settings only set timing intervals, and every event post (started, complete, failed, timelapse) plus the camera-watch alerts and spool questions comes from that one process, so those go quiet too. In Docker the listener is part of the supervised container, so silence the companion instead (next bullet). - Print Buddy notifies; the companion stays quiet. Remove the whole
[progress_notification]section from the companion'stelegram.conf(or never add it). Its notifier is enabled by that section existing at all; without it, the bot sends no print-started, progress or print-finished posts. Its on-demand commands (/statusand the buttons) and its timelapse still work, and it still reports printer errors.
Companion-side sources, checked 2026-10-26: the upstream sample-config wiki page ([progress_notification], [timelapse], [telegram_ui] require_confirmation) and the project's bot/configuration.py / bot/notifications.py, where the notifier's enabled flag is exactly "the [progress_notification] section is present".
How Telegram differs from Discord here
- Buttons: prompts are inline keyboards. Answering edits the message to remove the keyboard and record the choice.
- Alert buttons: the listener's Discord button posts (pause alerts, plate picker, insertion and spool questions) are sent as plain text on Telegram. The text names the typed command or reply that does the same job; nothing is lost except the tap target.
- Messages are plain text. No Markdown parse mode is set, so file names, model names and G-code fragments are shown exactly as written (Discord's
**bold**markers appear literally). - Limits: 4096 characters per message (long replies are split), files up to 50 MB through the Bot API. Model files (.3mf/.stl) arrive as documents; previews and timelapses are sent back as photos/videos when Telegram has a native type for them, everything else as a document.
- Commands arrive as text:
/print-next plan-1. A trailing@YourBotsuffix that group chats add is stripped automatically.
