mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
long message supported.
This commit is contained in:
parent
0ecd91e142
commit
98a77a6cf3
@ -17,7 +17,8 @@ qq-android协议的golang实现 移植于mirai
|
||||
- [x] 表情
|
||||
- [x] At
|
||||
- [x] 回复
|
||||
- [ ] 长消息
|
||||
- [x] 长消息
|
||||
- [ ] 富文本 ( xml/json/小程序 等 )
|
||||
- [x] 合并转发
|
||||
|
||||
#### 事件
|
||||
|
@ -383,18 +383,8 @@ func (c *QQClient) buildDeleteOnlinePushPacket(uin int64, seq uint16, delMsg []j
|
||||
}
|
||||
|
||||
// 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()
|
||||
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{
|
||||
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: groupCode}},
|
||||
ContentHead: &msg.ContentHead{PkgNum: 1},
|
||||
@ -834,7 +824,7 @@ func (c *QQClient) buildGroupMutePacket(groupCode, memberUin int64, time uint32)
|
||||
}
|
||||
|
||||
// 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()
|
||||
req := &multimsg.MultiReqBody{
|
||||
Subcmd: 1,
|
||||
@ -850,7 +840,7 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, groupUin int64) (u
|
||||
MsgType: 3,
|
||||
},
|
||||
},
|
||||
BuType: 2,
|
||||
BuType: buType,
|
||||
}
|
||||
payload, _ := proto.Marshal(req)
|
||||
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 {
|
||||
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)
|
||||
mr := int32(rand.Uint32())
|
||||
ch := make(chan int32)
|
||||
@ -214,7 +233,7 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage)
|
||||
}
|
||||
})
|
||||
defer c.onGroupMessageReceipt(eid)
|
||||
_, pkt := c.buildGroupSendingPacket(groupCode, mr, m)
|
||||
_, pkt := c.buildGroupSendingPacket(groupCode, mr, forward, m)
|
||||
_ = c.send(pkt)
|
||||
var mid int32
|
||||
ret := &message.GroupMessage{
|
||||
@ -281,14 +300,24 @@ func (c *QQClient) GetForwardMessage(resId string) *message.ForwardMessage {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
ts := time.Now().Unix()
|
||||
seq := c.nextGroupSeq()
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
@ -311,11 +340,24 @@ func (c *QQClient) SendForwardMessage(groupCode int64, m *message.ForwardMessage
|
||||
updServer := binary.UInt32ToIPV4Address(uint32(ip))
|
||||
err := c.highwayUploadImage(updServer+":"+strconv.FormatInt(int64(rsp.Uint32UpPort[i]), 10), rsp.MsgSig, body, 27)
|
||||
if err == nil {
|
||||
if !isLong {
|
||||
var pv string
|
||||
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, genForwardCard(rsp.MsgResid, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", len(m.Nodes)), ts))
|
||||
return c.sendGroupMessage(groupCode, true, genForwardTemplate(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
|
||||
|
@ -278,12 +278,36 @@ func packRequestDataV3(data []byte) (r []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
func genForwardCard(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)
|
||||
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,
|
||||
)
|
||||
return &message.SendingMessage{Elements: []message.IMessageElement{
|
||||
&message.ServiceElement{
|
||||
Id: 35,
|
||||
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;
|
||||
//PatsElem? patElem = 49;
|
||||
//GroupPostElem? groupPostElem = 50;
|
||||
//LightAppElem? lightApp = 51;
|
||||
LightAppElem lightApp = 51;
|
||||
//EIMInfo? eimInfo = 52;
|
||||
//CommonElem? commonElem = 53;
|
||||
}
|
||||
@ -430,6 +430,11 @@ message Face {
|
||||
bytes buf = 11;
|
||||
}
|
||||
|
||||
message LightAppElem {
|
||||
bytes data = 1;
|
||||
bytes msgResid = 2;
|
||||
}
|
||||
|
||||
message CustomFace {
|
||||
bytes guid = 1;
|
||||
string filePath = 2;
|
||||
|
@ -51,6 +51,8 @@ type ReplyElement struct {
|
||||
type ServiceElement struct {
|
||||
Id int32
|
||||
Content string
|
||||
ResId string
|
||||
SubType string
|
||||
}
|
||||
|
||||
type ForwardElement struct {
|
||||
|
@ -5,7 +5,9 @@ import (
|
||||
"encoding/hex"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -148,6 +150,47 @@ func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
|
||||
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 {
|
||||
if s.CardName == "" {
|
||||
return s.Nickname
|
||||
@ -260,15 +303,27 @@ func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) {
|
||||
}
|
||||
}
|
||||
if generalFlags {
|
||||
L:
|
||||
for _, elem := range elems {
|
||||
switch elem.(type) {
|
||||
switch e := elem.(type) {
|
||||
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")
|
||||
r = append(r, &msg.Elem{
|
||||
GeneralFlags: &msg.GeneralFlags{
|
||||
PbReserve: d,
|
||||
},
|
||||
})
|
||||
break L
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -290,6 +345,19 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement {
|
||||
}
|
||||
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 len(elem.Text.Attr6Buf) == 0 {
|
||||
res = append(res, NewText(elem.Text.Str))
|
||||
|
@ -21,3 +21,23 @@ func RandomStringRange(len int, str string) string {
|
||||
}
|
||||
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