From 80d77a8a0f48992b1f1f80f8aca55f10820f3ef1 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 22 Apr 2026 14:00:03 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20wyr=20votes=20never=20counted=20?= =?UTF-8?q?=E2=80=94=20reactions=20arrive=20as=20ReactionEvent=20not=20Unk?= =?UTF-8?q?nownEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nio has a dedicated ReactionEvent type with .reacts_to and .key attributes. The callback was registered for UnknownEvent so reaction events were silently dropped. Register for ReactionEvent and use its native attributes; keep the UnknownEvent fallback for edge cases. Co-Authored-By: Claude Sonnet 4.6 --- matrixbot/bot.py | 4 +++- matrixbot/callbacks.py | 42 ++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/matrixbot/bot.py b/matrixbot/bot.py index ae10f59..577ee2f 100644 --- a/matrixbot/bot.py +++ b/matrixbot/bot.py @@ -9,6 +9,7 @@ from nio import ( AsyncClientConfig, InviteMemberEvent, LoginResponse, + ReactionEvent, RoomMemberEvent, RoomMessageText, UnknownEvent, @@ -143,7 +144,8 @@ async def main(): callbacks = Callbacks(client) client.add_event_callback(callbacks.message, RoomMessageText) - client.add_event_callback(callbacks.reaction, UnknownEvent) + client.add_event_callback(callbacks.reaction, ReactionEvent) + client.add_event_callback(callbacks.unknown_event, UnknownEvent) client.add_event_callback(callbacks.member, RoomMemberEvent) # Auto-accept room invites diff --git a/matrixbot/callbacks.py b/matrixbot/callbacks.py index 737743e..3a0619e 100644 --- a/matrixbot/callbacks.py +++ b/matrixbot/callbacks.py @@ -70,40 +70,38 @@ class Callbacks: await wrapped(self.client, room.room_id, event.sender, args) async def reaction(self, room, event): - """Handle m.reaction events (sent as UnknownEvent by matrix-nio).""" - # Ignore events from before startup + """Handle ReactionEvent (nio's native reaction type).""" if self.startup_sync_token is None: return - - # Ignore our own reactions if event.sender == MATRIX_USER_ID: return - # m.reaction events come as UnknownEvent with type "m.reaction" + reacted_event_id = event.reacts_to + key = event.key + logger.info("reaction: key=%r target=%s sender=%s", key, reacted_event_id[:16], event.sender) + + 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 unknown_event(self, room, event): + """Fallback handler for UnknownEvent — catches any m.reaction not parsed by nio.""" + if self.startup_sync_token is None: + return + if event.sender == MATRIX_USER_ID: + return if not hasattr(event, "source"): - logger.info("reaction: event has no source attr, type=%s sender=%s", type(event).__name__, event.sender) return - event_type = event.source.get("type", "") content = event.source.get("content", {}) relates_to = content.get("m.relates_to", {}) - rel_type = relates_to.get("rel_type", "") - reacted_event_id = relates_to.get("event_id", "") - key = relates_to.get("key", "") - - logger.info( - "reaction: type=%s rel_type=%s key=%r target=%s sender=%s", - event_type, rel_type, key, reacted_event_id[:16] if reacted_event_id else "", event.sender, - ) - - if rel_type != "m.annotation": + if relates_to.get("rel_type") != "m.annotation": return - await handle_welcome_reaction( - self.client, room.room_id, event.sender, reacted_event_id, key - ) - from commands import _WYR_POLLS - logger.info("reaction: wyr polls active=%s matched=%s", list(_WYR_POLLS.keys()), reacted_event_id in _WYR_POLLS) + reacted_event_id = relates_to.get("event_id", "") + key = relates_to.get("key", "") + logger.info("unknown_event reaction: key=%r target=%s sender=%s", key, reacted_event_id[:16], event.sender) + + 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):