From c5015f21606875a41e0caa16871ec4ed71412370 Mon Sep 17 00:00:00 2001 From: KoenigMjr <135820716+KoenigMjr@users.noreply.github.com> Date: Mon, 17 Nov 2025 18:31:34 +0100 Subject: [PATCH] =?UTF-8?q?enh(telegram):=20parse=5Fmode=20Unterst=C3=BCtz?= =?UTF-8?q?ung=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Internationalisierung der Kommentare - parse_mode hinzugefügt (für Formatierungsmöglichkeiten) mit Auswahlmöglichkeit "HTML" und "MarkdownV2" - Ergänzung in Dokumentation - kleinere Korrekturen in Dokumentation - Dokumentation um die Möglichkeit von Block-Strings (|) ergänzt (Danke sm7tix für den Input!) --- docu/docs/plugin/telegram.md | 53 ++++++++++++++++++++++-------------- plugin/telegram.py | 33 ++++++++++++---------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/docu/docs/plugin/telegram.md b/docu/docs/plugin/telegram.md index 8c23bbd..45e10c5 100644 --- a/docu/docs/plugin/telegram.md +++ b/docu/docs/plugin/telegram.md @@ -2,16 +2,16 @@ --- ## Beschreibung -Mit diesem Plugin ist es moeglich, Telegram-Nachrichten für POCSAG-Alarmierungen zu senden. -Außerdem werden Locations versendet, wenn die Felder `lat` und `lon` im Paket definiert sind. (beispielsweise durch das [Geocoding](../modul/geocoding.md) Modul) +Dieses Plugin ermöglicht das Versenden von Telegram-Nachrichten für verschiedene Alarmierungsarten. +Wenn im eingehenden Paket die Felder `lat` und `lon` vorhanden sind (z. B. durch das [Geocoding](../modul/geocoding.md) Modul), wird zusätzlich automatisch der Standort als Telegram-Location gesendet. -Die abarbeitung der Alarmierungen erfolgt per Queue nach den Limits der Telegram API, damit keine Nachrichten verloren gehen, diese Funktion kann mit dem ```queue``` Parameter deaktiviert werden. +Das Senden der Nachrichten erfolgt über eine interne Queue mit Retry-Logik und exponentiellem Backoff, um die Vorgaben der Telegram API einzuhalten und Nachrichtenverluste zu verhindern. Die Retry-Parameter (max_retries, initial_delay, max_delay) können in der Konfiguration angepasst werden. ## Unterstütze Alarmtypen -- Fms -- Pocsag -- Zvei -- Msg +- FMS +- POCSAG +- ZVEI +- MSG ## Resource `telegram` @@ -20,16 +20,17 @@ Die abarbeitung der Alarmierungen erfolgt per Queue nach den Limits der Telegram |Feld|Beschreibung|Default| |----|------------|-------| -|botToken|Der Api-Key des Telegram-Bots|| -|chatIds|Liste mit Chat-Ids der Empfängers / der Emfänger-Gruppen|| -|startup_message|Nachricht, dass das Telegram-Plugin erfolgreich geladen wurde|leer| -|message_fms|Format der Nachricht für FMS|`{FMS}`| -|message_pocsag|Format der Nachricht für Pocsag|`{RIC}({SRIC})\n{MSG}`| -|message_zvei|Format der Nachricht für ZVEI|`{TONE}`| -|message_msg|Format der Nachricht für MSG|| -|max_retries|Anzahl der Versuche, bis das Senden abgebrochen wird|5| -|initial_delay|Verzögerung des zweiten Sendeversuchs|2 [Sek.]| -|max_delay|Maximale Verzögerung|60 [Sek.]| +|botToken|Der Api-Key des Telegram-Bots|-| +|chatIds|Liste mit Chat-Ids der Empfängers / der Empfänger-Gruppen|-| +|startup_message|Nachricht beim erfolgreichen Initialisieren des Plugins|leer| +|message_fms|Formatvorlage für FMS-Alarm|`{FMS}`| +|message_pocsag|Formatvorlage für POCSAG|`{RIC}({SRIC})\n{MSG}`| +|message_zvei|Formatvorlage für ZVEI|`{TONE}`| +|message_msg|Formatvorlage für MSG-Nachricht|-| +|max_retries|Anzahl Wiederholungsversuche bei Fehlern|5| +|initial_delay|Initiale Wartezeit bei Wiederholungsversuchen|2 [Sek.]| +|max_delay|Maximale Retry-Verzögerung|300 [Sek.]| +|parse_mode|Formatierung ("HTML" oder "MarkdownV2"), Case-sensitive!|leer| **Beispiel:** ```yaml @@ -37,20 +38,32 @@ Die abarbeitung der Alarmierungen erfolgt per Queue nach den Limits der Telegram name: Telegram Plugin res: telegram config: - message_pocsag: "{RIC}({SRIC})\n{MSG}" + message_pocsag: | + POCSAG Alarm: + RIC: {RIC} ({SRIC}) + {MSG} + parse_mode: "HTML" startup_message: "Server up and running!" botToken: "BOT_TOKEN" chatIds: - "CHAT_ID" ``` +Hinweis: +Über parse_mode kannst du Telegram-Formatierungen verwenden: + +- HTML: `fett`, `kursiv`, `unterstrichen`, `durchgestrichen`, ... +- MarkdownV2: `**fett**`, `__unterstrichen__`, `_italic \*text_` usw. (Escape-Regeln beachten) + +Block-Strings (|) eignen sich perfekt für mehrzeilige Nachrichten und vermeiden Escape-Zeichen wie \n + --- ## Modul Abhängigkeiten -Aus dem Modul [Geocoding](../modul/geocoding.md) (optional/nur POCSAG): +OPTIONAL, nur für POCSAG-Locationversand: Aus dem Modul [Geocoding](../modul/geocoding.md): - `lat` - `lon` --- ## Externe Abhängigkeiten -- python-telegram-bot +keine diff --git a/plugin/telegram.py b/plugin/telegram.py index a73a222..18029a9 100644 --- a/plugin/telegram.py +++ b/plugin/telegram.py @@ -10,7 +10,7 @@ r"""! by Bastian Schroll @file: telegram.py -@date: 12.07.2025 +@date: 17.11.2025 @author: Claus Schichl nach der Idee von Jan Speller @description: Telegram-Plugin mit Retry-Logik ohne externe Telegram-Abhängigkeiten """ @@ -31,16 +31,17 @@ logger = logging.getLogger(__name__) # =========================== -# TelegramSender-Klasse +# TelegramSender-Class # =========================== class TelegramSender: - def __init__(self, bot_token, chat_ids, max_retries=None, initial_delay=None, max_delay=None): + def __init__(self, bot_token, chat_ids, max_retries=None, initial_delay=None, max_delay=None, parse_mode=None): self.bot_token = bot_token self.chat_ids = chat_ids self.max_retries = max_retries if max_retries is not None else 5 self.initial_delay = initial_delay if initial_delay is not None else 2 self.max_delay = max_delay if max_delay is not None else 300 + self.parse_mode = parse_mode self.msg_queue = queue.Queue() self._worker = threading.Thread(target=self._worker_loop, daemon=True) self._worker.start() @@ -75,11 +76,11 @@ class TelegramSender: logger.warning(f"Erneutes Einreihen der Nachricht (Versuch {retry_count + 1}).") self.msg_queue.put((msg_type, chat_id, content, retry_count + 1)) - # Nutze den von Telegram gelieferten Wert (retry_after), falls vorhanden + # use the Telegram-provided value (retry_after) if available wait_time = custom_delay if custom_delay is not None else delay time.sleep(wait_time) - # Erhöhe Delay für den nächsten Versuch (exponentielles Backoff) + # increase delay for the next attempt (exponential backoff) delay = min(delay * 2, self.max_delay) except Exception as e: @@ -91,8 +92,10 @@ class TelegramSender: url = f"https://api.telegram.org/bot{self.bot_token}/sendMessage" payload = { 'chat_id': chat_id, - 'text': content + 'text': content, } + if self.parse_mode: + payload['parse_mode'] = self.parse_mode elif msg_type == "location": url = f"https://api.telegram.org/bot{self.bot_token}/sendLocation" payload = { @@ -101,25 +104,25 @@ class TelegramSender: } else: logger.error("Unbekannter Nachrichtentyp.") - return False, True, None # Unbekannter Typ = permanent falsch + return False, True, None # unknown message type = permanent failure try: - custom_delay = None # Standardwert für Rückgabe, außer bei 429 + custom_delay = None # standardvalue for return, except in case of 429 response = requests.post(url, data=payload, timeout=10) if response.status_code == 429: custom_delay = response.json().get("parameters", {}).get("retry_after", 5) logger.warning(f"Rate Limit erreicht – warte {custom_delay} Sekunden.") - return False, False, custom_delay # Telegram gibt genaue Wartezeit vor + return False, False, custom_delay # Telegram gives exact wait time if response.status_code == 400: logger.error("Ungültige Parameter – Nachricht wird nicht erneut gesendet.") - return False, True, custom_delay # Permanent fehlerhaft + return False, True, custom_delay # permanent failure if response.status_code == 401: logger.critical("Ungültiger Bot-Token – bitte prüfen!") - return False, True, custom_delay # Permanent fehlerhaft + return False, True, custom_delay # permanent failure response.raise_for_status() logger.info(f"Erfolgreich gesendet an Chat-ID {chat_id}") @@ -131,7 +134,7 @@ class TelegramSender: # =========================== -# BoswatchPlugin-Klasse +# BoswatchPlugin-Class # =========================== @@ -149,17 +152,19 @@ class BoswatchPlugin(PluginBase): logger.error("botToken oder chatIds fehlen in der Konfiguration!") return - # Konfigurierbare Parameter mit Fallback-Defaults + # configurable parameters with fallback defaults max_retries = self.config.get("max_retries") initial_delay = self.config.get("initial_delay") max_delay = self.config.get("max_delay") + parse_mode = self.config.get("parse_mode") self.sender = TelegramSender( bot_token=bot_token, chat_ids=chat_ids, max_retries=max_retries, initial_delay=initial_delay, - max_delay=max_delay + max_delay=max_delay, + parse_mode=parse_mode ) startup_message = self.config.get("startup_message")