brandmeister-lastheard-tele.../README.md
2026-06-08 23:23:42 +02:00

96 lines
3.6 KiB
Markdown

# BrandMeister LastHeard Telegram Bot
A small bot that listens to the [BrandMeister](https://brandmeister.network) DMR
"LastHeard" stream and posts a formatted message to a Telegram channel whenever a
relevant transmission ends. It is useful for keeping a club or regional channel
informed about activity on a specific Talkgroup or repeater cluster.
## What it does
The bot connects to the BrandMeister LastHeard Socket.IO stream and reacts to
`Session-Stop` events. It sends a Telegram message when **either** of the
following matches:
- **Mirror Talkgroup** — the transmission's destination is the Talkgroup you set
via `TG_TO_WATCH`.
- **TG8 cluster** — the transmission is on TG8 *and* came in via a repeater that
belongs to the cluster named in `CLUSTER_TO_WATCH` (only when `CLUSTER_WATCH`
is enabled). Cluster membership is resolved at startup from the BrandMeister
API.
Only recent events (stopped within the last ~20 seconds) trigger a message, so
old/replayed events are ignored.
Each Telegram message includes the source callsign, ID, name, talker alias, the
repeater/hotspot used to access the network, slot, Talkgroup, master, RSSI and
BER.
## Requirements
- A Telegram bot token (create one via [@BotFather](https://t.me/BotFather)) and
a channel the bot can post to.
- Either Docker (recommended) or Python 3.12+.
## Configuration
The bot is configured entirely through environment variables. Copy the example
file and fill in your values:
```bash
cp .env.example .env
```
| Variable | Required | Description |
| ------------------ | -------- | --------------------------------------------------------------------------- |
| `TG_TO_WATCH` | yes | Talkgroup ID to mirror to Telegram (e.g. `26235`). |
| `CLUSTER_TO_WATCH` | yes | BrandMeister cluster name used for the TG8 check (e.g. `Cluster Name`). |
| `TELEGRAM_CHANNEL` | yes | Target channel/chat ID (e.g. `@your_channel`). |
| `TELEGRAM_API_KEY` | yes | Telegram bot token from BotFather. |
| `CLUSTER_WATCH` | no | `true`/`false` — enable the TG8 cluster check. Defaults to `true`. |
| `SLOT_EXCLUDE` | no | Comma-separated `repeaterid:slot` pairs that never count as a slot match (e.g. `262399:2,262383:2`). |
| `TG8_EXCLUDE_REPEATERS`| no | Comma-separated repeater IDs whose TG8 traffic is ignored (e.g. `262399`). |
## Running
### With Docker Compose (recommended)
```bash
docker compose up -d --build
```
The service restarts automatically unless explicitly stopped. View logs with:
```bash
docker compose logs -f
```
### With plain Docker
```bash
docker build -t bm-lh .
docker run --env-file .env --restart unless-stopped bm-lh
```
### Locally with Python
```bash
pip install -r requirements.txt
set -a && source .env && set +a # load env vars into the shell
python -u main.py
```
## How it works
1. On startup the bot queries the BrandMeister API (`/v2/cluster`) to find the
cluster matching `CLUSTER_TO_WATCH`, then fetches its member repeaters
(`/v2/cluster/<id>/members`).
2. It opens a WebSocket connection to the LastHeard stream
(`https://api.brandmeister.network`, path `/lh/socket.io`) and subscribes by
emitting `join` with `everything`.
3. For every incoming `mqtt` event it parses the payload and decides whether to
forward a Telegram message based on the rules described above.
4. The connection auto-reconnects on errors.
## License
Released under the [MIT License](LICENSE).