diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index d28003f0..3ea7d530 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -151,7 +151,6 @@ class MatrixClient(nio.AsyncClient): "m.call.*", "m.room.third_party_invite", "m.room.tombstone", - "m.reaction", ], }, }, @@ -183,6 +182,7 @@ class MatrixClient(nio.AsyncClient): # handlers in the user config self.cmd_handler_map = { "me ": MatrixClient.handle_cmd_emote, + "react ": MatrixClient.handle_cmd_react, } try: @@ -748,6 +748,55 @@ class MatrixClient(nio.AsyncClient): ) + async def handle_cmd_react( + self, + room_id: str, + text: str, + display_name_mentions: Optional[Dict[str, str]] = None, # {id: name} + reply_to_event_id: Optional[str] = None, + ) -> None: + if reply_to_event_id is None or reply_to_event_id == '': + await self.send_fake_notice(room_id, "please reply to a message to react to it 🙃") + else: + reaction = emoji.emojize(text, language="alias") + await self.send_reaction(room_id, reaction, reply_to_event_id) + + + async def send_reaction( + self, + room_id: str, + key: str, + reacts_to: str, + ) -> None: + + # local event id in model isn't necessarily the actual event id + reacts_to_event_id = self.models[self.user_id, room_id, "events"][reacts_to].event_id + + item_uuid = uuid4() + + content: Dict[str, Any] = { + "m.relates_to": { + "rel_type": "m.annotation", + "event_id": reacts_to_event_id, + "key": key, + }, + } + + tx_id = uuid4() + content[f"{__reverse_dns__}.transaction_id"] = str(tx_id) + + await self.pause_while_offline() + await self._send_message(room_id, content, item_uuid, message_type = "m.reaction") + + # only update the UI after the reaction is sent, to not be misleading + await self._register_reaction( + self.all_rooms[room_id], + reacts_to_event_id, + key, + self.user_id, + ) + + async def toggle_pause_transfer( self, room_id: str, uuid: Union[str, UUID], ) -> None: @@ -1130,7 +1179,7 @@ class MatrixClient(nio.AsyncClient): async def _send_message( - self, room_id: str, content: dict, transaction_id: UUID, + self, room_id: str, content: dict, transaction_id: UUID, message_type: str = "m.room.message", ) -> None: """Send a message event with `content` dict to a room.""" @@ -1140,7 +1189,7 @@ class MatrixClient(nio.AsyncClient): async with self.backend.send_locks[room_id]: await self.room_send( room_id = room_id, - message_type = "m.room.message", + message_type = message_type, content = content, ignore_unverified_devices = True, ) @@ -2493,13 +2542,23 @@ class MatrixClient(nio.AsyncClient): self, room: nio.MatrixRoom, ev: nio.ReactionEvent, - event_id: str = "", + event_id: str = "", **fields, - ) -> Event: + ) -> None: + await self._register_reaction(room, ev.reacts_to, ev.key, ev.sender) + await self.register_nio_event( + room, ev, event_id, type_specifier=TypeSpecifier.Reaction, + content=ev.key, hidden=True, **fields, + ) + + async def _register_reaction( + self, + room: nio.MatrixRoom, + reacts_to: str, + key: str, + sender: str, + ) -> None: """Register/update a reaction.""" - reacts_to = ev.reacts_to - key = ev.key - sender = ev.sender model = self.models[self.user_id, room.room_id, "events"] reacts_to_event = model.get(reacts_to) @@ -2512,7 +2571,7 @@ class MatrixClient(nio.AsyncClient): if reacts_to_event: reactions = reacts_to_event.reactions if key not in reactions: - reactions[key] = {"hint": emoji.demojize(key), "users": []} + reactions[key] = {"hint": emoji.demojize(key, language="alias"), "users": []} if sender not in reactions[key]["users"]: reactions[key]["users"].append(sender) reacts_to_event.set_fields(reactions=reactions) @@ -2528,12 +2587,6 @@ class MatrixClient(nio.AsyncClient): if sender not in registry[reacts_to][key]: registry[reacts_to][key].append(sender) - await self.register_nio_event( - room, ev, event_id, type_specifier=TypeSpecifier.Reaction, - content=key, hidden=True, **fields, - ) - - async def register_message_replacement( self, room: nio.MatrixRoom,