diff --git a/backend/app/channels/discord.py b/backend/app/channels/discord.py index 35cf228406..32699216fb 100644 --- a/backend/app/channels/discord.py +++ b/backend/app/channels/discord.py @@ -876,8 +876,12 @@ class DiscordChannel(Channel): split_at = remaining.rfind("\n", 0, _DISCORD_MAX_MESSAGE_LEN) if split_at <= 0: split_at = _DISCORD_MAX_MESSAGE_LEN + else: + # Keep the delimiter on this chunk's tail so consecutive + # Discord messages reconstruct the original text exactly. + split_at += 1 chunks.append(remaining[:split_at]) - remaining = remaining[split_at:].lstrip("\n") + remaining = remaining[split_at:] if remaining: chunks.append(remaining) diff --git a/backend/tests/test_channels.py b/backend/tests/test_channels.py index f045a54e3c..5ffdfe6606 100644 --- a/backend/tests/test_channels.py +++ b/backend/tests/test_channels.py @@ -6131,6 +6131,30 @@ class TestHandleChatWithArtifacts: class TestDiscordChannel: + @pytest.mark.parametrize( + "text", + [ + "x" * 1990 + "\n\n" + "y" * 30, + "x" * 2000 + "\n\n\n" + "y" * 30, + "x" * 2000 + "\n" + "y" * 2000, + ("第一段\n\n" + "句" * 1990 + "\n\n最后一段") * 3, + "x" * 4500, + ], + ) + def test_split_text_preserves_content_within_discord_limit(self, text: str): + from app.channels.discord import _DISCORD_MAX_MESSAGE_LEN, DiscordChannel + + chunks = DiscordChannel._split_text(text) + + assert all(0 < len(chunk) <= _DISCORD_MAX_MESSAGE_LEN for chunk in chunks) + assert "".join(chunks) == text + + def test_split_text_preserves_empty_and_short_messages(self): + from app.channels.discord import DiscordChannel + + assert DiscordChannel._split_text("") == [""] + assert DiscordChannel._split_text("hello\n\nworld") == ["hello\n\nworld"] + def test_stop_prevents_queued_typing_starter_from_creating_task(self): from app.channels.discord import DiscordChannel