1
0
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:
风之凌殇 2021-12-23 12:57:18 +08:00 committed by GitHub
parent ffc6cc1861
commit c51e1956e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 2 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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
View 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)
}
}