feat(matrix): send markdown as formatted html messages
This commit is contained in:
@@ -3,6 +3,7 @@ import logging
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from mistune import create_markdown
|
||||||
from nio import (
|
from nio import (
|
||||||
AsyncClient,
|
AsyncClient,
|
||||||
AsyncClientConfig,
|
AsyncClientConfig,
|
||||||
@@ -21,6 +22,47 @@ from nanobot.config.loader import get_data_dir
|
|||||||
|
|
||||||
LOGGING_STACK_BASE_DEPTH = 2
|
LOGGING_STACK_BASE_DEPTH = 2
|
||||||
TYPING_NOTICE_TIMEOUT_MS = 30_000
|
TYPING_NOTICE_TIMEOUT_MS = 30_000
|
||||||
|
MATRIX_HTML_FORMAT = "org.matrix.custom.html"
|
||||||
|
|
||||||
|
MATRIX_MARKDOWN = create_markdown(
|
||||||
|
escape=True,
|
||||||
|
plugins=["table", "strikethrough", "task_lists"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _render_markdown_html(text: str) -> str | None:
|
||||||
|
"""Render markdown to HTML for Matrix formatted messages."""
|
||||||
|
try:
|
||||||
|
formatted = MATRIX_MARKDOWN(text).strip()
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(
|
||||||
|
"Matrix markdown rendering failed ({}): {}",
|
||||||
|
type(e).__name__,
|
||||||
|
str(e),
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not formatted:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Skip formatted_body for plain output (<p>...</p>) to keep payload minimal.
|
||||||
|
stripped = formatted.strip()
|
||||||
|
if stripped.startswith("<p>") and stripped.endswith("</p>") and "<p>" not in stripped[3:-4]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
|
||||||
|
def _build_matrix_text_content(text: str) -> dict[str, str]:
|
||||||
|
"""Build Matrix m.text payload with plaintext fallback and optional HTML."""
|
||||||
|
content: dict[str, str] = {"msgtype": "m.text", "body": text}
|
||||||
|
formatted_html = _render_markdown_html(text)
|
||||||
|
if not formatted_html:
|
||||||
|
return content
|
||||||
|
|
||||||
|
content["format"] = MATRIX_HTML_FORMAT
|
||||||
|
content["formatted_body"] = formatted_html
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
class _NioLoguruHandler(logging.Handler):
|
class _NioLoguruHandler(logging.Handler):
|
||||||
@@ -141,7 +183,7 @@ class MatrixChannel(BaseChannel):
|
|||||||
await self.client.room_send(
|
await self.client.room_send(
|
||||||
room_id=msg.chat_id,
|
room_id=msg.chat_id,
|
||||||
message_type="m.room.message",
|
message_type="m.room.message",
|
||||||
content={"msgtype": "m.text", "body": msg.content},
|
content=_build_matrix_text_content(msg.content),
|
||||||
ignore_unverified_devices=True,
|
ignore_unverified_devices=True,
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ dependencies = [
|
|||||||
"mcp>=1.26.0,<2.0.0",
|
"mcp>=1.26.0,<2.0.0",
|
||||||
"json-repair>=0.57.0,<1.0.0",
|
"json-repair>=0.57.0,<1.0.0",
|
||||||
"matrix-nio[e2e]>=0.25.2",
|
"matrix-nio[e2e]>=0.25.2",
|
||||||
|
"mistune>=3.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
Reference in New Issue
Block a user