From e03124dc8a695b36b28eb2798fc914efa4493906 Mon Sep 17 00:00:00 2001 From: Hua Date: Wed, 18 Feb 2026 20:21:51 +0000 Subject: [PATCH] refactor: improve SplitMessage API clarity - Accept hard upper limit (maxLen) instead of pre-subtracted value - Caller now passes actual platform limit (e.g., 2000 for Discord) - Internal buffer of 500 chars is handled within message.go - Preferred split at maxLen - 500, may extend to maxLen for code blocks - Never exceeds maxLen, no more mental math for callers --- pkg/channels/discord.go | 2 +- pkg/utils/message.go | 41 +++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/pkg/channels/discord.go b/pkg/channels/discord.go index 7dc3f3198..ba02f7598 100644 --- a/pkg/channels/discord.go +++ b/pkg/channels/discord.go @@ -105,7 +105,7 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro return nil } - chunks := utils.SplitMessage(msg.Content, 1500) // Discord has a limit of 2000 characters per message, leave 500 for natural split e.g. code blocks + chunks := utils.SplitMessage(msg.Content, 2000) // Discord hard limit: 2000 chars (prefers split at 1500 to leave room for code blocks) for _, chunk := range chunks { if err := c.sendChunk(ctx, channelID, chunk); err != nil { diff --git a/pkg/utils/message.go b/pkg/utils/message.go index 3a4cf2ad6..9ca49ba53 100644 --- a/pkg/utils/message.go +++ b/pkg/utils/message.go @@ -4,26 +4,35 @@ import ( "strings" ) -// SplitMessage splits long messages into chunks, preserving code block integrity -// Uses natural boundaries (newlines, spaces) and extends messages slightly to avoid breaking code blocks -func SplitMessage(content string, limit int) []string { +const defaultCodeBlockBuffer = 500 + +// SplitMessage splits long messages into chunks, preserving code block integrity. +// The maxLen parameter is the hard upper limit - no message will exceed this length. +// The function prefers to split at maxLen - defaultCodeBlockBuffer to leave room for code blocks, +// but may extend up to maxLen when needed to avoid breaking incomplete code blocks. +func SplitMessage(content string, maxLen int) []string { var messages []string + codeBlockBuffer := defaultCodeBlockBuffer for len(content) > 0 { - if len(content) <= limit { + if len(content) <= maxLen { messages = append(messages, content) break } - msgEnd := limit + // Effective split point: maxLen minus buffer, to leave room for code blocks + effectiveLimit := maxLen - codeBlockBuffer + if effectiveLimit < maxLen/2 { + effectiveLimit = maxLen / 2 + } - // Find natural split point within the limit - msgEnd = FindLastNewline(content[:limit], 200) + // Find natural split point within the effective limit + msgEnd := FindLastNewline(content[:effectiveLimit], 200) if msgEnd <= 0 { - msgEnd = FindLastSpace(content[:limit], 100) + msgEnd = FindLastSpace(content[:effectiveLimit], 100) } if msgEnd <= 0 { - msgEnd = limit + msgEnd = effectiveLimit } // Check if this would end with an incomplete code block @@ -32,15 +41,14 @@ func SplitMessage(content string, limit int) []string { if unclosedIdx >= 0 { // Message would end with incomplete code block - // Try to extend to include the closing ``` (with some buffer) - extendedLimit := limit + 500 // Allow 500 char buffer for code blocks - if len(content) > extendedLimit { + // Try to extend up to maxLen (hard limit, never exceed) to include the closing ``` + if len(content) > msgEnd { closingIdx := FindNextClosingCodeBlock(content, msgEnd) - if closingIdx > 0 && closingIdx <= extendedLimit { + if closingIdx > 0 && closingIdx <= maxLen { // Extend to include the closing ``` msgEnd = closingIdx } else { - // Can't find closing, split before the code block + // Can't find closing within maxLen, split before the code block msgEnd = FindLastNewline(content[:unclosedIdx], 200) if msgEnd <= 0 { msgEnd = FindLastSpace(content[:unclosedIdx], 100) @@ -49,14 +57,11 @@ func SplitMessage(content string, limit int) []string { msgEnd = unclosedIdx } } - } else { - // Remaining content fits within extended limit - msgEnd = len(content) } } if msgEnd <= 0 { - msgEnd = limit + msgEnd = effectiveLimit } messages = append(messages, content[:msgEnd])