diff --git a/src/backend/html_markdown.py b/src/backend/html_markdown.py index 19baa469..d414dc9f 100644 --- a/src/backend/html_markdown.py +++ b/src/backend/html_markdown.py @@ -138,6 +138,13 @@ class HTMLProcessor: re.MULTILINE, ) + spoiler_regex = re.compile( + r"(]+data-mx-spoiler[^>]*>)" + r"(.*?)" + r"()", + re.MULTILINE, + ) + extra_newlines_regex = re.compile(r"\n(\n*)") @@ -224,6 +231,7 @@ class HTMLProcessor: # Client-side modifications html = self.quote_regex.sub(r'\1\2\3', html) + html = self.spoiler_regex.sub(r'\1\2\3', html) if not inline: return html @@ -250,11 +258,11 @@ class HTMLProcessor: "font": {"color"}, "a": {"href", "class", "data-mention"}, "code": {"class"}, + "span": {"data-mx-spoiler", "data-mx-color"}, } attributes = {**inlines_attributes, **{ "ol": {"start"}, "hr": {"width"}, - "span": {"data-mx-color"}, "img": { "data-mx-emote", "src", "alt", "title", "width", "height", }, diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 3ea7d530..91e79d87 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -183,6 +183,8 @@ class MatrixClient(nio.AsyncClient): self.cmd_handler_map = { "me ": MatrixClient.handle_cmd_emote, "react ": MatrixClient.handle_cmd_react, + "spoiler ": MatrixClient.handle_cmd_spoiler, + "unspoiler": MatrixClient.handle_cmd_unspoiler, } try: @@ -655,6 +657,8 @@ class MatrixClient(nio.AsyncClient): text: str, display_name_mentions: Optional[Dict[str, str]] = None, # {id: name} reply_to_event_id: Optional[str] = None, + override_to_html: Optional[str] = None, + override_echo_body: Optional[str] = None, emote: bool = False, ) -> None: """Send a markdown `m.text` or `m.emote` message .""" @@ -676,6 +680,12 @@ class MatrixClient(nio.AsyncClient): to_html = from_md(text, outgoing=True) echo_body = from_md(text) + # override_echo_body will not be effective if it is a reply. + # echo_body is only shown before the event is received back from the + # server, so this is fine if not ideal + to_html = override_to_html or to_html + echo_body = override_echo_body or echo_body + if to_html not in (html.escape(text), f"

{html.escape(text)}

"): content["format"] = "org.matrix.custom.html" content["formatted_body"] = to_html @@ -762,6 +772,56 @@ class MatrixClient(nio.AsyncClient): await self.send_reaction(room_id, reaction, reply_to_event_id) + async def handle_cmd_spoiler( + self, + room_id: str, + text: str, + display_name_mentions: Optional[Dict[str, str]] = None, # {id: name} + reply_to_event_id: Optional[str] = None, + ) -> None: + + from_md = partial( + HTML.from_markdown, display_name_mentions=display_name_mentions, + ) + + to_html = from_md(text, outgoing=True) + echo_body = from_md(text) + + to_html = f"{to_html}" + echo_body = f"{echo_body}" + + + await self._send_text( + room_id, + text, + display_name_mentions, + reply_to_event_id, + override_to_html = to_html, + override_echo_body = echo_body, + ) + + + async def handle_cmd_unspoiler( + 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 with /unspoiler to unspoiler it 🙃") + else: + spoiler_event: Event = \ + self.models[self.user_id, room_id, "events"][reply_to_event_id] + + # get formatted_body, fallback to body, + spoiler = getattr(spoiler_event.source, "formatted_body", None) or \ + getattr(spoiler_event.source, "body", "") + + unspoiler = re.sub(r']+data-mx-spoiler[^>]*>(.*?)', r'\1', spoiler) + await self.send_fake_notice(room_id, unspoiler) + + async def send_reaction( self, room_id: str,