mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-06-18 21:45:04 +08:00
feat: add util function to split long message (#184)
* feat: add util function to split long message * fix: move util to message/message.go to avoid import cycle * fix: review opinions and add test
This commit is contained in:
parent
ffc6cc1861
commit
c51e1956e8
@ -49,7 +49,7 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage,
|
||||
}
|
||||
}
|
||||
msgLen := message.EstimateLength(m.Elements)
|
||||
if msgLen > 5000 || imgCount > 50 {
|
||||
if msgLen > message.MaxMessageSize || imgCount > 50 {
|
||||
return nil
|
||||
}
|
||||
if !useFram && (msgLen > 100 || imgCount > 2) {
|
||||
|
@ -26,7 +26,7 @@ func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *
|
||||
}
|
||||
}
|
||||
msgLen := message.EstimateLength(m.Elements)
|
||||
if msgLen > 5000 || imgCount > 50 {
|
||||
if msgLen > message.MaxMessageSize || imgCount > 50 {
|
||||
return nil
|
||||
}
|
||||
if frag && (msgLen > 300 || imgCount > 2) {
|
||||
|
@ -203,6 +203,9 @@ func (msg *SendingMessage) ToFragmented() [][]IMessageElement {
|
||||
return fragmented
|
||||
}
|
||||
|
||||
// 单条消息发送的大小限制(预估)
|
||||
const MaxMessageSize = 5000
|
||||
|
||||
func EstimateLength(elems []IMessageElement) int {
|
||||
sum := 0
|
||||
for _, elem := range elems {
|
||||
@ -624,3 +627,161 @@ func FaceNameById(id int) string {
|
||||
}
|
||||
return "未知表情"
|
||||
}
|
||||
|
||||
// SplitLongMessage 将过长的消息分割为若干个适合发送的消息
|
||||
func SplitLongMessage(sendingMessage *SendingMessage) []*SendingMessage {
|
||||
// 合并连续文本消息
|
||||
sendingMessage = mergeContinuousTextMessages(sendingMessage)
|
||||
|
||||
// 分割过长元素
|
||||
sendingMessage = splitElements(sendingMessage)
|
||||
|
||||
// 将元素分为多组,确保各组不超过单条消息的上限
|
||||
splitMessages := splitMessages(sendingMessage)
|
||||
|
||||
return splitMessages
|
||||
}
|
||||
|
||||
// mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割
|
||||
func mergeContinuousTextMessages(sendingMessage *SendingMessage) *SendingMessage {
|
||||
// 检查下是否有连续的文本消息,若没有,则可以直接返回
|
||||
lastIsText := false
|
||||
hasContinuousText := false
|
||||
for _, message := range sendingMessage.Elements {
|
||||
if message.Type() == Text {
|
||||
if lastIsText {
|
||||
// 有连续的文本消息,需要进行处理
|
||||
hasContinuousText = true
|
||||
break
|
||||
}
|
||||
|
||||
// 遇到文本元素先存放起来,方便将连续的文本元素合并
|
||||
lastIsText = true
|
||||
continue
|
||||
} else {
|
||||
lastIsText = false
|
||||
}
|
||||
}
|
||||
if !hasContinuousText {
|
||||
return sendingMessage
|
||||
}
|
||||
|
||||
// 存在连续的文本消息,需要进行合并处理
|
||||
textBuffer := strings.Builder{}
|
||||
lastIsText = false
|
||||
totalMessageCount := 0
|
||||
for _, message := range sendingMessage.Elements {
|
||||
if msgVal, ok := message.(*TextElement); ok {
|
||||
// 遇到文本元素先存放起来,方便将连续的文本元素合并
|
||||
textBuffer.WriteString(msgVal.Content)
|
||||
lastIsText = true
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果之前的是文本元素(可能是多个合并起来的),则在这里将其实际放入消息中
|
||||
if lastIsText {
|
||||
sendingMessage.Elements[totalMessageCount] = NewText(textBuffer.String())
|
||||
totalMessageCount += 1
|
||||
textBuffer.Reset()
|
||||
}
|
||||
lastIsText = false
|
||||
|
||||
// 非文本元素则直接处理
|
||||
sendingMessage.Elements[totalMessageCount] = message
|
||||
totalMessageCount += 1
|
||||
}
|
||||
// 处理最后几个元素是文本的情况
|
||||
if textBuffer.Len() != 0 {
|
||||
sendingMessage.Elements[totalMessageCount] = NewText(textBuffer.String())
|
||||
totalMessageCount += 1
|
||||
textBuffer.Reset()
|
||||
}
|
||||
sendingMessage.Elements = sendingMessage.Elements[:totalMessageCount]
|
||||
|
||||
return sendingMessage
|
||||
}
|
||||
|
||||
// splitElements 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素
|
||||
func splitElements(sendingMessage *SendingMessage) *SendingMessage {
|
||||
// 检查下是否存在需要文本消息,若不存在,则直接返回
|
||||
needSplit := false
|
||||
for _, message := range sendingMessage.Elements {
|
||||
if msgVal, ok := message.(*TextElement); ok {
|
||||
if textNeedSplit(msgVal.Content) {
|
||||
needSplit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !needSplit {
|
||||
return sendingMessage
|
||||
}
|
||||
|
||||
// 开始尝试切割
|
||||
messageParts := NewSendingMessage()
|
||||
|
||||
for _, message := range sendingMessage.Elements {
|
||||
switch msgVal := message.(type) {
|
||||
case *TextElement:
|
||||
messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...)
|
||||
default:
|
||||
messageParts.Append(message)
|
||||
}
|
||||
}
|
||||
|
||||
return messageParts
|
||||
}
|
||||
|
||||
// splitMessages 根据大小分为多个消息进行发送
|
||||
func splitMessages(sendingMessage *SendingMessage) []*SendingMessage {
|
||||
var splitMessages []*SendingMessage
|
||||
|
||||
messagePart := NewSendingMessage()
|
||||
msgSize := 0
|
||||
for _, part := range sendingMessage.Elements {
|
||||
estimateSize := EstimateLength([]IMessageElement{part})
|
||||
// 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素
|
||||
if msgSize+estimateSize > MaxMessageSize && len(messagePart.Elements) > 0 {
|
||||
splitMessages = append(splitMessages, messagePart)
|
||||
|
||||
messagePart = NewSendingMessage()
|
||||
msgSize = 0
|
||||
}
|
||||
|
||||
// 加上新的元素
|
||||
messagePart.Append(part)
|
||||
msgSize += estimateSize
|
||||
}
|
||||
// 将最后一个分片加上
|
||||
if len(messagePart.Elements) != 0 {
|
||||
splitMessages = append(splitMessages, messagePart)
|
||||
}
|
||||
|
||||
return splitMessages
|
||||
}
|
||||
|
||||
func splitPlainMessage(content string) []IMessageElement {
|
||||
if !textNeedSplit(content) {
|
||||
return []IMessageElement{NewText(content)}
|
||||
}
|
||||
|
||||
splittedMessage := make([]IMessageElement, 0, (len(content)+MaxMessageSize-1)/MaxMessageSize)
|
||||
|
||||
last := 0
|
||||
for runeIndex, runeValue := range content {
|
||||
// 如果加上新的这个字符后,会超出大小,则从这个字符前分一次片
|
||||
if runeIndex+len(string(runeValue))-last > MaxMessageSize {
|
||||
splittedMessage = append(splittedMessage, NewText(content[last:runeIndex]))
|
||||
last = runeIndex
|
||||
}
|
||||
}
|
||||
if last != len(content) {
|
||||
splittedMessage = append(splittedMessage, NewText(content[last:len(content)]))
|
||||
}
|
||||
|
||||
return splittedMessage
|
||||
}
|
||||
|
||||
func textNeedSplit(content string) bool {
|
||||
return len(content) > MaxMessageSize
|
||||
}
|
||||
|
36
message/message_test.go
Normal file
36
message/message_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_mergeContinuousTextMessages(t *testing.T) {
|
||||
msg := NewSendingMessage()
|
||||
msg.Append(NewText("短片段一"))
|
||||
msg.Append(NewText(strings.Repeat("长一", 800))) // 6*800
|
||||
msg.Append(NewText("短片段二"))
|
||||
msg.Append(NewText(strings.Repeat("长二", 1200))) // 6*1200
|
||||
msg.Append(NewText("短片段三"))
|
||||
|
||||
// 总长度为 12036
|
||||
totalSize := EstimateLength(msg.Elements)
|
||||
expectedPart := (totalSize + MaxMessageSize - 1) / MaxMessageSize
|
||||
|
||||
messages := SplitLongMessage(msg)
|
||||
// 应分为 3段
|
||||
if len(messages) != expectedPart {
|
||||
t.Errorf("should split into %v part", expectedPart)
|
||||
}
|
||||
partsSize := 0
|
||||
for idx, message := range messages {
|
||||
partSize := EstimateLength(message.Elements)
|
||||
if partSize > MaxMessageSize {
|
||||
t.Errorf("part %v size=%v is more than %v", idx, partSize, MaxMessageSize)
|
||||
}
|
||||
partsSize += partSize
|
||||
}
|
||||
if partsSize != totalSize {
|
||||
t.Errorf("parts size sum=%v is not equal to total size=%v", partsSize, totalSize)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user