feat: metadata enrichment for alerts and subscription plugins

Alert backends now populate structured `extra` field with engagement
metrics (views, stars, votes, etc.) instead of embedding them in titles.
Subscription plugins show richer announcements: Twitch viewer counts,
YouTube views/likes/dates, RSS published dates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
user
2026-02-19 10:00:17 +01:00
parent c3b19feb0f
commit 1fe7da9ed8
10 changed files with 614 additions and 52 deletions

View File

@@ -17,6 +17,7 @@ sys.modules[_spec.name] = _mod
_spec.loader.exec_module(_mod)
from plugins.twitch import ( # noqa: E402
_compact_num,
_delete,
_errors,
_load,
@@ -652,6 +653,17 @@ class TestCmdTwitchList:
assert "broken (error)" in bot.replied[0]
def test_list_shows_live(self):
_clear()
bot = _FakeBot()
_save(bot, "#test:xqc", {
"name": "xqc", "channel": "#test",
"last_error": "", "was_live": True,
"last_viewers": 50000,
})
asyncio.run(cmd_twitch(bot, _msg("!twitch list")))
assert "xqc (live, 50k)" in bot.replied[0]
def test_list_shows_live_no_viewers(self):
_clear()
bot = _FakeBot()
_save(bot, "#test:xqc", {
@@ -725,8 +737,10 @@ class TestCmdTwitchCheck:
assert len(announcements) == 1
assert "[xqc] is live" in announcements[0]
assert "Fortnite" in announcements[0]
# Check reply shows live status
assert "| 50k viewers" in announcements[0]
# Check reply shows live status with viewers
assert "xqc: live" in bot.replied[0]
assert "| 50k viewers" in bot.replied[0]
asyncio.run(inner())
@@ -794,6 +808,7 @@ class TestPollOnce:
assert "[xqc] is live" in messages[0]
assert "Playing games" in messages[0]
assert "Fortnite" in messages[0]
assert "| 50k viewers" in messages[0]
assert "https://twitch.tv/xqc" in messages[0]
updated = _load(bot, key)
assert updated["was_live"] is True
@@ -941,6 +956,7 @@ class TestPollOnce:
assert len(messages) == 1
assert "Just chatting" in messages[0]
assert "(" not in messages[0] # No game parenthetical
assert "| 100 viewers" in messages[0]
asyncio.run(inner())
@@ -1132,3 +1148,33 @@ class TestCmdTwitchUsage:
bot = _FakeBot()
asyncio.run(cmd_twitch(bot, _msg("!twitch foobar")))
assert "Usage:" in bot.replied[0]
# ---------------------------------------------------------------------------
# TestCompactNum
# ---------------------------------------------------------------------------
class TestCompactNum:
def test_zero(self):
assert _compact_num(0) == "0"
def test_small(self):
assert _compact_num(999) == "999"
def test_one_k(self):
assert _compact_num(1000) == "1k"
def test_fractional_k(self):
assert _compact_num(1500) == "1.5k"
def test_one_m(self):
assert _compact_num(1_000_000) == "1M"
def test_fractional_m(self):
assert _compact_num(2_500_000) == "2.5M"
def test_fifty_k(self):
assert _compact_num(50000) == "50k"
def test_hundred(self):
assert _compact_num(100) == "100"