github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/message/forward.go (about) 1 package message 2 3 import ( 4 "crypto/md5" 5 "regexp" 6 "strings" 7 "sync" 8 9 "github.com/Mrs4s/MiraiGo/binary" 10 "github.com/Mrs4s/MiraiGo/client/pb/msg" 11 "github.com/Mrs4s/MiraiGo/internal/proto" 12 "github.com/Mrs4s/MiraiGo/utils" 13 ) 14 15 // *----- Definitions -----* // 16 17 // ForwardMessage 合并转发消息 18 type ForwardMessage struct { 19 Nodes []*ForwardNode 20 } 21 22 type ForwardNode struct { // todo 加一个group_id 23 GroupId int64 24 SenderId int64 25 SenderName string 26 Time int32 27 Message []IMessageElement 28 } 29 30 type ForwardElement struct { 31 FileName string 32 Content string 33 ResId string 34 Items []*msg.PbMultiMsgItem 35 } 36 37 // *----- Implementations -----* // 38 39 func (f *ForwardMessage) Type() ElementType { 40 return Forward 41 } 42 43 // Type impl IMessageElement 44 func (e *ForwardElement) Type() ElementType { 45 return Forward 46 } 47 48 func (e *ForwardElement) Pack() []*msg.Elem { 49 rich := &msg.Elem{ 50 RichMsg: &msg.RichMsg{ 51 Template1: append([]byte{1}, binary.ZlibCompress(utils.S2B(e.Content))...), 52 ServiceId: proto.Int32(35), 53 MsgResId: []byte{}, 54 }, 55 } 56 txt := &msg.Elem{ 57 Text: &msg.Text{ 58 Str: proto.String("你的QQ暂不支持查看[转发多条消息],请期待后续版本。"), 59 }, 60 } 61 return []*msg.Elem{rich, txt} 62 } 63 64 func NewForwardMessage() *ForwardMessage { 65 return &ForwardMessage{} 66 } 67 68 // AddNode adds a node to the forward message. return for method chaining. 69 func (f *ForwardMessage) AddNode(node *ForwardNode) *ForwardMessage { 70 f.Nodes = append(f.Nodes, node) 71 for _, item := range node.Message { 72 if item.Type() != Forward { // quick path 73 continue 74 } 75 } 76 return f 77 } 78 79 // Length return the length of Nodes. 80 func (f *ForwardMessage) Length() int { return len(f.Nodes) } 81 82 func (f *ForwardMessage) Brief() string { 83 var brief strings.Builder 84 for _, n := range f.Nodes { 85 brief.WriteString(ToReadableString(n.Message)) 86 if brief.Len() >= 27 { 87 break 88 } 89 } 90 return brief.String() 91 } 92 93 func (f *ForwardMessage) Preview() string { 94 var pv strings.Builder 95 for i, node := range f.Nodes { 96 if i >= 4 { 97 break 98 } 99 pv.WriteString(`<title size="26" color="#777777">`) 100 pv.WriteString(utils.XmlEscape(node.SenderName)) 101 pv.WriteString(": ") 102 pv.WriteString(utils.XmlEscape(ToReadableString(node.Message))) 103 pv.WriteString("</title>") 104 } 105 return pv.String() 106 } 107 108 func (f *ForwardMessage) CalculateValidationData(seq, random int32, groupCode int64) ([]byte, []byte) { 109 msgs := f.PackForwardMessage(seq, random, groupCode) 110 trans := &msg.PbMultiMsgTransmit{Msg: msgs, PbItemList: []*msg.PbMultiMsgItem{ 111 { 112 FileName: proto.String("MultiMsg"), 113 Buffer: &msg.PbMultiMsgNew{Msg: msgs}, 114 }, 115 }} 116 b, _ := proto.Marshal(trans) 117 data := binary.GZipCompress(b) 118 hash := md5.Sum(data) 119 return data, hash[:] 120 } 121 122 func (f *ForwardMessage) PackForwardMessage(seq int32, random int32, groupCode int64) []*msg.Message { 123 ml := make([]*msg.Message, 0, len(f.Nodes)) 124 for _, node := range f.Nodes { 125 ml = append(ml, &msg.Message{ 126 Head: &msg.MessageHead{ 127 FromUin: proto.Some(node.SenderId), 128 MsgSeq: proto.Some(seq), 129 MsgTime: proto.Some(node.Time), 130 MsgUid: proto.Int64(0x0100_0000_0000_0000 | (int64(random) & 0xFFFFFFFF)), 131 MutiltransHead: &msg.MutilTransHead{ 132 MsgId: proto.Int32(1), 133 }, 134 MsgType: proto.Int32(82), 135 GroupInfo: &msg.GroupInfo{ 136 GroupCode: proto.Some(groupCode), 137 GroupRank: []byte{}, 138 GroupName: []byte{}, 139 GroupCard: proto.Some(node.SenderName), 140 }, 141 }, 142 Body: &msg.MessageBody{ 143 RichText: &msg.RichText{ 144 Elems: ToProtoElems(node.Message, false), 145 }, 146 }, 147 }) 148 } 149 return ml 150 } 151 152 type lazyRegex struct { 153 once sync.Once 154 reg *regexp.Regexp 155 Pattern string 156 } 157 158 func (l *lazyRegex) init() { 159 l.reg = regexp.MustCompile(l.Pattern) 160 } 161 162 func (l *lazyRegex) findMatch1(content string) string { 163 l.once.Do(l.init) 164 matches := l.reg.FindStringSubmatch(content) 165 if matches == nil { 166 return "" 167 } 168 return matches[1] 169 } 170 171 var ( 172 mResID = lazyRegex{Pattern: `m_resid="(.*?)"`} 173 mFileName = lazyRegex{Pattern: `m_fileName="(.*?)"`} 174 ) 175 176 func forwardMsgFromXML(xml string) *ForwardElement { 177 resid := mResID.findMatch1(xml) 178 fileName := mFileName.findMatch1(xml) 179 if resid == "" && fileName == "" { 180 return nil 181 } 182 return &ForwardElement{FileName: fileName, ResId: resid} 183 }