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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user