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)