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)
|
msgLen := message.EstimateLength(m.Elements)
|
||||||
if msgLen > 5000 || imgCount > 50 {
|
if msgLen > message.MaxMessageSize || imgCount > 50 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !useFram && (msgLen > 100 || imgCount > 2) {
|
if !useFram && (msgLen > 100 || imgCount > 2) {
|
||||||
|
@ -26,7 +26,7 @@ func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
msgLen := message.EstimateLength(m.Elements)
|
msgLen := message.EstimateLength(m.Elements)
|
||||||
if msgLen > 5000 || imgCount > 50 {
|
if msgLen > message.MaxMessageSize || imgCount > 50 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if frag && (msgLen > 300 || imgCount > 2) {
|
if frag && (msgLen > 300 || imgCount > 2) {
|
||||||
|
@ -203,6 +203,9 @@ func (msg *SendingMessage) ToFragmented() [][]IMessageElement {
|
|||||||
return fragmented
|
return fragmented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 单条消息发送的大小限制(预估)
|
||||||
|
const MaxMessageSize = 5000
|
||||||
|
|
||||||
func EstimateLength(elems []IMessageElement) int {
|
func EstimateLength(elems []IMessageElement) int {
|
||||||
sum := 0
|
sum := 0
|
||||||
for _, elem := range elems {
|
for _, elem := range elems {
|
||||||
@ -624,3 +627,161 @@ func FaceNameById(id int) string {
|
|||||||
}
|
}
|
||||||
return "未知表情"
|
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