Add DM support, friend identification, and target pruning
React in 1:1 DMs alongside servers/groups, surface the resolved friend username at startup and in the stats panel, verify reactions actually stick after adding them (with jump links for manual spot-checking), and prune stale targets from stats on each run so only the currently mutual set is shown.
This commit is contained in:
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS targets (
|
||||
target_id TEXT PRIMARY KEY,
|
||||
target_type TEXT NOT NULL CHECK(target_type IN ('guild','group_dm')),
|
||||
target_type TEXT NOT NULL CHECK(target_type IN ('guild','group_dm','dm')),
|
||||
name TEXT,
|
||||
first_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||||
reaction_count INTEGER NOT NULL DEFAULT 0
|
||||
@@ -55,6 +55,28 @@ class Storage:
|
||||
if "message_created_at" not in columns:
|
||||
self._conn.execute("ALTER TABLE reactions_log ADD COLUMN message_created_at TEXT")
|
||||
|
||||
# Older DBs created before 'dm' targets (1:1 DMs) existed have a CHECK
|
||||
# constraint that only allows 'guild'/'group_dm' — rebuild the table
|
||||
# with the wider constraint, preserving all existing rows.
|
||||
targets_sql = self._conn.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name='targets'"
|
||||
).fetchone()
|
||||
if targets_sql and "'dm'" not in targets_sql["sql"]:
|
||||
self._conn.executescript(
|
||||
"""
|
||||
ALTER TABLE targets RENAME TO targets_old;
|
||||
CREATE TABLE targets (
|
||||
target_id TEXT PRIMARY KEY,
|
||||
target_type TEXT NOT NULL CHECK(target_type IN ('guild','group_dm','dm')),
|
||||
name TEXT,
|
||||
first_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||||
reaction_count INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
INSERT INTO targets SELECT * FROM targets_old;
|
||||
DROP TABLE targets_old;
|
||||
"""
|
||||
)
|
||||
|
||||
def upsert_target(self, target_id, target_type: str, name: str) -> None:
|
||||
with self._lock, self._conn:
|
||||
self._conn.execute(
|
||||
@@ -66,6 +88,21 @@ class Storage:
|
||||
(str(target_id), target_type, name),
|
||||
)
|
||||
|
||||
def prune_targets(self, keep_target_ids: list) -> int:
|
||||
"""Remove targets not present in the latest discovery run (e.g. a server
|
||||
that's no longer mutual, or a stale entry from an earlier bug/test run).
|
||||
reactions_log/channel_scan_state rows are left alone for history/undo."""
|
||||
keep = [str(t) for t in keep_target_ids]
|
||||
with self._lock, self._conn:
|
||||
placeholders = ",".join("?" * len(keep)) if keep else None
|
||||
if placeholders:
|
||||
cur = self._conn.execute(
|
||||
f"DELETE FROM targets WHERE target_id NOT IN ({placeholders})", keep
|
||||
)
|
||||
else:
|
||||
cur = self._conn.execute("DELETE FROM targets")
|
||||
return cur.rowcount
|
||||
|
||||
def has_reacted(self, message_id, emoji: str) -> bool:
|
||||
with self._lock:
|
||||
cur = self._conn.execute(
|
||||
|
||||
Reference in New Issue
Block a user