Skip to content

tomusdrw/telemach-bot

Repository files navigation

telemach-bot

CI

A personal Telegram bot that forwards messages, attachments, and transcribed voice notes to your email. Subject lines generated by an LLM via OpenRouter, delivery via Resend. Multi-user with admin approval. Single container, one mounted data volume.

See docs/superpowers/specs/2026-05-18-telegram-email-bot-design.md for the full design.

Quick start (Docker)

  1. Copy .env.example to .env and fill in:

    • TELEGRAM_BOT_TOKEN from @BotFather
    • ADMIN_TELEGRAM_USER_ID (your numeric Telegram user id, ask @userinfobot)
    • ADMIN_EMAIL
    • OPENROUTER_API_KEY (used for both transcription and subject generation)
    • Optionally override OPENROUTER_MODEL (subjects) and OPENROUTER_TRANSCRIPTION_MODEL (voice → text)
    • Optionally set EVENT_MODEL (event extraction) and EXPANSION_MODEL (body cleanup/expansion); both default to OPENROUTER_MODEL
    • RESEND_API_KEY and RESEND_FROM_EMAIL (must be a verified Resend domain)
  2. Create the data directory with the right ownership. The container runs as the node user (UID 1000); the bind-mount preserves host UID, so:

    mkdir -p data
    sudo chown 1000:1000 data

    Skip the chown if your host user is already UID 1000 (typical for a fresh non-root user on most Linux distros).

  3. docker compose up -d --build (or pull the prebuilt image — see below).

  4. DM your bot /start.

Using the prebuilt image from GHCR

Every push to main builds and publishes an image to ghcr.io/tomusdrw/telemach-bot. To pull it instead of building locally, edit docker-compose.yml:

services:
  telemach-bot:
    image: ghcr.io/tomusdrw/telemach-bot:latest   # remove `build: .`
    # ... rest unchanged

Then docker compose pull && docker compose up -d.

Development

npm install
npm test
npm run dev

How it works

  • /start → bot greets, asks for /register your@email.com.
  • /register → bot stores email, DMs admin with Approve/Reject buttons.
  • After approval, forwarded messages are acknowledged with reactions only: 👀 received → ✍ working → 👍 sent (or 💩 on error).
  • A short, single-line message (≤ 80 chars) becomes the email subject verbatim; longer messages get an LLM-generated subject. For every text message the LLM also appends a cleaned-up/expanded rendition (typos fixed, terse notes spelled out) below the original in the body; links are left untouched.
  • Voice messages are transcribed via OpenRouter's /audio/transcriptions endpoint (default model: openai/whisper-large-v3, override with OPENROUTER_TRANSCRIPTION_MODEL). The transcript is sent as the email body; original audio is not attached.
  • Photos/documents/videos/audio/animations/stickers are attached verbatim.
  • Multiple photos uploaded together bundle into one email.
  • When a message contains a date, the bot extracts the event and attaches an .ics calendar file to the email so you can add it to your calendar in one click. A 📅 reaction confirms the attachment was created.
  • /timezone Europe/London sets your per-user IANA timezone (default: Europe/Warsaw). Run /timezone with no argument to see the current value. Affects how calendar invite times are interpreted and displayed.

Admin commands

These work only when invoked by the configured ADMIN_TELEGRAM_USER_ID:

  • /users — list up to 50 most-recent users with status and email.
  • /revoke <telegramId> — flip an APPROVED user to REJECTED. Refuses to act on the admin.
  • /reset <telegramId> — clear the user's email and return them to PENDING_EMAIL so they can re-register.

Troubleshooting

  • 💩 reaction on every message. Check the container logs (docker compose logs -f telemach-bot). The error class (TransientError/FatalError) and provider (whisper / openrouter / resend / telegram / db) are in every error line. The most common first-deploy cause is an unverified RESEND_FROM_EMAIL domain — verify the domain at https://resend.com/domains before sending the first message.

  • Container exits immediately with "Invalid environment". A required env var is missing. The fatal log line lists every missing or invalid var name.

  • EACCES: permission denied, open '/data/bot.db'. The bind-mounted host directory is owned by the wrong UID. Run sudo chown 1000:1000 data on the host.

  • Bot never responds to /start. Double-check TELEGRAM_BOT_TOKEN. The container logs a {"msg":"bot online"} line on successful login.

  • Approved user can't actually send anything. Confirm the admin tapped Approve (look for Approved @username ✓ in your own admin DM). If the status got stuck in PENDING_APPROVAL, fix it directly:

    docker compose exec telemach-bot \
      sqlite3 /data/bot.db "UPDATE users SET status='APPROVED' WHERE telegram_id=<id>;"
  • Disable / un-approve a user (no command for this yet).

    docker compose exec telemach-bot \
      sqlite3 /data/bot.db "UPDATE users SET status='REJECTED' WHERE telegram_id=<id>;"
  • See recent activity for a user.

    docker compose exec telemach-bot \
      sqlite3 /data/bot.db \
      "SELECT created_at, event, details FROM audit_log
         WHERE telegram_id=<id> ORDER BY id DESC LIMIT 20;"

Updating

git pull
docker compose up -d --build

The SQLite file in ./data/bot.db survives rebuilds.

About

A personal Telegram bot that forwards messages, attachments, and transcribed voice notes to your email. Subject lines generated by an LLM via OpenRouter, delivery via Resend. Multi-user with admin approval. Single container, one mounted data volume.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors