message_utils

Message utility functions for Discord bot.

Includes markdown-aware message splitting that preserves formatting across chunk boundaries, plus helper functions for mention filtering and XML escaping.

message_utils.escape_xml(text)[source]

Escape the five XML special characters so text is safe inside markup.

Replaces &, <, >, double-quote, and apostrophe with their XML entity forms (& first so already-escaped entities are not doubled) so arbitrary text can be embedded as element content or an attribute value without breaking the surrounding XML. Pure string transformation with no side effects.

No callers of this module-level function were found in the repo (other modules define their own _escape_xml methods); it is a public utility available for callers that need standalone XML escaping.

Parameters:

text (str) – The raw text to escape.

Returns:

The XML-escaped text.

Return type:

str

message_utils.repair_whitespace_split_discord_mentions(text)[source]

Remove whitespace inside numeric Discord mentions so they render.

Collapses spaces, tabs, newlines (including \r\n and runs of blank lines) after the opening <, within the snowflake, or both (e.g. <\n@123\n456>). Does not match <@everyone> / similar (non-digit bodies). Skips fenced code blocks like filter_backticks_from_mentions.

Return type:

str

Parameters:

text (str)

message_utils.filter_backticks_from_mentions(text)[source]

Strip backticks wrapping a Discord user mention so it renders as a ping.

The LLM sometimes emits a mention inside inline code (e.g. backtick-wrapped <@123>), which Discord shows literally instead of as a real mention. This walks the text fence by fence and, only in the non-fenced segments, removes one-or-more backticks immediately surrounding a <@123> or <@!123> token; genuine fenced code blocks are passed through untouched so deliberate mention examples in code samples stay intact. Pure string transformation with no side effects.

Called by response_postprocessor.py while cleaning up an outgoing model response (alongside repair_whitespace_split_discord_mentions()), and exercised by the message-utils tests.

Parameters:

text (str) – The candidate message text to clean.

Returns:

The text with backticks stripped from real mentions; returned unchanged if text is not a str.

Return type:

str

message_utils.split_message(text, max_length=1950, overflow_allowed=45)[source]

Split a long message into Discord-sendable chunks without breaking markdown.

The public entry point for chunking an outgoing response so each piece fits under Discord’s per-message length limit. It first honours manual split markers ({{ SPLIT_HERE }} and --- lines), converts backtick newline joiners outside fences via _replace_backtick_newline_joiners_outside_fences(), then hands each segment to _split_with_markdown_awareness(), which closes and re-opens active formats (bold, italic, code blocks, etc.) across chunk boundaries so Discord still renders them correctly. Includes fallbacks so a message that is all markers or otherwise yields no chunks still produces non-empty output rather than silently dropping the send. Pure string computation.

Called by the Discord platforms (platforms/discord.py, platforms/discord_self.py), task_manager.py, and tools/_egregore_discord.py before sending, and covered by tests/test_message_utils_split.py.

Parameters:
  • text (str) – The full message text to chunk.

  • max_length (int) – Target maximum length per chunk (default 1950).

  • overflow_allowed (int) – Extra characters a chunk may exceed max_length by before a hard cut, to avoid corrupting markdown (default 45).

Returns:

The ordered list of message chunks; a single error/placeholder element for non-string or empty input.

Return type:

List[str]