mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-16 04:22:36 +00:00
fix(weixin): wrap long copy-unfriendly lines
This commit is contained in:
parent
a494a614d0
commit
7244a1f0d3
2 changed files with 66 additions and 1 deletions
|
|
@ -23,6 +23,7 @@ import re
|
||||||
import secrets
|
import secrets
|
||||||
import struct
|
import struct
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
@ -32,6 +33,8 @@ from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
WEIXIN_COPY_LINE_WIDTH = 120
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
|
@ -731,6 +734,46 @@ def _normalize_markdown_blocks(content: str) -> str:
|
||||||
return "\n".join(result).strip()
|
return "\n".join(result).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _wrap_copy_friendly_lines_for_weixin(content: str) -> str:
|
||||||
|
"""Wrap long display lines that are hard to copy in WeChat clients."""
|
||||||
|
if not content:
|
||||||
|
return content
|
||||||
|
|
||||||
|
wrapped: List[str] = []
|
||||||
|
in_code_block = False
|
||||||
|
|
||||||
|
for raw_line in content.splitlines():
|
||||||
|
line = raw_line.rstrip()
|
||||||
|
stripped = line.strip()
|
||||||
|
|
||||||
|
if _FENCE_RE.match(stripped):
|
||||||
|
in_code_block = not in_code_block
|
||||||
|
wrapped.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (
|
||||||
|
in_code_block
|
||||||
|
or len(line) <= WEIXIN_COPY_LINE_WIDTH
|
||||||
|
or not stripped
|
||||||
|
or stripped.startswith("|")
|
||||||
|
or _TABLE_RULE_RE.match(stripped)
|
||||||
|
):
|
||||||
|
wrapped.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
wrapped_lines = textwrap.wrap(
|
||||||
|
line,
|
||||||
|
width=WEIXIN_COPY_LINE_WIDTH,
|
||||||
|
break_long_words=False,
|
||||||
|
break_on_hyphens=False,
|
||||||
|
replace_whitespace=False,
|
||||||
|
drop_whitespace=True,
|
||||||
|
)
|
||||||
|
wrapped.extend(wrapped_lines or [line])
|
||||||
|
|
||||||
|
return "\n".join(wrapped).strip()
|
||||||
|
|
||||||
|
|
||||||
def _split_markdown_blocks(content: str) -> List[str]:
|
def _split_markdown_blocks(content: str) -> List[str]:
|
||||||
if not content:
|
if not content:
|
||||||
return []
|
return []
|
||||||
|
|
@ -2022,7 +2065,7 @@ class WeixinAdapter(BasePlatformAdapter):
|
||||||
def format_message(self, content: Optional[str]) -> str:
|
def format_message(self, content: Optional[str]) -> str:
|
||||||
if content is None:
|
if content is None:
|
||||||
return ""
|
return ""
|
||||||
return _normalize_markdown_blocks(content)
|
return _wrap_copy_friendly_lines_for_weixin(_normalize_markdown_blocks(content))
|
||||||
|
|
||||||
|
|
||||||
async def send_weixin_direct(
|
async def send_weixin_direct(
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,28 @@ class TestWeixinFormatting:
|
||||||
|
|
||||||
assert adapter.format_message(content) == content
|
assert adapter.format_message(content) == content
|
||||||
|
|
||||||
|
def test_format_message_wraps_long_plain_lines_for_copying(self):
|
||||||
|
adapter = _make_adapter()
|
||||||
|
|
||||||
|
content = (
|
||||||
|
"Here is a long issue template line with many copyable fields "
|
||||||
|
+ " ".join(f"field_{idx}=value_{idx}" for idx in range(24))
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted = adapter.format_message(content)
|
||||||
|
|
||||||
|
assert "\n" in formatted
|
||||||
|
assert all(len(line) <= weixin.WEIXIN_COPY_LINE_WIDTH for line in formatted.splitlines())
|
||||||
|
assert " ".join(formatted.split()) == " ".join(content.split())
|
||||||
|
|
||||||
|
def test_format_message_does_not_wrap_long_code_block_lines(self):
|
||||||
|
adapter = _make_adapter()
|
||||||
|
|
||||||
|
command = "hermes " + " ".join(f"--option-{idx}=value" for idx in range(30))
|
||||||
|
content = f"```bash\n{command}\n```"
|
||||||
|
|
||||||
|
assert adapter.format_message(content) == content
|
||||||
|
|
||||||
def test_format_message_returns_empty_string_for_none(self):
|
def test_format_message_returns_empty_string_for_none(self):
|
||||||
adapter = _make_adapter()
|
adapter = _make_adapter()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue