1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00
MiraiGo/client/group_msg.go
2020-12-20 12:59:33 +08:00

347 lines
11 KiB
Go

package client
import (
"fmt"
"github.com/Mrs4s/MiraiGo/client/pb/longmsg"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/multimsg"
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/protocol/packets"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"math"
"math/rand"
"time"
)
func init() {
decoders["OnlinePush.PbPushGroupMsg"] = decodeGroupMessagePacket
decoders["MessageSvc.PbSendMsg"] = decodeMsgSendResponse
decoders["MessageSvc.PbGetGroupMsg"] = decodeGetGroupMsgResponse
decoders["OidbSvc.0x8a7_0"] = decodeAtAllRemainResponse
}
// SendGroupMessage 发送群消息
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, f ...bool) *message.GroupMessage {
useFram := false
if len(f) > 0 {
useFram = f[0]
}
imgCount := m.Count(func(e message.IMessageElement) bool { return e.Type() == message.Image })
if useFram {
if m.Any(func(e message.IMessageElement) bool { return e.Type() == message.Reply }) {
useFram = false
}
}
msgLen := message.EstimateLength(m.Elements, 703)
if msgLen > 5000 || imgCount > 50 {
return nil
}
if (msgLen > 200 || imgCount > 1) && !useFram {
ret := c.sendGroupLongOrForwardMessage(groupCode, true, &message.ForwardMessage{Nodes: []*message.ForwardNode{
{
SenderId: c.Uin,
SenderName: c.Nickname,
Time: int32(time.Now().Unix()),
Message: m.Elements,
},
}})
return ret
}
return c.sendGroupMessage(groupCode, false, m)
}
// SendGroupForwardMessage 发送群合并转发消息
func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.GroupMessage {
return c.sendGroupLongOrForwardMessage(groupCode, false, m)
}
// GetGroupMessages 从服务器获取历史信息
func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*message.GroupMessage, error) {
i, err := c.sendAndWait(c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq))
if err != nil {
return nil, err
}
return i.([]*message.GroupMessage), nil
}
func (c *QQClient) GetAtAllRemain(groupCode int64) (*AtAllRemainInfo, error) {
i, err := c.sendAndWait(c.buildAtAllRemainRequestPacket(groupCode))
if err != nil {
return nil, err
}
return i.(*AtAllRemainInfo), nil
}
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)
c.onGroupMessageReceipt(eid, func(c *QQClient, e *groupMessageReceiptEvent) {
if e.Rand == mr && !utils.IsChanClosed(ch) {
ch <- e.Seq
}
})
defer c.onGroupMessageReceipt(eid)
imgCount := m.Count(func(e message.IMessageElement) bool { return e.Type() == message.Image })
msgLen := message.EstimateLength(m.Elements, 703)
if (msgLen > 100 || imgCount > 1) && !forward && !m.Any(func(e message.IMessageElement) bool {
_, ok := e.(*message.GroupVoiceElement)
_, ok2 := e.(*message.ServiceElement)
_, ok3 := e.(*message.ReplyElement)
return ok || ok2 || ok3
}) {
div := int32(rand.Uint32())
fragmented := m.ToFragmented()
for i, elems := range fragmented {
_, pkt := c.buildGroupSendingPacket(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems)
_ = c.send(pkt)
}
} else {
_, pkt := c.buildGroupSendingPacket(groupCode, mr, 1, 0, 0, forward, m.Elements)
_ = c.send(pkt)
}
var mid int32
ret := &message.GroupMessage{
Id: -1,
InternalId: mr,
GroupCode: groupCode,
Sender: &message.Sender{
Uin: c.Uin,
Nickname: c.Nickname,
IsFriend: true,
},
Time: int32(time.Now().Unix()),
Elements: m.Elements,
}
select {
case mid = <-ch:
ret.Id = mid
return ret
case <-time.After(time.Second * 5):
if g, err := c.GetGroupInfo(groupCode); err == nil {
if history, err := c.GetGroupMessages(groupCode, g.lastMsgSeq-10, g.lastMsgSeq+1); err == nil {
for _, m := range history {
if m.InternalId == mr {
return m
}
}
}
}
return ret
}
}
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, func() int32 {
if isLong {
return 1
} else {
return 2
}
}(), utils.ToGroupUin(groupCode)))
if err != nil {
return nil
}
rsp := i.(*multimsg.MultiMsgApplyUpRsp)
body, _ := proto.Marshal(&longmsg.LongReqBody{
Subcmd: 1,
TermType: 5,
PlatformType: 9,
MsgUpReq: []*longmsg.LongMsgUpReq{
{
MsgType: 3,
DstUin: utils.ToGroupUin(groupCode),
MsgContent: data,
StoreType: 2,
MsgUkey: rsp.MsgUkey,
},
},
})
for i, ip := range rsp.Uint32UpIp {
err := c.highwayUpload(uint32(ip), int(rsp.Uint32UpPort[i]), 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, 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
}
// MessageSvc.PbSendMsg
func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) {
seq := c.nextSeq()
var ptt *message.GroupVoiceElement
if len(m) > 0 {
if p, ok := m[0].(*message.GroupVoiceElement); ok {
ptt = p
m = []message.IMessageElement{}
}
}
req := &msg.SendMessageRequest{
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: &groupCode}},
ContentHead: &msg.ContentHead{PkgNum: &pkgNum, PkgIndex: &pkgIndex, DivSeq: &pkgDiv},
MsgBody: &msg.MessageBody{
RichText: &msg.RichText{
Elems: message.ToProtoElems(m, true),
Ptt: func() *msg.Ptt {
if ptt != nil {
return ptt.Ptt
}
return nil
}(),
},
},
MsgSeq: proto.Int32(c.nextGroupSeq()),
MsgRand: &r,
SyncCookie: EmptyBytes,
MsgVia: proto.Int32(1),
MsgCtrl: func() *msg.MsgCtrl {
if forward {
return &msg.MsgCtrl{MsgFlag: proto.Int32(4)}
}
return nil
}(),
}
payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbSendMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
}
func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) {
seq := c.nextSeq()
req := &msg.GetGroupMsgReq{
GroupCode: proto.Uint64(uint64(groupCode)),
BeginSeq: proto.Uint64(uint64(beginSeq)),
EndSeq: proto.Uint64(uint64(endSeq)),
PublicGroup: proto.Bool(false),
}
payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetGroupMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
}
func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) {
seq := c.nextSeq()
body := &oidb.D8A7ReqBody{
SubCmd: proto.Uint32(1),
LimitIntervalTypeForUin: proto.Uint32(2),
LimitIntervalTypeForGroup: proto.Uint32(1),
Uin: proto.Uint64(uint64(c.Uin)),
GroupCode: proto.Uint64(uint64(groupCode)),
}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 2215,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x8a7_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
}
// OnlinePush.PbPushGroupMsg
func decodeGroupMessagePacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(payload, &pkt)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if pkt.Message.Head.GetFromUin() == c.Uin {
c.dispatchGroupMessageReceiptEvent(&groupMessageReceiptEvent{
Rand: pkt.Message.Body.RichText.Attr.GetRandom(),
Seq: pkt.Message.Head.GetMsgSeq(),
Msg: c.parseGroupMessage(pkt.Message),
})
return nil, nil
}
if pkt.Message.Content != nil && pkt.Message.Content.GetPkgNum() > 1 {
var builder *groupMessageBuilder // TODO: 支持多SEQ
i, ok := c.groupMsgBuilders.Load(pkt.Message.Content.DivSeq)
if !ok {
builder = &groupMessageBuilder{}
c.groupMsgBuilders.Store(pkt.Message.Content.DivSeq, builder)
} else {
builder = i.(*groupMessageBuilder)
}
builder.MessageSlices = append(builder.MessageSlices, pkt.Message)
if int32(len(builder.MessageSlices)) >= pkt.Message.Content.GetPkgNum() {
c.groupMsgBuilders.Delete(pkt.Message.Content.DivSeq)
c.dispatchGroupMessage(c.parseGroupMessage(builder.build()))
}
return nil, nil
}
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message))
return nil, nil
}
func decodeMsgSendResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
rsp := msg.SendMessageResponse{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.GetResult() != 0 {
c.Error("send msg error: %v %v", rsp.GetResult(), rsp.GetErrMsg())
}
return nil, nil
}
func decodeGetGroupMsgResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
rsp := msg.GetGroupMsgResp{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.GetResult() != 0 {
c.Error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.GetResult(), rsp.GetErrmsg())
}
var ret []*message.GroupMessage
for _, m := range rsp.Msg {
if m.Head.FromUin == nil {
continue
}
ret = append(ret, c.parseGroupMessage(m))
}
return ret, nil
}
func decodeAtAllRemainResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D8A7RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return &AtAllRemainInfo{
CanAtAll: rsp.GetCanAtAll(),
RemainAtAllCountForGroup: rsp.GetRemainAtAllCountForGroup(),
RemainAtAllCountForUin: rsp.GetRemainAtAllCountForUin(),
}, nil
}