wyr: track real reaction votes and announce winner with counts
Lint / Shell (shellcheck) (push) Successful in 27s
Lint / JS (eslint) (push) Successful in 14s
Lint / Python (ruff) (push) Successful in 11s
Lint / Python deps (pip-audit) (push) Successful in 45s
Lint / Secret scan (gitleaks) (push) Successful in 5s

- Add _WYR_POLLS dict keyed by poll event_id to accumulate votes
- record_wyr_vote() called from callbacks.reaction() on every reaction
- reveal() reads actual vote counts and announces winner with percentage
- Handles tie and zero-vote cases
- Remove the useless 'check the reactions above' message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 01:02:25 -04:00
parent 6c00e8b4fd
commit c9d9febbe0
2 changed files with 53 additions and 16 deletions
+2 -1
View File
@@ -4,7 +4,7 @@ from functools import wraps
from nio import AsyncClient from nio import AsyncClient
from config import BOT_PREFIX, MATRIX_USER_ID from config import BOT_PREFIX, MATRIX_USER_ID
from commands import COMMANDS, metrics, check_scramble_answer, check_riddle_answer from commands import COMMANDS, metrics, check_scramble_answer, check_riddle_answer, record_wyr_vote
from welcome import handle_welcome_reaction, handle_space_join, SPACE_ROOM_ID from welcome import handle_welcome_reaction, handle_space_join, SPACE_ROOM_ID
logger = logging.getLogger("matrixbot") logger = logging.getLogger("matrixbot")
@@ -94,6 +94,7 @@ class Callbacks:
await handle_welcome_reaction( await handle_welcome_reaction(
self.client, room.room_id, event.sender, reacted_event_id, key self.client, room.room_id, event.sender, reacted_event_id, key
) )
record_wyr_vote(reacted_event_id, event.sender, key)
async def member(self, room, event): async def member(self, room, event):
"""Handle m.room.member events — watch for Space joins.""" """Handle m.room.member events — watch for Space joins."""
+51 -15
View File
@@ -1558,6 +1558,22 @@ async def check_scramble_answer(client: AsyncClient, room_id: str, sender: str,
# Would You Rather (WYR) # Would You Rather (WYR)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Keyed by the poll event_id; each value: {"option_a": str, "option_b": str, "votes": {"🅰️": set(), "🅱️": set()}}
_WYR_POLLS: dict[str, dict] = {}
def record_wyr_vote(event_id: str, sender: str, key: str) -> None:
"""Called from callbacks when a reaction is added to a WYR poll message."""
if event_id not in _WYR_POLLS:
return
poll = _WYR_POLLS[event_id]
# Remove sender from both buckets first (prevent double-voting)
for bucket in poll["votes"].values():
bucket.discard(sender)
if key in poll["votes"]:
poll["votes"][key].add(sender)
async def _generate_wyr() -> dict | None: async def _generate_wyr() -> dict | None:
system_msg = ( system_msg = (
"You are a game host generating Would You Rather questions. " "You are a game host generating Would You Rather questions. "
@@ -1625,25 +1641,45 @@ async def cmd_wyr(client: AsyncClient, room_id: str, sender: str, args: str):
resp = await send_html(client, room_id, plain, html) resp = await send_html(client, room_id, plain, html)
if hasattr(resp, "event_id"): if hasattr(resp, "event_id"):
await send_reaction(client, room_id, resp.event_id, "🅰️") poll_event_id = resp.event_id
await send_reaction(client, room_id, resp.event_id, "🅱️") _WYR_POLLS[poll_event_id] = {
"option_a": wyr["option_a"],
"option_b": wyr["option_b"],
"votes": {"🅰️": set(), "🅱️": set()},
}
await send_reaction(client, room_id, poll_event_id, "🅰️")
await send_reaction(client, room_id, poll_event_id, "🅱️")
async def reveal(): async def reveal():
await asyncio.sleep(30) await asyncio.sleep(30)
# Count reactions via room state — we just tally what we have poll = _WYR_POLLS.pop(poll_event_id, None)
# Since we can't easily query reaction counts via nio without extra API calls, votes_a = len(poll["votes"]["🅰️"]) if poll else 0
# we announce the results and encourage re-voting awareness votes_b = len(poll["votes"]["🅱️"]) if poll else 0
plain_r = ( total = votes_a + votes_b
f"⏰ WYR Results!\n"
f"🅰️ {wyr['option_a']}\n" opt_a = wyr["option_a"]
f"🅱️ {wyr['option_b']}\n" opt_b = wyr["option_b"]
f"Check the reactions above to see which won!"
) if total == 0:
result_line = "No votes — you're all cowards. 🐔"
result_html = "<em>No votes — you're all cowards. 🐔</em>"
elif votes_a > votes_b:
pct = round(votes_a / total * 100)
result_line = f"🅰️ {opt_a} wins! ({votes_a} vs {votes_b}{pct}%)"
result_html = f'🅰️ <strong>{opt_a}</strong> wins! <em>({votes_a} vs {votes_b}{pct}%)</em>'
elif votes_b > votes_a:
pct = round(votes_b / total * 100)
result_line = f"🅱️ {opt_b} wins! ({votes_b} vs {votes_a}{pct}%)"
result_html = f'🅱️ <strong>{opt_b}</strong> wins! <em>({votes_b} vs {votes_a}{pct}%)</em>'
else:
result_line = f"It's a tie! ({votes_a} each)"
result_html = f"It's a tie! <em>({votes_a} each)</em>"
plain_r = f"⏰ WYR Results!\n{wyr['question']}\n{result_line}"
html_r = ( html_r = (
f'<font color="#a855f7"><strong>⏰ WYR — Time\'s up!</strong></font><br>' f'<font color="#a855f7"><strong>⏰ WYR — Results!</strong></font><br>'
f'🅰️ <strong>{wyr["option_a"]}</strong><br>' f'<em>{wyr["question"]}</em><br><br>'
f'🅱️ <strong>{wyr["option_b"]}</strong><br>' f'{result_html}'
f'<em>Check the reactions on the poll above to see which option won!</em>'
) )
await send_html(client, room_id, plain_r, html_r) await send_html(client, room_id, plain_r, html_r)