Add a live status line to the stats panel

Shows what's happening right now underneath the table (connecting,
resolving the friend, discovering targets, fetching channel history,
waiting out a jittered delay before reacting, sending a reaction,
removing a reaction during --undo, etc). Status updates are cheap
and DB-free, using the last fetched stats for re-rendering.
This commit is contained in:
2026-08-01 18:53:48 +02:00
parent ab0afa149a
commit 7b3c358b05
5 changed files with 57 additions and 11 deletions

View File

@@ -1,18 +1,19 @@
import asyncio
from typing import List, Optional
from rich.console import Console
from rich.console import Console, Group
from rich.live import Live
from rich.panel import Panel
from rich.progress_bar import ProgressBar
from rich.table import Table
from rich.text import Text
from bot.storage import Storage
_TYPE_LABELS = {"guild": "server", "group_dm": "group", "dm": "DM"}
def _render(stats: List[dict], friend_label: Optional[str] = None) -> Panel:
def _render(stats: List[dict], friend_label: Optional[str] = None, status: Optional[str] = None) -> Panel:
table = Table(expand=True)
table.add_column("Target")
table.add_column("Type")
@@ -40,8 +41,12 @@ def _render(stats: List[dict], friend_label: Optional[str] = None) -> Panel:
if not stats:
table.add_row("(no targets discovered yet)", "-", "-", "-", "-")
content = table
if status:
content = Group(table, Text(f"{status}", style="dim italic"))
who = f" for {friend_label}" if friend_label else ""
return Panel(table, title=f"Reaction stats{who} — total: {total_reacted}/{total_found}", border_style="cyan")
return Panel(content, title=f"Reaction stats{who} — total: {total_reacted}/{total_found}", border_style="cyan")
class StatsDisplay:
@@ -52,8 +57,10 @@ class StatsDisplay:
self.console = console
self.storage = storage
self.friend_label: Optional[str] = None
self.status: str = "Starting..."
self._last_stats: List[dict] = []
self._live = Live(
_render([]),
_render([], None, self.status),
console=console,
refresh_per_second=4,
transient=False,
@@ -69,10 +76,16 @@ class StatsDisplay:
def set_friend(self, label: str) -> None:
self.friend_label = label
def set_status(self, text: str) -> None:
"""Update the "what's happening right now" line without hitting the
DB — cheap enough to call for every jittered wait / API call."""
self.status = text
self._live.update(_render(self._last_stats, self.friend_label, self.status))
def refresh_sync(self) -> None:
stats = self.storage.get_stats()
self._live.update(_render(stats, self.friend_label))
self._last_stats = self.storage.get_stats()
self._live.update(_render(self._last_stats, self.friend_label, self.status))
async def refresh(self) -> None:
stats = await asyncio.to_thread(self.storage.get_stats)
self._live.update(_render(stats, self.friend_label))
self._last_stats = await asyncio.to_thread(self.storage.get_stats)
self._live.update(_render(self._last_stats, self.friend_label, self.status))