Add /spoiler and /unspoiler

This commit is contained in:
Tim Clifford 2023-11-04 23:32:33 +00:00 committed by Maze
parent 022df56c9e
commit 160670bea3
2 changed files with 69 additions and 1 deletions

View File

@ -138,6 +138,13 @@ class HTMLProcessor:
re.MULTILINE, re.MULTILINE,
) )
spoiler_regex = re.compile(
r"(<span[^>]+data-mx-spoiler[^>]*>)"
r"(.*?)"
r"(</?span>)",
re.MULTILINE,
)
extra_newlines_regex = re.compile(r"\n(\n*)") extra_newlines_regex = re.compile(r"\n(\n*)")
@ -224,6 +231,7 @@ class HTMLProcessor:
# Client-side modifications # Client-side modifications
html = self.quote_regex.sub(r'\1<span class="quote">\2</span>\3', html) html = self.quote_regex.sub(r'\1<span class="quote">\2</span>\3', html)
html = self.spoiler_regex.sub(r'\1<font color="#00000000">\2</font>\3', html)
if not inline: if not inline:
return html return html
@ -250,11 +258,11 @@ class HTMLProcessor:
"font": {"color"}, "font": {"color"},
"a": {"href", "class", "data-mention"}, "a": {"href", "class", "data-mention"},
"code": {"class"}, "code": {"class"},
"span": {"data-mx-spoiler", "data-mx-color"},
} }
attributes = {**inlines_attributes, **{ attributes = {**inlines_attributes, **{
"ol": {"start"}, "ol": {"start"},
"hr": {"width"}, "hr": {"width"},
"span": {"data-mx-color"},
"img": { "img": {
"data-mx-emote", "src", "alt", "title", "width", "height", "data-mx-emote", "src", "alt", "title", "width", "height",
}, },

View File

@ -183,6 +183,8 @@ class MatrixClient(nio.AsyncClient):
self.cmd_handler_map = { self.cmd_handler_map = {
"me ": MatrixClient.handle_cmd_emote, "me ": MatrixClient.handle_cmd_emote,
"react ": MatrixClient.handle_cmd_react, "react ": MatrixClient.handle_cmd_react,
"spoiler ": MatrixClient.handle_cmd_spoiler,
"unspoiler": MatrixClient.handle_cmd_unspoiler,
} }
try: try:
@ -655,6 +657,8 @@ class MatrixClient(nio.AsyncClient):
text: str, text: str,
display_name_mentions: Optional[Dict[str, str]] = None, # {id: name} display_name_mentions: Optional[Dict[str, str]] = None, # {id: name}
reply_to_event_id: Optional[str] = None, reply_to_event_id: Optional[str] = None,
override_to_html: Optional[str] = None,
override_echo_body: Optional[str] = None,
emote: bool = False, emote: bool = False,
) -> None: ) -> None:
"""Send a markdown `m.text` or `m.emote` message .""" """Send a markdown `m.text` or `m.emote` message ."""
@ -676,6 +680,12 @@ class MatrixClient(nio.AsyncClient):
to_html = from_md(text, outgoing=True) to_html = from_md(text, outgoing=True)
echo_body = from_md(text) 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"<p>{html.escape(text)}</p>"): if to_html not in (html.escape(text), f"<p>{html.escape(text)}</p>"):
content["format"] = "org.matrix.custom.html" content["format"] = "org.matrix.custom.html"
content["formatted_body"] = to_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) 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"<span data-mx-spoiler>{to_html}</span>"
echo_body = f"<span data-mx-spoiler>{echo_body}</span>"
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'<span[^>]+data-mx-spoiler[^>]*>(.*?)</?span>', r'\1', spoiler)
await self.send_fake_notice(room_id, unspoiler)
async def send_reaction( async def send_reaction(
self, self,
room_id: str, room_id: str,