No description
Find a file
2026-06-08 23:23:42 +02:00
.dockerignore Initial Commit 2026-06-08 23:23:42 +02:00
.env.example Initial Commit 2026-06-08 23:23:42 +02:00
.gitignore Initial Commit 2026-06-08 23:23:42 +02:00
compose.yml Initial Commit 2026-06-08 23:23:42 +02:00
Dockerfile Initial Commit 2026-06-08 23:23:42 +02:00
LICENSE Initial Commit 2026-06-08 23:23:42 +02:00
main.py Initial Commit 2026-06-08 23:23:42 +02:00
README.md Initial Commit 2026-06-08 23:23:42 +02:00
requirements.txt Initial Commit 2026-06-08 23:23:42 +02:00

BrandMeister LastHeard Telegram Bot

A small bot that listens to the BrandMeister 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) 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:

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

docker compose up -d --build

The service restarts automatically unless explicitly stopped. View logs with:

docker compose logs -f

With plain Docker

docker build -t bm-lh .
docker run --env-file .env --restart unless-stopped bm-lh

Locally with Python

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.