diff --git a/ROADMAP.md b/ROADMAP.md index 3c2e5c83..28229b58 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,7 +3,7 @@ * Matrix → Telegram * [x] Plaintext messages * [x] Formatted messages - * [ ] Bot commands (!command -> /command) + * [x] Bot commands (!command -> /command) * [x] Mentions * [x] Rich quotes * [ ] Locations (not implemented in Riot) diff --git a/mautrix_telegram/formatter/__init__.py b/mautrix_telegram/formatter/__init__.py index cf46d796..0428e723 100644 --- a/mautrix_telegram/formatter/__init__.py +++ b/mautrix_telegram/formatter/__init__.py @@ -1,2 +1,2 @@ -from .from_matrix import matrix_reply_to_telegram, matrix_to_telegram +from .from_matrix import matrix_reply_to_telegram, matrix_to_telegram, matrix_text_to_telegram from .from_telegram import telegram_reply_to_matrix, telegram_to_matrix diff --git a/mautrix_telegram/formatter/from_matrix.py b/mautrix_telegram/formatter/from_matrix.py index c1e78f6d..5999a68b 100644 --- a/mautrix_telegram/formatter/from_matrix.py +++ b/mautrix_telegram/formatter/from_matrix.py @@ -106,6 +106,8 @@ class MatrixParser(HTMLParser): elif tag == "pre": entity_type = MessageEntityPre args["language"] = "" + elif tag == "command": + entity_type = MessageEntityBotCommand elif tag == "li": self._list_entry_is_new = True elif tag == "a": @@ -157,6 +159,8 @@ class MatrixParser(HTMLParser): url = self._open_tags_meta[0] if url: text = url + elif previous_tag == "command": + text = f"/{text}" list_entry_handled_once = False # In order to maintain order of things like blockquotes in lists or lists in blockquotes, # we can't just have ifs/elses and we need to actually loop through the open tags in order. @@ -180,7 +184,7 @@ class MatrixParser(HTMLParser): prefix = "* " if self._list_entry_is_new else 3 * " " if not self._list_entry_is_new and not self._line_is_new: prefix = "" - extra_offset = len(indent) + len(prefix) + extra_offset += len(indent) + len(prefix) text = indent + prefix + text self._list_entry_is_new = False list_entry_handled_once = True @@ -206,10 +210,20 @@ class MatrixParser(HTMLParser): self.entities.append(entity) +command_regex = re.compile("(\s|^)!([A-Za-z0-9]+)") + + +def matrix_text_to_telegram(text): + text = command_regex.sub(r"\1/\2", text) + return text + + def matrix_to_telegram(html): try: parser = MatrixParser() - parser.feed(add_surrogates(html.replace("\n", ""))) + html = html.replace("\n", "") + html = command_regex.sub(r"\1\2", html) + parser.feed(add_surrogates(html)) return remove_surrogates(parser.text.strip()), parser.entities except Exception: log.exception("Failed to convert Matrix format:\nhtml=%s", html) diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index c702db99..472c0d23 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -573,11 +573,10 @@ class Portal: def _handle_matrix_text(self, client, message, reply_to): if "format" in message and message["format"] == "org.matrix.custom.html": message, entities = formatter.matrix_to_telegram(message["formatted_body"]) - return client.send_message(self.peer, message, entities=entities, - reply_to=reply_to) + return client.send_message(self.peer, message, entities=entities, reply_to=reply_to) else: - return client.send_message(self.peer, message["body"], - reply_to=reply_to) + message = formatter.matrix_text_to_telegram(message["body"]) + return client.send_message(self.peer, message, reply_to=reply_to) async def _handle_matrix_file(self, client, message, reply_to): file = await self.main_intent.download_file(message["url"])