mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
94a1b8664b
- Move FindLast, findLast, and SplitMessage from discord.go to pkg/utils/message.go - Update discord.go to use utils.SplitMessage() - Makes splitting logic reusable across other channels
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
package utils
|
|
|
|
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 {
|
|
var messages []string
|
|
|
|
for len(content) > 0 {
|
|
if len(content) <= limit {
|
|
messages = append(messages, content)
|
|
break
|
|
}
|
|
|
|
msgEnd := limit
|
|
|
|
// Find natural split point within the limit
|
|
msgEnd = FindLastNewline(content[:limit], 200)
|
|
if msgEnd <= 0 {
|
|
msgEnd = FindLastSpace(content[:limit], 100)
|
|
}
|
|
if msgEnd <= 0 {
|
|
msgEnd = limit
|
|
}
|
|
|
|
// Check if this would end with an incomplete code block
|
|
candidate := content[:msgEnd]
|
|
unclosedIdx := FindLastUnclosedCodeBlock(candidate)
|
|
|
|
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 {
|
|
closingIdx := FindNextClosingCodeBlock(content, msgEnd)
|
|
if closingIdx > 0 && closingIdx <= extendedLimit {
|
|
// Extend to include the closing ```
|
|
msgEnd = closingIdx
|
|
} else {
|
|
// Can't find closing, split before the code block
|
|
msgEnd = FindLastNewline(content[:unclosedIdx], 200)
|
|
if msgEnd <= 0 {
|
|
msgEnd = FindLastSpace(content[:unclosedIdx], 100)
|
|
}
|
|
if msgEnd <= 0 {
|
|
msgEnd = unclosedIdx
|
|
}
|
|
}
|
|
} else {
|
|
// Remaining content fits within extended limit
|
|
msgEnd = len(content)
|
|
}
|
|
}
|
|
|
|
if msgEnd <= 0 {
|
|
msgEnd = limit
|
|
}
|
|
|
|
messages = append(messages, content[:msgEnd])
|
|
content = strings.TrimSpace(content[msgEnd:])
|
|
}
|
|
|
|
return messages
|
|
}
|
|
|
|
// FindLastUnclosedCodeBlock finds the last opening ``` that doesn't have a closing ```
|
|
// Returns the position of the opening ``` or -1 if all code blocks are complete
|
|
func FindLastUnclosedCodeBlock(text string) int {
|
|
count := 0
|
|
lastOpenIdx := -1
|
|
|
|
for i := 0; i < len(text); i++ {
|
|
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
|
|
if count == 0 {
|
|
lastOpenIdx = i
|
|
}
|
|
count++
|
|
i += 2
|
|
}
|
|
}
|
|
|
|
// If odd number of ``` markers, last one is unclosed
|
|
if count%2 == 1 {
|
|
return lastOpenIdx
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// FindNextClosingCodeBlock finds the next closing ``` starting from a position
|
|
// Returns the position after the closing ``` or -1 if not found
|
|
func FindNextClosingCodeBlock(text string, startIdx int) int {
|
|
for i := startIdx; i < len(text); i++ {
|
|
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
|
|
return i + 3
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// FindLastNewline finds the last newline character within the last N characters
|
|
// Returns the position of the newline or -1 if not found
|
|
func FindLastNewline(s string, searchWindow int) int {
|
|
searchStart := len(s) - searchWindow
|
|
if searchStart < 0 {
|
|
searchStart = 0
|
|
}
|
|
for i := len(s) - 1; i >= searchStart; i-- {
|
|
if s[i] == '\n' {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// FindLastSpace finds the last space character within the last N characters
|
|
// Returns the position of the space or -1 if not found
|
|
func FindLastSpace(s string, searchWindow int) int {
|
|
searchStart := len(s) - searchWindow
|
|
if searchStart < 0 {
|
|
searchStart = 0
|
|
}
|
|
for i := len(s) - 1; i >= searchStart; i-- {
|
|
if s[i] == ' ' || s[i] == '\t' {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|