An open API service indexing awesome lists of open source software.

https://github.com/tomusdrw/telemach-bot

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.
https://github.com/tomusdrw/telemach-bot

Last synced: 4 days ago
JSON representation

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.

Awesome Lists containing this project

README

          

# telemach-bot

[![CI](https://github.com/tomusdrw/telemach-bot/actions/workflows/ci.yml/badge.svg)](https://github.com/tomusdrw/telemach-bot/actions/workflows/ci.yml)

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:

```bash
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`:

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

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

## Development

```bash
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 ` — flip an `APPROVED` user to `REJECTED`. Refuses to act on the admin.
- `/reset ` — 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
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:

```bash
docker compose exec telemach-bot \
sqlite3 /data/bot.db "UPDATE users SET status='APPROVED' WHERE telegram_id=;"
```

- **Disable / un-approve a user (no command for this yet).**

```bash
docker compose exec telemach-bot \
sqlite3 /data/bot.db "UPDATE users SET status='REJECTED' WHERE telegram_id=;"
```

- **See recent activity for a user.**

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

## Updating

```bash
git pull
docker compose up -d --build
```

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