Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 完善合并转发 #87

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions client/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,25 @@ func decodeOlPushServicePacket(c *QQClient, pkt *network.Packet) (any, error) {
}
switch typ {
case 166, 208, 529: // 166 for private msg, 208 for private record, 529 for private file
prvMsg := msgConverter.ParsePrivateMessage(&msg)
_ = c.PreprocessPrivateMessageEvent(prvMsg)
prvMsg := msgConverter.ParsePrivateMessage(pkg)
c.PreprocessPrivateMessageEvent(prvMsg)
if prvMsg.Sender.Uin != c.Uin {
c.PrivateMessageEvent.dispatch(c, prvMsg)
} else {
c.SelfPrivateMessageEvent.dispatch(c, prvMsg)
}
return nil, nil
case 82: // group msg
grpMsg := msgConverter.ParseGroupMessage(&msg)
_ = c.PreprocessGroupMessageEvent(grpMsg)
grpMsg := msgConverter.ParseGroupMessage(pkg)
c.PreprocessGroupMessageEvent(grpMsg)
if grpMsg.Sender.Uin != c.Uin {
c.GroupMessageEvent.dispatch(c, grpMsg)
} else {
c.SelfGroupMessageEvent.dispatch(c, grpMsg)
}
return nil, nil
case 141: // temp msg
tempMsg := msgConverter.ParseTempMessage(&msg)
tempMsg := msgConverter.ParseTempMessage(pkg)
if tempMsg.Sender.Uin != c.Uin {
c.TempMessageEvent.dispatch(c, tempMsg)
} else {
Expand Down Expand Up @@ -325,7 +325,7 @@ func decodeKickNTPacket(c *QQClient, pkt *network.Packet) (any, error) {
return nil, nil
}

func (c *QQClient) PreprocessGroupMessageEvent(msg *msgConverter.GroupMessage) error {
func (c *QQClient) PreprocessGroupMessageEvent(msg *msgConverter.GroupMessage) {
for _, elem := range msg.Elements {
switch e := elem.(type) {
case *msgConverter.ImageElement:
Expand All @@ -340,18 +340,25 @@ func (c *QQClient) PreprocessGroupMessageEvent(msg *msgConverter.GroupMessage) e
case *msgConverter.ShortVideoElement:
url, err := c.GetVideoUrl(true, e)
if err != nil {
return err
continue
}
e.Url = url
case *msgConverter.FileElement:
url, _ := c.GetGroupFileUrl(msg.GroupUin, e.FileId)
e.FileUrl = url
case *msgConverter.ForwardMessage:
if e.Nodes == nil {
if forward, err := c.FetchForwardMsg(e.ResID); err != nil {
continue
} else {
e.Nodes = forward.Nodes
}
}
}
}
return nil
}

func (c *QQClient) PreprocessPrivateMessageEvent(msg *msgConverter.PrivateMessage) error {
func (c *QQClient) PreprocessPrivateMessageEvent(msg *msgConverter.PrivateMessage) {
if friend := c.GetCachedFriendInfo(msg.Sender.Uin); friend != nil {
msg.Sender.Nickname = friend.Nickname
}
Expand All @@ -366,24 +373,31 @@ func (c *QQClient) PreprocessPrivateMessageEvent(msg *msgConverter.PrivateMessag
case *msgConverter.VoiceElement:
url, err := c.GetPrivateRecordUrl(e.Node)
if err != nil {
return err
continue
}
e.Url = url
case *msgConverter.ShortVideoElement:
url, err := c.GetVideoUrl(false, e)
if err != nil {
return err
continue
}
e.Url = url
case *msgConverter.FileElement:
url, err := c.GetPrivateFileUrl(e.FileUUID, e.FileHash)
if err != nil {
return err
continue
}
e.FileUrl = url
case *msgConverter.ForwardMessage:
if e.Nodes == nil {
if forward, err := c.FetchForwardMsg(e.ResID); err != nil {
continue
} else {
e.Nodes = forward.Nodes
}
}
}
}
return nil
}

func (c *QQClient) PreprocessOther(g eventConverter.CanPreprocess) error {
Expand Down
51 changes: 50 additions & 1 deletion client/message.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package client

import (
"fmt"
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/action"
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
message2 "github.com/LagrangeDev/LagrangeGo/message"
Expand Down Expand Up @@ -98,7 +99,7 @@ func (c *QQClient) SendPrivateMessage(uin uint32, elements []message2.IMessageEl
resp := &message2.PrivateMessage{
Id: ret.PrivateSequence,
InternalId: mr,
CleintSeq: clientSeq,
ClientSeq: clientSeq,
Self: c.Uin,
Target: uin,
Time: ret.Timestamp1,
Expand Down Expand Up @@ -145,6 +146,54 @@ func (c *QQClient) SendTempMessage(groupUin uint32, uin uint32, elements []messa
return resp, nil
}

// BuildFakeMessage make a fake message
func (c *QQClient) BuildFakeMessage(msgElems []*message2.ForwardNode) []*message.PushMsgBody {
body := make([]*message.PushMsgBody, len(msgElems))
for idx, elem := range msgElems {
avatar := fmt.Sprintf("https://q.qlogo.cn/headimg_dl?dst_uin=%d&spec=640&img_type=jpg", elem.SenderId)
body[idx] = &message.PushMsgBody{
ResponseHead: &message.ResponseHead{
FromUid: proto.String(""),
},
ContentHead: &message.ContentHead{
Type: uint32(utils.Ternary(elem.GroupId != 0, 82, 9)),
MsgId: proto.Uint32(crypto.RandU32()),
Sequence: proto.Uint32(crypto.RandU32()),
TimeStamp: proto.Uint32(uint32(utils.TimeStamp())),
Field7: proto.Uint64(1),
Field8: proto.Uint32(0),
Field9: proto.Uint32(0),
Foward: &message.ForwardHead{
Field1: proto.Uint32(0),
Field2: proto.Uint32(0),
Field3: utils.Ternary(elem.GroupId != 0, proto.Uint32(0), proto.Uint32(2)),
UnknownBase64: proto.String(avatar),
Avatar: proto.String(avatar),
},
},
}
if elem.GroupId != 0 {
body[idx].ResponseHead.FromUin = uint32(elem.SenderId)
body[idx].ResponseHead.Grp = &message.ResponseGrp{
GroupUin: uint32(elem.GroupId),
MemberName: elem.SenderName,
Unknown5: 2,
}
c.preProcessGroupMessage(uint32(elem.GroupId), elem.Message)
} else {
body[idx].ResponseHead.ToUid = proto.String(c.GetUid(c.Uin))
body[idx].ResponseHead.Forward = &message.ResponseForward{
FriendName: proto.String(elem.SenderName),
}
body[idx].ContentHead.SubType = proto.Uint32(4)
body[idx].ContentHead.DivSeq = proto.Uint32(4)
c.preProcessPrivateMessage(c.Uin, elem.Message)
}
body[idx].Body = message2.PackElementsToBody(elem.Message)
}
return body
}

func (c *QQClient) preProcessGroupMessage(groupUin uint32, elements []message2.IMessageElement) []message2.IMessageElement {
for _, element := range elements {
switch elem := element.(type) {
Expand Down
83 changes: 76 additions & 7 deletions client/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ package client
import (
"errors"

"github.com/LagrangeDev/LagrangeGo/utils/crypto"

"github.com/LagrangeDev/LagrangeGo/client/packets/pb/service/oidb"

"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
"github.com/LagrangeDev/LagrangeGo/internal/proto"

"github.com/LagrangeDev/LagrangeGo/client/entity"
messagePkt "github.com/LagrangeDev/LagrangeGo/client/packets/message"
oidb2 "github.com/LagrangeDev/LagrangeGo/client/packets/oidb"
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/service/oidb"
"github.com/LagrangeDev/LagrangeGo/internal/proto"
message2 "github.com/LagrangeDev/LagrangeGo/message"
"github.com/LagrangeDev/LagrangeGo/utils/binary"
"github.com/LagrangeDev/LagrangeGo/utils/crypto"
)

// FetchFriends 获取好友列表信息,使用token可以获取下一页的群成员信息
Expand Down Expand Up @@ -706,3 +705,73 @@ func (c *QQClient) DeleteGroupFolder(groupUin uint32, folderID string) error {
}
return oidb2.ParseGroupFolderDeleteResp(resp)
}

// FetchForwardMsg 获取合并转发消息
func (c *QQClient) FetchForwardMsg(resId string) (msg *message2.ForwardMessage, err error) {
if resId == "" {
return msg, errors.New("empty resId")
}
forwardMsg := &message2.ForwardMessage{ResID: resId}
pkt, err := messagePkt.BuildMultiMsgDownloadReq(c.GetUid(c.Uin), resId)
if err != nil {
return forwardMsg, err
}
resp, err := c.sendUniPacketAndWait("trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg", pkt)
if err != nil {
return forwardMsg, err
}
pasted, err := messagePkt.ParseMultiMsgDownloadResp(resp)
if err != nil {
return forwardMsg, err
}
if pasted.Result == nil || pasted.Result.Payload == nil {
return forwardMsg, errors.New("empty response data")
}
data := binary.GZipUncompress(pasted.Result.Payload)
result := &message.LongMsgResult{}
if err = proto.Unmarshal(data, result); err != nil {
return forwardMsg, err
}
forwardMsg.Nodes = make([]*message2.ForwardNode, len(result.Action.ActionData.MsgBody))
for idx, b := range result.Action.ActionData.MsgBody {
isGroupMsg := b.ResponseHead.Grp != nil
forwardMsg.Nodes[idx] = &message2.ForwardNode{
SenderId: int64(b.ResponseHead.FromUin),
SenderName: b.ResponseHead.Forward.FriendName.Unwrap(),
Time: int32(b.ContentHead.TimeStamp.Unwrap()),
}
if isGroupMsg {
forwardMsg.Nodes[idx].GroupId = int64(b.ResponseHead.Grp.GroupUin)
grpMsg := message2.ParseGroupMessage(b)
c.PreprocessGroupMessageEvent(grpMsg)
forwardMsg.Nodes[idx].Message = grpMsg.Elements
} else {
prvMsg := message2.ParsePrivateMessage(b)
c.PreprocessPrivateMessageEvent(prvMsg)
forwardMsg.Nodes[idx].Message = prvMsg.Elements
}
}
return forwardMsg, nil
}

// UploadForwardMsg 上传合并转发消息
// groupUin should be the group number where the uploader is located or 0 (c2c)
func (c *QQClient) UploadForwardMsg(forwardNodes []*message2.ForwardNode, groupUin uint32) (resId string, err error) {
msgBody := c.BuildFakeMessage(forwardNodes)
pkt, err := messagePkt.BuildMultiMsgUploadReq(c.GetUid(c.Uin), groupUin, msgBody)
if err != nil {
return "", err
}
resp, err := c.sendUniPacketAndWait("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", pkt)
if err != nil {
return "", err
}
pasted, err := messagePkt.ParseMultiMsgUploadResp(resp)
if err != nil {
return "", err
}
if pasted.Result == nil {
return "", errors.New("empty response data")
}
return pasted.Result.ResId, nil
}
2 changes: 1 addition & 1 deletion client/packets/album/get_media_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func BuildGetMediaListReq(selfUin uint32, groupUin uint32, albumId string, pageI
Field4: "",
PageInfo: pageInfo,
},
UinTimeStamp: utils.GenerateUinTimestamp(selfUin),
UinTimeStamp: utils.UinTimestamp(selfUin),
Field10: &album.QzoneGetMediaList_F10{
AppIdFlag: "fc-appid",
AppIdValue: "100",
Expand Down
32 changes: 32 additions & 0 deletions client/packets/message/multi_msg_download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package message

import (
"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
"github.com/LagrangeDev/LagrangeGo/internal/proto"
)

func BuildMultiMsgDownloadReq(uid string, resId string) ([]byte, error) {
return proto.Marshal(&message.RecvLongMsgReq{
Info: &message.RecvLongMsgInfo{
Uid: &message.LongMsgUid{
Uid: proto.String(uid),
},
ResId: proto.String(resId),
Acquire: true,
},
Settings: &message.LongMsgSettings{
Field1: 2,
Field2: 0,
Field3: 0,
Field4: 0,
},
})
}

func ParseMultiMsgDownloadResp(data []byte) (resp *message.RecvLongMsgResp, err error) {
resp = &message.RecvLongMsgResp{}
if err = proto.Unmarshal(data, resp); err != nil {
return nil, err
}
return resp, nil
}
49 changes: 49 additions & 0 deletions client/packets/message/multi_msg_upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package message

import (
"strconv"

"github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
message2 "github.com/LagrangeDev/LagrangeGo/client/packets/pb/message"
"github.com/LagrangeDev/LagrangeGo/internal/proto"
"github.com/LagrangeDev/LagrangeGo/utils"
"github.com/LagrangeDev/LagrangeGo/utils/binary"
)

func BuildMultiMsgUploadReq(selfUid string, groupUin uint32, msg []*message.PushMsgBody) ([]byte, error) {
longMsgResult := &message2.LongMsgResult{
Action: &message2.LongMsgAction{
ActionCommand: "MultiMsg",
ActionData: &message2.LongMsgContent{
MsgBody: msg,
},
},
}
longMsgResultData, _ := proto.Marshal(longMsgResult)
payload := binary.GZipCompress(longMsgResultData)
req := &message2.SendLongMsgReq{
Info: &message2.SendLongMsgInfo{
Type: utils.Ternary[uint32](groupUin == 0, 1, 3),
Uid: &message2.LongMsgUid{
Uid: utils.Ternary(groupUin == 0, proto.String(selfUid), proto.String(strconv.Itoa(int(groupUin)))),
},
GroupUin: proto.Uint32(groupUin),
Payload: payload,
},
Settings: &message2.LongMsgSettings{
Field1: 4,
Field2: 1,
Field3: 7,
Field4: 0,
},
}
return proto.Marshal(req)
}

func ParseMultiMsgUploadResp(data []byte) (resp *message2.SendLongMsgResp, err error) {
resp = &message2.SendLongMsgResp{}
if err = proto.Unmarshal(data, resp); err != nil {
return nil, err
}
return resp, nil
}
11 changes: 10 additions & 1 deletion client/packets/oidb/group_file_upload.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package oidb

import (
"errors"

"github.com/LagrangeDev/LagrangeGo/client/packets/pb/service/oidb"
"github.com/LagrangeDev/LagrangeGo/message"
)
Expand All @@ -26,5 +28,12 @@ func BuildGroupFileUploadReq(groupUin uint32, file *message.FileElement, targetD
}

func ParseGroupFileUploadResp(data []byte) (*oidb.OidbSvcTrpcTcp0X6D6Response, error) {
return ParseTypedError[oidb.OidbSvcTrpcTcp0X6D6Response](data)
var resp oidb.OidbSvcTrpcTcp0X6D6Response
if _, err := ParseOidbPacket(data, &resp); err != nil {
return nil, err
}
if resp.Upload.RetCode != 0 {
return nil, errors.New(resp.Upload.ClientWording)
}
return &resp, nil
}
Loading
Loading