From 077e0032cf09dc48d677577ee9b2eb1c1d76820c Mon Sep 17 00:00:00 2001 From: ppfeiffer Date: Sun, 15 Feb 2026 14:38:57 +0100 Subject: [PATCH] feat: v0.2.5 - Message splitting, improved /mesh readability - Split long messages at 170 bytes with 1.5s delay between parts - /mesh: proper sections with line breaks and indentation - Hop labels: "Direkt", "1 Hop", "2 Hops" instead of "0h", "1h" Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 8 +++++++ config.yaml | 2 +- meshbot/bot.py | 61 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f42094..d531dd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.2.5] - 2026-02-15 +### Added +- Automatisches Aufteilen langer Nachrichten (max 170 Zeichen) mit 1,5s Pause + +### Changed +- /mesh Befehl: bessere Lesbarkeit mit Absätzen und Einrückungen +- Hop-Verteilung: "Direkt", "1 Hop", "2 Hops" statt "0h", "1h", "2h" + ## [0.2.4] - 2026-02-15 ### Added - Neuer Befehl /mesh - zeigt Mesh-Netzwerk-Infos (Nodes online/gesamt, aktiv 24h, Positionen, Hop-Verteilung, Top-Hardware) diff --git a/config.yaml b/config.yaml index 714370e..7096f13 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,4 @@ -version: "0.2.4" +version: "0.2.5" bot: name: "MeshDD-Bot" diff --git a/meshbot/bot.py b/meshbot/bot.py index b5bed2c..bf7712f 100644 --- a/meshbot/bot.py +++ b/meshbot/bot.py @@ -208,19 +208,51 @@ class MeshBot: response = f"⏱️ Uptime: {self._format_uptime()}" if response: - self._send_text(response, channel) + await self._send_text(response, channel) await self.db.insert_command(cmd) if self.ws_manager: stats = await self.db.get_stats() await self.ws_manager.broadcast("stats_update", stats) - def _send_text(self, text: str, channel: int): - if self.interface: + async def _send_text(self, text: str, channel: int, max_len: int = 170): + if not self.interface: + return + parts = self._split_message(text, max_len) + for i, part in enumerate(parts): + if i > 0: + await asyncio.sleep(1.5) try: - self.interface.sendText(text, channelIndex=channel) + self.interface.sendText(part, channelIndex=channel) except Exception: logger.exception("Error sending text") + @staticmethod + def _split_message(text: str, max_len: int) -> list[str]: + if len(text.encode('utf-8')) <= max_len: + return [text] + lines = text.split('\n') + parts = [] + current = "" + for line in lines: + candidate = f"{current}\n{line}" if current else line + if len(candidate.encode('utf-8')) > max_len: + if current: + parts.append(current) + if len(line.encode('utf-8')) > max_len: + while line: + chunk = line[:max_len] + while len(chunk.encode('utf-8')) > max_len: + chunk = chunk[:-1] + parts.append(chunk) + line = line[len(chunk):] + else: + current = line + else: + current = candidate + if current: + parts.append(current) + return parts + def _format_uptime(self) -> str: elapsed = int(time.time() - self.start_time) days, remainder = divmod(elapsed, 86400) @@ -250,7 +282,10 @@ class MeshBot: h = n.get("hop_count") if h is not None: hop_counts[h] = hop_counts.get(h, 0) + 1 - hop_str = ", ".join(f"{k}h:{v}" for k, v in sorted(hop_counts.items())) + hop_lines = [] + for k in sorted(hop_counts.keys()): + label = "Direkt" if k == 0 else f"{k} Hop{'s' if k > 1 else ''}" + hop_lines.append(f" {label}: {hop_counts[k]}") # Hardware distribution (top 3) hw_counts = {} @@ -259,16 +294,20 @@ class MeshBot: if hw: hw_counts[hw] = hw_counts.get(hw, 0) + 1 top_hw = sorted(hw_counts.items(), key=lambda x: -x[1])[:3] - hw_str = ", ".join(f"{hw}:{cnt}" for hw, cnt in top_hw) + hw_lines = [f" {hw}: {cnt}" for hw, cnt in top_hw] - return ( + parts = [ f"🕸️ Mesh-Netzwerk:\n" f"Nodes: {total} ({online} online)\n" f"Aktiv 24h: {active_24h}\n" - f"Mit Position: {with_pos}\n" - f"Hops: {hop_str or '-'}\n" - f"Hardware: {hw_str or '-'}" - ) + f"Mit Position: {with_pos}", + ] + if hop_lines: + parts.append("📊 Hop-Verteilung:\n" + "\n".join(hop_lines)) + if hw_lines: + parts.append("🔧 Top Hardware:\n" + "\n".join(hw_lines)) + + return "\n\n".join(parts) async def _get_weather(self, from_id: str) -> str: node = await self.db.get_node(from_id)