From 1d768c692160bc12a84115210e7d3d64c76bc1d7 Mon Sep 17 00:00:00 2001 From: ppfeiffer Date: Tue, 17 Feb 2026 16:24:26 +0100 Subject: [PATCH] feat: v0.5.7 - Anfragen pro Kanal mit Kanalnamen im Dashboard Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 5 +++++ config.yaml | 2 +- meshbot/bot.py | 2 +- meshbot/database.py | 22 ++++++++++++++++++---- static/index.html | 12 +++++++++--- static/js/dashboard.js | 11 +++++++++++ 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1817ac4..0f8ad48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.5.7] - 2026-02-17 +### Added +- Anfragen-Aufschluesselung pro Kanal mit Kanalnamen im Dashboard +- Channel-Spalte in der commands-Tabelle (mit DB-Migration fuer bestehende DBs) + ## [0.5.6] - 2026-02-17 ### Added - Node-Detail-Modal im Dashboard: Klick auf Node-Zeile oeffnet Modal mit allen Node-Daten diff --git a/config.yaml b/config.yaml index c228a7c..06e8d32 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,4 @@ -version: "0.5.6" +version: "0.5.7" bot: name: "MeshDD-Bot" diff --git a/meshbot/bot.py b/meshbot/bot.py index 9200928..a4e1420 100644 --- a/meshbot/bot.py +++ b/meshbot/bot.py @@ -324,7 +324,7 @@ class MeshBot: if response: await self._send_text(response, channel) - await self.db.insert_command(cmd) + await self.db.insert_command(cmd, channel) if self.ws_manager: stats = await self.db.get_stats() await self.ws_manager.broadcast("stats_update", stats) diff --git a/meshbot/database.py b/meshbot/database.py index 5f522d2..778e450 100644 --- a/meshbot/database.py +++ b/meshbot/database.py @@ -15,6 +15,7 @@ class Database: self.db.row_factory = aiosqlite.Row await self.db.execute("PRAGMA journal_mode=WAL") await self._create_tables() + await self._migrate() logger.info("Database connected: %s", self.db_path) async def close(self): @@ -55,7 +56,8 @@ class Database: CREATE TABLE IF NOT EXISTS commands ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp REAL, - command TEXT + command TEXT, + channel INTEGER ); CREATE TABLE IF NOT EXISTS users ( @@ -91,6 +93,14 @@ class Database: """) await self.db.commit() + async def _migrate(self): + async with self.db.execute("PRAGMA table_info(commands)") as c: + cols = {row[1] async for row in c} + if "channel" not in cols: + await self.db.execute("ALTER TABLE commands ADD COLUMN channel INTEGER") + await self.db.commit() + logger.info("Migration: added channel column to commands table") + # ── Node methods ────────────────────────────────── async def upsert_node(self, node_id: str, **kwargs) -> dict: @@ -164,11 +174,11 @@ class Database: # ── Command methods ─────────────────────────────── - async def insert_command(self, command: str): + async def insert_command(self, command: str, channel: int | None = None): now = time.time() await self.db.execute( - "INSERT INTO commands (timestamp, command) VALUES (?, ?)", - (now, command), + "INSERT INTO commands (timestamp, command, channel) VALUES (?, ?, ?)", + (now, command, channel), ) await self.db.commit() @@ -188,6 +198,10 @@ class Database: "SELECT command, COUNT(*) as cnt FROM commands GROUP BY command ORDER BY cnt DESC" ) as cursor: stats["command_breakdown"] = {row[0]: row[1] async for row in cursor} + async with self.db.execute( + "SELECT channel, COUNT(*) as cnt FROM commands GROUP BY channel ORDER BY cnt DESC" + ) as cursor: + stats["channel_breakdown"] = {row[0]: row[1] async for row in cursor} return stats # ── User methods ────────────────────────────────── diff --git a/static/index.html b/static/index.html index 2018498..fdc1a21 100644 --- a/static/index.html +++ b/static/index.html @@ -104,9 +104,15 @@
-
- Anfragen: - +
+
+ Anfragen: + +
+
+ Kanaele: + +
diff --git a/static/js/dashboard.js b/static/js/dashboard.js index 60ccff5..a0fc42d 100644 --- a/static/js/dashboard.js +++ b/static/js/dashboard.js @@ -175,6 +175,17 @@ function updateStats(stats) { } else { breakdown.innerHTML = 'Noch keine Anfragen'; } + + const chBreakdown = document.getElementById('channelBreakdown'); + const chCounts = stats.channel_breakdown || {}; + if (Object.keys(chCounts).length > 0) { + chBreakdown.innerHTML = Object.entries(chCounts).map(([chIdx, count]) => { + const chName = channels[chIdx] || `Ch ${chIdx}`; + return `${escapeHtml(chName)} ${count}`; + }).join(''); + } else { + chBreakdown.innerHTML = 'Noch keine Anfragen'; + } } function isOnline(lastSeen) {