diff --git a/client/global.go b/client/global.go index 10faba5c..493c8e6b 100644 --- a/client/global.go +++ b/client/global.go @@ -1,10 +1,8 @@ package client import ( - "bytes" "crypto/md5" "encoding/hex" - "encoding/xml" "fmt" "math/rand" "net" @@ -13,6 +11,9 @@ import ( "strings" "time" + "github.com/pkg/errors" + "google.golang.org/protobuf/proto" + "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary/jce" devinfo "github.com/Mrs4s/MiraiGo/client/pb" @@ -20,8 +21,6 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" - "github.com/pkg/errors" - "google.golang.org/protobuf/proto" ) type ( @@ -586,12 +585,6 @@ func packUniRequestData(data []byte) (r []byte) { return } -func XmlEscape(c string) string { - buf := new(bytes.Buffer) - _ = xml.EscapeText(buf, []byte(c)) - return buf.String() -} - func genForwardTemplate(resID, preview, title, brief, source, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement { template := fmt.Sprintf(`%s %s
%s
`, brief, resID, ts, title, preview, summary, source, @@ -617,7 +610,7 @@ func genLongTemplate(resID, brief string, ts int64) *message.ServiceElement { return brief }() template := fmt.Sprintf(` %s 点击查看完整消息 `, - XmlEscape(limited), resID, ts, XmlEscape(limited), + utils.XmlEscape(limited), resID, ts, utils.XmlEscape(limited), ) return &message.ServiceElement{ Id: 35, diff --git a/client/group_msg.go b/client/group_msg.go index 0003d036..d57a47c7 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -7,6 +7,7 @@ import ( "math" "math/rand" "strconv" + "strings" "time" "github.com/Mrs4s/MiraiGo/client/pb/longmsg" @@ -53,14 +54,12 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, ret := c.sendGroupMessage(groupCode, false, &message.SendingMessage{Elements: []message.IMessageElement{ c.uploadGroupLongMessage(groupCode, - &message.ForwardMessage{Nodes: []*message.ForwardNode{ - { - SenderId: c.Uin, - SenderName: c.Nickname, - Time: int32(time.Now().Unix()), - Message: m.Elements, - }, - }}, + message.NewForwardMessage().AddNode(&message.ForwardNode{ + SenderId: c.Uin, + SenderName: c.Nickname, + Time: int32(time.Now().Unix()), + Message: m.Elements, + }), ), }}, ) @@ -71,9 +70,9 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, } // SendGroupForwardMessage 发送群合并转发消息 -func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.GroupMessage { +func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardElement) *message.GroupMessage { return c.sendGroupMessage(groupCode, true, - &message.SendingMessage{Elements: []message.IMessageElement{c.UploadGroupForwardMessage(groupCode, m)}}, + &message.SendingMessage{Elements: []message.IMessageElement{m}}, ) } @@ -161,7 +160,7 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se } func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMessage) *message.ServiceElement { - if len(m.Nodes) >= 200 { + if m.Length() >= 200 { return nil } ts := time.Now().UnixNano() @@ -173,25 +172,16 @@ func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMes } for i, ip := range rsp.Uint32UpIp { err := c.highwayUpload(uint32(ip), int(rsp.Uint32UpPort[i]), rsp.MsgSig, body, 27) - if err == nil { - bri := func() string { - var r string - for _, n := range m.Nodes { - r += message.ToReadableString(n.Message) - if len(r) >= 27 { - break - } - } - return r - }() - return genLongTemplate(rsp.MsgResid, bri, ts) + if err != nil { + continue } + return genLongTemplate(rsp.MsgResid, m.Brief(), ts) } return nil } func (c *QQClient) UploadGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.ForwardElement { - if len(m.Nodes) > 200 { + if m.Length() > 200 { return nil } ts := time.Now().UnixNano() @@ -203,13 +193,10 @@ func (c *QQClient) UploadGroupForwardMessage(groupCode int64, m *message.Forward } for i, ip := range rsp.Uint32UpIp { err := c.highwayUpload(uint32(ip), int(rsp.Uint32UpPort[i]), rsp.MsgSig, body, 27) - if err == nil { - var pv string - for i := 0; i < int(math.Min(4, float64(len(m.Nodes)))); i++ { - pv += fmt.Sprintf(`%s: %s`, XmlEscape(m.Nodes[i].SenderName), XmlEscape(message.ToReadableString(m.Nodes[i].Message))) - } - return genForwardTemplate(rsp.MsgResid, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", len(m.Nodes)), ts, items) + if err != nil { + continue } + return genForwardTemplate(rsp.MsgResid, m.Preview(), "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", m.Length()), ts, items) } return nil } @@ -529,10 +516,11 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { if extInfo != nil && len(extInfo.GroupCard) > 0 && extInfo.GroupCard[0] == 0x0A { buf := oidb.D8FCCommCardNameBuf{} if err := proto.Unmarshal(extInfo.GroupCard, &buf); err == nil && len(buf.RichCardName) > 0 { - groupCard = "" + var gcard strings.Builder for _, e := range buf.RichCardName { - groupCard += string(e.Text) + gcard.Write(e.Text) } + groupCard = gcard.String() } } if m.Head.GroupInfo != nil && groupCard != "" && mem.CardName != groupCard { diff --git a/message/elements.go b/message/elements.go index 0910d47b..bf630c85 100644 --- a/message/elements.go +++ b/message/elements.go @@ -178,8 +178,7 @@ func NewPrivateReply(m *PrivateMessage) *ReplyElement { func NewUrlShare(url, title, content, image string) *ServiceElement { template := fmt.Sprintf(`%v%v`, - title, url, image, title, content, - ) + title, url, image, title, content) /* template := fmt.Sprintf(`%s%s`, url, url, image, title, content, diff --git a/message/forward.go b/message/forward.go index 21c923f0..1b144d6e 100644 --- a/message/forward.go +++ b/message/forward.go @@ -1,18 +1,22 @@ package message import ( + "bytes" "crypto/md5" "google.golang.org/protobuf/proto" "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client/pb/msg" + "github.com/Mrs4s/MiraiGo/utils" ) // *----- Definitions -----* // +// ForwardMessage 添加 Node 请用 AddNode 方法 type ForwardMessage struct { Nodes []*ForwardNode + items []*msg.PbMultiMsgItem } type ForwardNode struct { @@ -36,26 +40,67 @@ func (e *ForwardElement) Type() ElementType { return Forward } -func (e *ForwardElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} - r = append(r, &msg.Elem{ +func (e *ForwardElement) Pack() []*msg.Elem { + rich := &msg.Elem{ RichMsg: &msg.RichMsg{ - Template1: append([]byte{1}, binary.ZlibCompress([]byte(e.Content))...), + Template1: append([]byte{1}, binary.ZlibCompress(utils.S2B(e.Content))...), ServiceId: proto.Int32(35), MsgResId: []byte{}, }, - }) - r = append(r, &msg.Elem{ + } + txt := &msg.Elem{ Text: &msg.Text{ Str: proto.String("你的QQ暂不支持查看[转发多条消息],请期待后续版本。"), }, - }) - return + } + return []*msg.Elem{rich, txt} } -// Type impl IMessageElement -func (f *ForwardMessage) Type() ElementType { - return Forward +func NewForwardMessage() *ForwardMessage { + return &ForwardMessage{} +} + +// AddNode adds a node to the forward message. return for method chaining. +func (f *ForwardMessage) AddNode(node *ForwardNode) *ForwardMessage { + f.Nodes = append(f.Nodes, node) + for _, item := range node.Message { + if item.Type() != Forward { // quick path + continue + } + if forward, ok := item.(*ForwardElement); ok { + f.items = append(f.items, forward.Items...) + } + } + return f +} + +// Length return the length of Nodes. +func (f *ForwardMessage) Length() int { return len(f.Nodes) } + +func (f *ForwardMessage) Brief() string { + var brief bytes.Buffer + for _, n := range f.Nodes { + brief.WriteString(ToReadableString(n.Message)) + if brief.Len() >= 27 { + break + } + } + return brief.String() +} + +func (f *ForwardMessage) Preview() string { + var pv bytes.Buffer + for i, node := range f.Nodes { + if i >= 4 { + break + } + pv.WriteString(``) + pv.WriteString(utils.XmlEscape(node.SenderName)) + pv.WriteString(": ") + pv.WriteString(utils.XmlEscape(ToReadableString(node.Message))) + pv.WriteString("") + } + return pv.String() } func (f *ForwardMessage) CalculateValidationData(seq, random int32, groupCode int64) ([]byte, []byte) { @@ -81,13 +126,7 @@ func (f *ForwardMessage) CalculateValidationDataForward(seq, random int32, group Buffer: &msg.PbMultiMsgNew{Msg: msgs}, }, }} - for _, node := range f.Nodes { - for _, message := range node.Message { - if forwardElement, ok := message.(*ForwardElement); ok { - trans.PbItemList = append(trans.PbItemList, forwardElement.Items...) - } - } - } + trans.PbItemList = append(trans.PbItemList, f.items...) b, _ := proto.Marshal(trans) data := binary.GZipCompress(b) hash := md5.Sum(data) @@ -95,9 +134,9 @@ func (f *ForwardMessage) CalculateValidationDataForward(seq, random int32, group } func (f *ForwardMessage) packForwardMsg(seq int32, random int32, groupCode int64) []*msg.Message { - msgs := make([]*msg.Message, 0, len(f.Nodes)) + ml := make([]*msg.Message, 0, len(f.Nodes)) for _, node := range f.Nodes { - msgs = append(msgs, &msg.Message{ + ml = append(ml, &msg.Message{ Head: &msg.MessageHead{ FromUin: &node.SenderId, MsgSeq: &seq, @@ -121,5 +160,5 @@ func (f *ForwardMessage) packForwardMsg(seq int32, random int32, groupCode int64 }, }) } - return msgs + return ml } diff --git a/message/message.go b/message/message.go index 95f807c6..ccc38bb9 100644 --- a/message/message.go +++ b/message/message.go @@ -418,7 +418,7 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { } if content != "" { if elem.RichMsg.GetServiceId() == 35 { - reg := regexp.MustCompile(`m_resid="(.+)"`) + reg := regexp.MustCompile(`m_resid="(.*?)"`) sub := reg.FindAllStringSubmatch(content, -1) if len(sub) > 0 && len(sub[0]) > 1 { res = append(res, &ForwardElement{ResId: reg.FindAllStringSubmatch(content, -1)[0][1]}) diff --git a/utils/string.go b/utils/string.go index 0078e82b..41f69ab4 100644 --- a/utils/string.go +++ b/utils/string.go @@ -1,6 +1,7 @@ package utils import ( + "encoding/xml" "math/rand" "reflect" "strconv" @@ -66,3 +67,10 @@ func S2B(s string) (b []byte) { bh.Len = sh.Len return } + +// XmlEscape xml escape string +func XmlEscape(c string) string { + buf := new(strings.Builder) + _ = xml.EscapeText(buf, []byte(c)) + return buf.String() +}