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"(?span>)",
+ 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[^>]*>(.*?)?span>', r'\1', spoiler)
+ await self.send_fake_notice(room_id, unspoiler)
+
+
async def send_reaction(
self,
room_id: str,