From 0b7950cefa06df915d5a6d7ae5aeccd0227fbd9d Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Sun, 15 Feb 2026 22:26:38 -0600 Subject: [PATCH] Add state tracking and notifications DB support Add kv_scan() for prefix-based key-value lookups (used by ClickUp task state tracking). Add notifications table with add_notification() and get_notifications_after() for the UI-agnostic notification bus. Co-Authored-By: Claude Opus 4.6 --- cheddahbot/db.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cheddahbot/db.py b/cheddahbot/db.py index 9df2ba3..efbf6ed 100644 --- a/cheddahbot/db.py +++ b/cheddahbot/db.py @@ -65,6 +65,12 @@ class Database: key TEXT PRIMARY KEY, value TEXT NOT NULL ); + CREATE TABLE IF NOT EXISTS notifications ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + message TEXT NOT NULL, + category TEXT NOT NULL DEFAULT 'clickup', + created_at TEXT NOT NULL + ); """) self._conn.commit() @@ -192,6 +198,32 @@ class Database: row = self._conn.execute("SELECT value FROM kv_store WHERE key = ?", (key,)).fetchone() return row["value"] if row else None + def kv_scan(self, prefix: str) -> list[tuple[str, str]]: + """Return all key-value pairs where key starts with prefix.""" + rows = self._conn.execute( + "SELECT key, value FROM kv_store WHERE key LIKE ?", (prefix + "%",) + ).fetchall() + return [(r["key"], r["value"]) for r in rows] + + # -- Notifications -- + + def add_notification(self, message: str, category: str = "clickup") -> int: + now = _now() + cur = self._conn.execute( + "INSERT INTO notifications (message, category, created_at) VALUES (?, ?, ?)", + (message, category, now), + ) + self._conn.commit() + return cur.lastrowid + + def get_notifications_after(self, after_id: int = 0, limit: int = 50) -> list[dict]: + """Get notifications with id > after_id.""" + rows = self._conn.execute( + "SELECT id, message, category, created_at FROM notifications WHERE id > ? ORDER BY id ASC LIMIT ?", + (after_id, limit), + ).fetchall() + return [dict(r) for r in rows] + def _now() -> str: return datetime.now(timezone.utc).isoformat()