fix(discord): restore splitMessage and typing indicator
These functions were mistakenly removed in the original PR. - Re-add splitMessage to handle messages >2000 chars (Discord limit) - Re-add ChannelTyping to show typing indicator - Add sendChunk helper for sending message pieces The splitMessage function properly handles: - Newline boundaries - Space boundaries - Preserves code block integrity
This commit is contained in:
parent
6803568153
commit
7026f23f65
1 changed files with 134 additions and 3 deletions
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
|
@ -102,15 +103,31 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro
|
||||||
return fmt.Errorf("channel ID is empty")
|
return fmt.Errorf("channel ID is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := msg.Content
|
content := msg.Content
|
||||||
|
|
||||||
// 使用传入的 ctx 进行超时控制
|
runes := []rune(content)
|
||||||
|
if len(runes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks := splitMessage(msg.Content, 1500)
|
||||||
|
|
||||||
|
for _, chunk := range chunks {
|
||||||
|
if err := c.sendChunk(ctx, channelID, chunk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DiscordChannel) sendChunk(ctx context.Context, channelID, content string) error {
|
||||||
sendCtx, cancel := context.WithTimeout(ctx, sendTimeout)
|
sendCtx, cancel := context.WithTimeout(ctx, sendTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := c.session.ChannelMessageSend(channelID, message)
|
_, err := c.session.ChannelMessageSend(channelID, content)
|
||||||
done <- err
|
done <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -125,6 +142,114 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
msgEnd = findLastNewline(content[:limit], 200)
|
||||||
|
if msgEnd <= 0 {
|
||||||
|
msgEnd = findLastSpace(content[:limit], 100)
|
||||||
|
}
|
||||||
|
if msgEnd <= 0 {
|
||||||
|
msgEnd = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate := content[:msgEnd]
|
||||||
|
unclosedIdx := findLastUnclosedCodeBlock(candidate)
|
||||||
|
|
||||||
|
if unclosedIdx >= 0 {
|
||||||
|
extendedLimit := limit + 500
|
||||||
|
if len(content) > extendedLimit {
|
||||||
|
closingIdx := findNextClosingCodeBlock(content, msgEnd)
|
||||||
|
if closingIdx > 0 && closingIdx <= extendedLimit {
|
||||||
|
msgEnd = closingIdx
|
||||||
|
} else {
|
||||||
|
msgEnd = findLastNewline(content[:unclosedIdx], 200)
|
||||||
|
if msgEnd <= 0 {
|
||||||
|
msgEnd = findLastSpace(content[:unclosedIdx], 100)
|
||||||
|
}
|
||||||
|
if msgEnd <= 0 {
|
||||||
|
msgEnd = unclosedIdx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msgEnd = len(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if msgEnd <= 0 {
|
||||||
|
msgEnd = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
messages = append(messages, content[:msgEnd])
|
||||||
|
content = strings.TrimSpace(content[msgEnd:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
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 count%2 == 1 {
|
||||||
|
return lastOpenIdx
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// appendContent 安全地追加内容到现有文本
|
// appendContent 安全地追加内容到现有文本
|
||||||
func appendContent(content, suffix string) string {
|
func appendContent(content, suffix string) string {
|
||||||
if content == "" {
|
if content == "" {
|
||||||
|
|
@ -172,6 +297,12 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.session.ChannelTyping(m.ChannelID); err != nil {
|
||||||
|
logger.ErrorCF("discord", "Failed to send typing indicator", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
senderID := m.Author.ID
|
senderID := m.Author.ID
|
||||||
senderName := m.Author.Username
|
senderName := m.Author.Username
|
||||||
if m.Author.Discriminator != "" && m.Author.Discriminator != "0" {
|
if m.Author.Discriminator != "" && m.Author.Discriminator != "0" {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue