mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 19:17:38 +08:00
long message supported.
This commit is contained in:
parent
0ecd91e142
commit
98a77a6cf3
@ -17,7 +17,8 @@ qq-android协议的golang实现 移植于mirai
|
|||||||
- [x] 表情
|
- [x] 表情
|
||||||
- [x] At
|
- [x] At
|
||||||
- [x] 回复
|
- [x] 回复
|
||||||
- [ ] 长消息
|
- [x] 长消息
|
||||||
|
- [ ] 富文本 ( xml/json/小程序 等 )
|
||||||
- [x] 合并转发
|
- [x] 合并转发
|
||||||
|
|
||||||
#### 事件
|
#### 事件
|
||||||
|
@ -383,18 +383,8 @@ func (c *QQClient) buildDeleteOnlinePushPacket(uin int64, seq uint16, delMsg []j
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MessageSvc.PbSendMsg
|
// MessageSvc.PbSendMsg
|
||||||
func (c *QQClient) buildGroupSendingPacket(groupCode int64, r int32, m *message.SendingMessage) (uint16, []byte) {
|
func (c *QQClient) buildGroupSendingPacket(groupCode int64, r int32, forward bool, m *message.SendingMessage) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
forward := func() bool {
|
|
||||||
for _, elem := range m.Elements {
|
|
||||||
if e, ok := elem.(*message.ServiceElement); ok {
|
|
||||||
if e.Id == 35 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}()
|
|
||||||
req := &msg.SendMessageRequest{
|
req := &msg.SendMessageRequest{
|
||||||
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: groupCode}},
|
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: groupCode}},
|
||||||
ContentHead: &msg.ContentHead{PkgNum: 1},
|
ContentHead: &msg.ContentHead{PkgNum: 1},
|
||||||
@ -834,7 +824,7 @@ func (c *QQClient) buildGroupMutePacket(groupCode, memberUin int64, time uint32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MultiMsg.ApplyUp
|
// MultiMsg.ApplyUp
|
||||||
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, groupUin int64) (uint16, []byte) {
|
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
req := &multimsg.MultiReqBody{
|
req := &multimsg.MultiReqBody{
|
||||||
Subcmd: 1,
|
Subcmd: 1,
|
||||||
@ -850,7 +840,7 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, groupUin int64) (u
|
|||||||
MsgType: 3,
|
MsgType: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BuType: 2,
|
BuType: buType,
|
||||||
}
|
}
|
||||||
payload, _ := proto.Marshal(req)
|
payload, _ := proto.Marshal(req)
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
@ -205,6 +205,25 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
|
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
|
||||||
|
imgCount := m.Count(func(e message.IMessageElement) bool { return e.Type() == message.Image })
|
||||||
|
msgLen := message.EstimateLength(m.Elements, 703)
|
||||||
|
if msgLen > 5000 || imgCount > 50 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if msgLen > 702 || imgCount > 2 {
|
||||||
|
return c.sendGroupLongOrForwardMessage(groupCode, true, &message.ForwardMessage{Nodes: []*message.ForwardNode{
|
||||||
|
{
|
||||||
|
SenderId: c.Uin,
|
||||||
|
SenderName: c.Nickname,
|
||||||
|
Time: int32(time.Now().Unix()),
|
||||||
|
Message: m.Elements,
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
return c.sendGroupMessage(groupCode, false, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.SendingMessage) *message.GroupMessage {
|
||||||
eid := utils.RandomString(6)
|
eid := utils.RandomString(6)
|
||||||
mr := int32(rand.Uint32())
|
mr := int32(rand.Uint32())
|
||||||
ch := make(chan int32)
|
ch := make(chan int32)
|
||||||
@ -214,7 +233,7 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
defer c.onGroupMessageReceipt(eid)
|
defer c.onGroupMessageReceipt(eid)
|
||||||
_, pkt := c.buildGroupSendingPacket(groupCode, mr, m)
|
_, pkt := c.buildGroupSendingPacket(groupCode, mr, forward, m)
|
||||||
_ = c.send(pkt)
|
_ = c.send(pkt)
|
||||||
var mid int32
|
var mid int32
|
||||||
ret := &message.GroupMessage{
|
ret := &message.GroupMessage{
|
||||||
@ -281,14 +300,24 @@ func (c *QQClient) GetForwardMessage(resId string) *message.ForwardMessage {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) SendForwardMessage(groupCode int64, m *message.ForwardMessage) *message.GroupMessage {
|
func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.GroupMessage {
|
||||||
|
return c.sendGroupLongOrForwardMessage(groupCode, false, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) sendGroupLongOrForwardMessage(groupCode int64, isLong bool, m *message.ForwardMessage) *message.GroupMessage {
|
||||||
if len(m.Nodes) >= 200 {
|
if len(m.Nodes) >= 200 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ts := time.Now().Unix()
|
ts := time.Now().Unix()
|
||||||
seq := c.nextGroupSeq()
|
seq := c.nextGroupSeq()
|
||||||
data, hash := m.CalculateValidationData(seq, rand.Int31(), groupCode)
|
data, hash := m.CalculateValidationData(seq, rand.Int31(), groupCode)
|
||||||
i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, utils.ToGroupUin(groupCode)))
|
i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, func() int32 {
|
||||||
|
if isLong {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}(), utils.ToGroupUin(groupCode)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -311,11 +340,24 @@ func (c *QQClient) SendForwardMessage(groupCode int64, m *message.ForwardMessage
|
|||||||
updServer := binary.UInt32ToIPV4Address(uint32(ip))
|
updServer := binary.UInt32ToIPV4Address(uint32(ip))
|
||||||
err := c.highwayUploadImage(updServer+":"+strconv.FormatInt(int64(rsp.Uint32UpPort[i]), 10), rsp.MsgSig, body, 27)
|
err := c.highwayUploadImage(updServer+":"+strconv.FormatInt(int64(rsp.Uint32UpPort[i]), 10), rsp.MsgSig, body, 27)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var pv string
|
if !isLong {
|
||||||
for i := 0; i < int(math.Min(4, float64(len(m.Nodes)))); i++ {
|
var pv string
|
||||||
pv += fmt.Sprintf(`<title size="26" color="#777777">%s: %s</title>`, m.Nodes[i].SenderName, message.ToReadableString(m.Nodes[i].Message))
|
for i := 0; i < int(math.Min(4, float64(len(m.Nodes)))); i++ {
|
||||||
|
pv += fmt.Sprintf(`<title size="26" color="#777777">%s: %s</title>`, m.Nodes[i].SenderName, message.ToReadableString(m.Nodes[i].Message))
|
||||||
|
}
|
||||||
|
return c.sendGroupMessage(groupCode, true, genForwardTemplate(rsp.MsgResid, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", len(m.Nodes)), ts))
|
||||||
}
|
}
|
||||||
return c.SendGroupMessage(groupCode, genForwardCard(rsp.MsgResid, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", len(m.Nodes)), ts))
|
bri := func() string {
|
||||||
|
var r string
|
||||||
|
for _, n := range m.Nodes {
|
||||||
|
r += message.ToReadableString(n.Message)
|
||||||
|
if len(r) >= 27 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}()
|
||||||
|
return c.sendGroupMessage(groupCode, false, genLongTemplate(rsp.MsgResid, bri, ts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -278,12 +278,36 @@ func packRequestDataV3(data []byte) (r []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func genForwardCard(resId, preview, title, brief, source, summary string, ts int64) *message.SendingMessage {
|
func genForwardTemplate(resId, preview, title, brief, source, summary string, ts int64) *message.SendingMessage {
|
||||||
template := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8'?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="%s" m_resid="%s" m_fileName="%d" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1"><title color="#000000" size="34">%s</title> %s<hr></hr><summary size="26" color="#808080">%s</summary></item><source name="%s"></source></msg>`, brief, resId, ts, title, preview, summary, source)
|
template := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8'?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="%s" m_resid="%s" m_fileName="%d" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1"><title color="#000000" size="34">%s</title> %s<hr></hr><summary size="26" color="#808080">%s</summary></item><source name="%s"></source></msg>`,
|
||||||
|
brief, resId, ts, title, preview, summary, source,
|
||||||
|
)
|
||||||
return &message.SendingMessage{Elements: []message.IMessageElement{
|
return &message.SendingMessage{Elements: []message.IMessageElement{
|
||||||
&message.ServiceElement{
|
&message.ServiceElement{
|
||||||
Id: 35,
|
Id: 35,
|
||||||
Content: template,
|
Content: template,
|
||||||
|
ResId: resId,
|
||||||
|
SubType: "Forward",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genLongTemplate(resId, brief string, ts int64) *message.SendingMessage {
|
||||||
|
limited := func() string {
|
||||||
|
if len(brief) > 30 {
|
||||||
|
return brief[:30] + "…"
|
||||||
|
}
|
||||||
|
return brief
|
||||||
|
}()
|
||||||
|
template := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="%s" m_resid="%s" m_fileName="%d" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="1"> <item layout="1"> <title>%s</title> <hr hidden="false" style="0"/> <summary>点击查看完整消息</summary> </item> <source name="聊天记录" icon="" action="" appid="-1"/> </msg>`,
|
||||||
|
limited, resId, ts, limited,
|
||||||
|
)
|
||||||
|
return &message.SendingMessage{Elements: []message.IMessageElement{
|
||||||
|
&message.ServiceElement{
|
||||||
|
Id: 35,
|
||||||
|
Content: template,
|
||||||
|
ResId: resId,
|
||||||
|
SubType: "Long",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -217,7 +217,7 @@ message Elem {
|
|||||||
//WorkflowNotifyMsg? msgWorkflowNotify = 48;
|
//WorkflowNotifyMsg? msgWorkflowNotify = 48;
|
||||||
//PatsElem? patElem = 49;
|
//PatsElem? patElem = 49;
|
||||||
//GroupPostElem? groupPostElem = 50;
|
//GroupPostElem? groupPostElem = 50;
|
||||||
//LightAppElem? lightApp = 51;
|
LightAppElem lightApp = 51;
|
||||||
//EIMInfo? eimInfo = 52;
|
//EIMInfo? eimInfo = 52;
|
||||||
//CommonElem? commonElem = 53;
|
//CommonElem? commonElem = 53;
|
||||||
}
|
}
|
||||||
@ -430,6 +430,11 @@ message Face {
|
|||||||
bytes buf = 11;
|
bytes buf = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LightAppElem {
|
||||||
|
bytes data = 1;
|
||||||
|
bytes msgResid = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message CustomFace {
|
message CustomFace {
|
||||||
bytes guid = 1;
|
bytes guid = 1;
|
||||||
string filePath = 2;
|
string filePath = 2;
|
||||||
|
@ -51,6 +51,8 @@ type ReplyElement struct {
|
|||||||
type ServiceElement struct {
|
type ServiceElement struct {
|
||||||
Id int32
|
Id int32
|
||||||
Content string
|
Content string
|
||||||
|
ResId string
|
||||||
|
SubType string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForwardElement struct {
|
type ForwardElement struct {
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -148,6 +150,47 @@ func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *SendingMessage) Any(filter func(e IMessageElement) bool) bool {
|
||||||
|
for _, e := range msg.Elements {
|
||||||
|
if filter(e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SendingMessage) Count(filter func(e IMessageElement) bool) (c int) {
|
||||||
|
for _, e := range msg.Elements {
|
||||||
|
if filter(e) {
|
||||||
|
c++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func EstimateLength(elems []IMessageElement, limit int) int {
|
||||||
|
sum := 0
|
||||||
|
for _, elem := range elems {
|
||||||
|
if sum >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
left := int(math.Max(float64(limit-sum), 0))
|
||||||
|
switch e := elem.(type) {
|
||||||
|
case *TextElement:
|
||||||
|
sum += utils.ChineseLength(e.Content, left)
|
||||||
|
case *AtElement:
|
||||||
|
sum += utils.ChineseLength(e.Display, left)
|
||||||
|
case *ReplyElement:
|
||||||
|
sum += 444 + EstimateLength(e.Elements, left)
|
||||||
|
case *ImageElement, *GroupImageElement, *FriendImageElement:
|
||||||
|
sum += 260
|
||||||
|
default:
|
||||||
|
sum += utils.ChineseLength(ToReadableString([]IMessageElement{elem}), left)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Sender) DisplayName() string {
|
func (s *Sender) DisplayName() string {
|
||||||
if s.CardName == "" {
|
if s.CardName == "" {
|
||||||
return s.Nickname
|
return s.Nickname
|
||||||
@ -260,15 +303,27 @@ func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if generalFlags {
|
if generalFlags {
|
||||||
|
L:
|
||||||
for _, elem := range elems {
|
for _, elem := range elems {
|
||||||
switch elem.(type) {
|
switch e := elem.(type) {
|
||||||
case *ServiceElement:
|
case *ServiceElement:
|
||||||
|
if e.SubType == "Long" {
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
GeneralFlags: &msg.GeneralFlags{
|
||||||
|
LongTextFlag: 1,
|
||||||
|
LongTextResid: e.ResId,
|
||||||
|
PbReserve: []byte{0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
break L
|
||||||
|
}
|
||||||
d, _ := hex.DecodeString("08097800C80100F00100F80100900200C80200980300A00320B00300C00300D00300E803008A04020803900480808010B80400C00400")
|
d, _ := hex.DecodeString("08097800C80100F00100F80100900200C80200980300A00320B00300C00300D00300E803008A04020803900480808010B80400C00400")
|
||||||
r = append(r, &msg.Elem{
|
r = append(r, &msg.Elem{
|
||||||
GeneralFlags: &msg.GeneralFlags{
|
GeneralFlags: &msg.GeneralFlags{
|
||||||
PbReserve: d,
|
PbReserve: d,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
break L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,6 +345,19 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if elem.LightApp != nil && len(elem.LightApp.Data) > 1 {
|
||||||
|
var content string
|
||||||
|
if elem.LightApp.Data[0] == 0 {
|
||||||
|
content = string(elem.LightApp.Data[1:])
|
||||||
|
}
|
||||||
|
if elem.LightApp.Data[0] == 1 {
|
||||||
|
content = string(binary.ZlibUncompress(elem.LightApp.Data[1:]))
|
||||||
|
}
|
||||||
|
if content != "" {
|
||||||
|
// TODO: 解析具体的APP
|
||||||
|
return append(res, NewText(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
if elem.Text != nil {
|
if elem.Text != nil {
|
||||||
if len(elem.Text.Attr6Buf) == 0 {
|
if len(elem.Text.Attr6Buf) == 0 {
|
||||||
res = append(res, NewText(elem.Text.Str))
|
res = append(res, NewText(elem.Text.Str))
|
||||||
|
@ -21,3 +21,23 @@ func RandomStringRange(len int, str string) string {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChineseLength(str string, limit int) int {
|
||||||
|
sum := 0
|
||||||
|
for _, r := range []rune(str) {
|
||||||
|
switch {
|
||||||
|
case r >= '\u0000' && r <= '\u007F':
|
||||||
|
sum += 1
|
||||||
|
case r >= '\u0080' && r <= '\u07FF':
|
||||||
|
sum += 2
|
||||||
|
case r >= '\u0800' && r <= '\uFFFF':
|
||||||
|
sum += 3
|
||||||
|
default:
|
||||||
|
sum += 4
|
||||||
|
}
|
||||||
|
if sum >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user