diff --git a/matrixbot/callbacks.py b/matrixbot/callbacks.py index 1ac0ab1..5f6f820 100644 --- a/matrixbot/callbacks.py +++ b/matrixbot/callbacks.py @@ -4,7 +4,7 @@ from functools import wraps from nio import AsyncClient 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 logger = logging.getLogger("matrixbot") @@ -94,6 +94,7 @@ class Callbacks: await handle_welcome_reaction( 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): """Handle m.room.member events — watch for Space joins.""" diff --git a/matrixbot/commands.py b/matrixbot/commands.py index a548e2a..40d3f8f 100644 --- a/matrixbot/commands.py +++ b/matrixbot/commands.py @@ -1558,6 +1558,22 @@ async def check_scramble_answer(client: AsyncClient, room_id: str, sender: str, # 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: system_msg = ( "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) if hasattr(resp, "event_id"): - await send_reaction(client, room_id, resp.event_id, "🅰️") - await send_reaction(client, room_id, resp.event_id, "🅱️") + poll_event_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(): await asyncio.sleep(30) - # Count reactions via room state — we just tally what we have - # Since we can't easily query reaction counts via nio without extra API calls, - # we announce the results and encourage re-voting awareness - plain_r = ( - f"⏰ WYR Results!\n" - f"🅰️ {wyr['option_a']}\n" - f"🅱️ {wyr['option_b']}\n" - f"Check the reactions above to see which won!" - ) + poll = _WYR_POLLS.pop(poll_event_id, None) + votes_a = len(poll["votes"]["🅰️"]) if poll else 0 + votes_b = len(poll["votes"]["🅱️"]) if poll else 0 + total = votes_a + votes_b + + opt_a = wyr["option_a"] + opt_b = wyr["option_b"] + + if total == 0: + result_line = "No votes — you're all cowards. 🐔" + result_html = "No votes — you're all cowards. 🐔" + 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'🅰️ {opt_a} wins! ({votes_a} vs {votes_b} — {pct}%)' + 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'🅱️ {opt_b} wins! ({votes_b} vs {votes_a} — {pct}%)' + else: + result_line = f"It's a tie! ({votes_a} each)" + result_html = f"It's a tie! ({votes_a} each)" + + plain_r = f"⏰ WYR Results!\n{wyr['question']}\n{result_line}" html_r = ( - f'⏰ WYR — Time\'s up!
' - f'🅰️ {wyr["option_a"]}
' - f'🅱️ {wyr["option_b"]}
' - f'Check the reactions on the poll above to see which option won!' + f'⏰ WYR — Results!
' + f'{wyr["question"]}

' + f'{result_html}' ) await send_html(client, room_id, plain_r, html_r)