mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-05 03:23:50 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
db8e673266
@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
"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/client/pb/oidb"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
||||||
@ -594,12 +593,20 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
|
|||||||
// MessageSvc.PbSendMsg
|
// MessageSvc.PbSendMsg
|
||||||
func (c *QQClient) buildFriendSendingPacket(target int64, msgSeq, r, pkgNum, pkgIndex, pkgDiv int32, time int64, m []message.IMessageElement) (uint16, []byte) {
|
func (c *QQClient) buildFriendSendingPacket(target int64, msgSeq, r, pkgNum, pkgIndex, pkgDiv int32, time int64, m []message.IMessageElement) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
|
var ptt *msg.Ptt
|
||||||
|
if len(m) > 0 {
|
||||||
|
if p, ok := m[0].(*message.PrivateVoiceElement); ok {
|
||||||
|
ptt = p.Ptt
|
||||||
|
m = []message.IMessageElement{}
|
||||||
|
}
|
||||||
|
}
|
||||||
req := &msg.SendMessageRequest{
|
req := &msg.SendMessageRequest{
|
||||||
RoutingHead: &msg.RoutingHead{C2C: &msg.C2C{ToUin: target}},
|
RoutingHead: &msg.RoutingHead{C2C: &msg.C2C{ToUin: target}},
|
||||||
ContentHead: &msg.ContentHead{PkgNum: pkgNum, PkgIndex: pkgIndex, DivSeq: pkgDiv},
|
ContentHead: &msg.ContentHead{PkgNum: pkgNum, PkgIndex: pkgIndex, DivSeq: pkgDiv},
|
||||||
MsgBody: &msg.MessageBody{
|
MsgBody: &msg.MessageBody{
|
||||||
RichText: &msg.RichText{
|
RichText: &msg.RichText{
|
||||||
Elems: message.ToProtoElems(m, false),
|
Elems: message.ToProtoElems(m, false),
|
||||||
|
Ptt: ptt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MsgSeq: msgSeq,
|
MsgSeq: msgSeq,
|
||||||
@ -762,37 +769,6 @@ func (c *QQClient) buildImageUploadPacket(data, updKey []byte, commandId int32,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// PttStore.GroupPttUp
|
|
||||||
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
|
|
||||||
seq := c.nextSeq()
|
|
||||||
req := &pb.D388ReqBody{
|
|
||||||
NetType: 3,
|
|
||||||
Subcmd: 3,
|
|
||||||
MsgTryUpPttReq: []*pb.TryUpPttReq{
|
|
||||||
{
|
|
||||||
GroupCode: groupCode,
|
|
||||||
SrcUin: c.Uin,
|
|
||||||
FileMd5: md5,
|
|
||||||
FileSize: int64(size),
|
|
||||||
FileName: md5,
|
|
||||||
SrcTerm: 5,
|
|
||||||
PlatformType: 9,
|
|
||||||
BuType: 4,
|
|
||||||
InnerIp: 0,
|
|
||||||
BuildVer: "6.5.5.663",
|
|
||||||
VoiceLength: voiceLength,
|
|
||||||
Codec: codec,
|
|
||||||
VoiceType: 1,
|
|
||||||
BoolNewUpChan: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Extension: EmptyBytes,
|
|
||||||
}
|
|
||||||
payload, _ := proto.Marshal(req)
|
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttStore.GroupPttUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
|
||||||
return seq, packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProfileService.Pb.ReqSystemMsgNew.Group
|
// ProfileService.Pb.ReqSystemMsgNew.Group
|
||||||
func (c *QQClient) buildSystemMsgNewGroupPacket() (uint16, []byte) {
|
func (c *QQClient) buildSystemMsgNewGroupPacket() (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
@ -912,30 +888,6 @@ func (c *QQClient) buildSystemMsgFriendActionPacket(reqId, requester int64, acce
|
|||||||
return seq, packet
|
return seq, packet
|
||||||
}
|
}
|
||||||
|
|
||||||
// PbMessageSvc.PbMsgWithDraw
|
|
||||||
func (c *QQClient) buildGroupRecallPacket(groupCode int64, msgSeq, msgRan int32) (uint16, []byte) {
|
|
||||||
seq := c.nextSeq()
|
|
||||||
req := &msg.MsgWithDrawReq{
|
|
||||||
GroupWithDraw: []*msg.GroupMsgWithDrawReq{
|
|
||||||
{
|
|
||||||
SubCmd: 1,
|
|
||||||
GroupCode: groupCode,
|
|
||||||
MsgList: []*msg.GroupMsgInfo{
|
|
||||||
{
|
|
||||||
MsgSeq: msgSeq,
|
|
||||||
MsgRandom: msgRan,
|
|
||||||
MsgType: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
UserDef: []byte{0x08, 0x00},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
payload, _ := proto.Marshal(req)
|
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "PbMessageSvc.PbMsgWithDraw", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
|
||||||
return seq, packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// friendlist.ModifyGroupCardReq
|
// friendlist.ModifyGroupCardReq
|
||||||
func (c *QQClient) buildEditGroupTagPacket(groupCode, memberUin int64, newTag string) (uint16, []byte) {
|
func (c *QQClient) buildEditGroupTagPacket(groupCode, memberUin int64, newTag string) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
@ -1169,53 +1121,6 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte)
|
|||||||
return seq, packet
|
return seq, packet
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiMsg.ApplyUp
|
|
||||||
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
|
|
||||||
seq := c.nextSeq()
|
|
||||||
req := &multimsg.MultiReqBody{
|
|
||||||
Subcmd: 1,
|
|
||||||
TermType: 5,
|
|
||||||
PlatformType: 9,
|
|
||||||
NetType: 3,
|
|
||||||
BuildVer: "8.2.0.1296",
|
|
||||||
MultimsgApplyupReq: []*multimsg.MultiMsgApplyUpReq{
|
|
||||||
{
|
|
||||||
DstUin: groupUin,
|
|
||||||
MsgSize: int64(len(data)),
|
|
||||||
MsgMd5: hash,
|
|
||||||
MsgType: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BuType: buType,
|
|
||||||
}
|
|
||||||
payload, _ := proto.Marshal(req)
|
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
|
||||||
return seq, packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiMsg.ApplyDown
|
|
||||||
func (c *QQClient) buildMultiApplyDownPacket(resId string) (uint16, []byte) {
|
|
||||||
seq := c.nextSeq()
|
|
||||||
req := &multimsg.MultiReqBody{
|
|
||||||
Subcmd: 2,
|
|
||||||
TermType: 5,
|
|
||||||
PlatformType: 9,
|
|
||||||
NetType: 3,
|
|
||||||
BuildVer: "8.2.0.1296",
|
|
||||||
MultimsgApplydownReq: []*multimsg.MultiMsgApplyDownReq{
|
|
||||||
{
|
|
||||||
MsgResid: []byte(resId),
|
|
||||||
MsgType: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BuType: 2,
|
|
||||||
ReqChannelType: 2,
|
|
||||||
}
|
|
||||||
payload, _ := proto.Marshal(req)
|
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyDown", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
|
||||||
return seq, packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProfileService.GroupMngReq
|
// ProfileService.GroupMngReq
|
||||||
func (c *QQClient) buildQuitGroupPacket(groupCode int64) (uint16, []byte) {
|
func (c *QQClient) buildQuitGroupPacket(groupCode int64) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
@ -1332,6 +1237,7 @@ func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) {
|
|||||||
packet := packets.BuildUniPacket(c.Uin, seq, "LightAppSvc.mini_app_info.GetAppInfoById", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
packet := packets.BuildUniPacket(c.Uin, seq, "LightAppSvc.mini_app_info.GetAppInfoById", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
return seq, packet
|
return seq, packet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) buildWordSegmentationPacket(data []byte) (uint16, []byte) {
|
func (c *QQClient) buildWordSegmentationPacket(data []byte) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
body := &oidb.D79ReqBody{
|
body := &oidb.D79ReqBody{
|
||||||
|
@ -39,6 +39,7 @@ type QQClient struct {
|
|||||||
FriendList []*FriendInfo
|
FriendList []*FriendInfo
|
||||||
GroupList []*GroupInfo
|
GroupList []*GroupInfo
|
||||||
Online bool
|
Online bool
|
||||||
|
NetLooping bool
|
||||||
|
|
||||||
SequenceId int32
|
SequenceId int32
|
||||||
OutGoingPacketSessionId []byte
|
OutGoingPacketSessionId []byte
|
||||||
@ -158,6 +159,7 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
|
|||||||
"PttCenterSvr.ShortVideoDownReq": decodePttShortVideoDownResponse,
|
"PttCenterSvr.ShortVideoDownReq": decodePttShortVideoDownResponse,
|
||||||
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
|
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
|
||||||
"OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200": decodeOfflineFileDownloadResponse,
|
"OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200": decodeOfflineFileDownloadResponse,
|
||||||
|
"PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse,
|
||||||
},
|
},
|
||||||
sigInfo: &loginSigInfo{},
|
sigInfo: &loginSigInfo{},
|
||||||
requestPacketRequestId: 1921334513,
|
requestPacketRequestId: 1921334513,
|
||||||
@ -200,7 +202,6 @@ func (c *QQClient) Login() (*LoginResponse, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.Online = true
|
|
||||||
go c.netLoop()
|
go c.netLoop()
|
||||||
seq, packet := c.buildLoginPacket()
|
seq, packet := c.buildLoginPacket()
|
||||||
rsp, err := c.sendAndWait(seq, packet)
|
rsp, err := c.sendAndWait(seq, packet)
|
||||||
@ -209,6 +210,7 @@ func (c *QQClient) Login() (*LoginResponse, error) {
|
|||||||
}
|
}
|
||||||
l := rsp.(LoginResponse)
|
l := rsp.(LoginResponse)
|
||||||
if l.Success {
|
if l.Success {
|
||||||
|
c.Online = true
|
||||||
c.lastLostMsg = ""
|
c.lastLostMsg = ""
|
||||||
c.registerClient()
|
c.registerClient()
|
||||||
if !c.heartbeatEnabled {
|
if !c.heartbeatEnabled {
|
||||||
@ -227,6 +229,7 @@ func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, er
|
|||||||
}
|
}
|
||||||
l := rsp.(LoginResponse)
|
l := rsp.(LoginResponse)
|
||||||
if l.Success {
|
if l.Success {
|
||||||
|
c.Online = true
|
||||||
c.registerClient()
|
c.registerClient()
|
||||||
if !c.heartbeatEnabled {
|
if !c.heartbeatEnabled {
|
||||||
c.startHeartbeat()
|
c.startHeartbeat()
|
||||||
@ -242,6 +245,7 @@ func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
|
|||||||
}
|
}
|
||||||
l := rsp.(LoginResponse)
|
l := rsp.(LoginResponse)
|
||||||
if l.Success {
|
if l.Success {
|
||||||
|
c.Online = true
|
||||||
c.registerClient()
|
c.registerClient()
|
||||||
if !c.heartbeatEnabled {
|
if !c.heartbeatEnabled {
|
||||||
c.startHeartbeat()
|
c.startHeartbeat()
|
||||||
@ -620,11 +624,6 @@ func (c *QQClient) sendGroupPoke(groupCode, target int64) {
|
|||||||
_, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target))
|
_, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) RecallGroupMessage(groupCode int64, msgId, msgInternalId int32) {
|
|
||||||
_, pkt := c.buildGroupRecallPacket(groupCode, msgId, msgInternalId)
|
|
||||||
_ = c.send(pkt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *QQClient) UploadGroupImage(groupCode int64, img []byte) (*message.GroupImageElement, error) {
|
func (c *QQClient) UploadGroupImage(groupCode int64, img []byte) (*message.GroupImageElement, error) {
|
||||||
h := md5.Sum(img)
|
h := md5.Sum(img)
|
||||||
seq, pkt := c.buildGroupImageStorePacket(groupCode, h[:], int32(len(img)))
|
seq, pkt := c.buildGroupImageStorePacket(groupCode, h[:], int32(len(img)))
|
||||||
@ -691,42 +690,6 @@ func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) {
|
|||||||
return nil, errors.New("image error")
|
return nil, errors.New("image error")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.GroupVoiceElement, error) {
|
|
||||||
h := md5.Sum(voice)
|
|
||||||
seq, pkt := c.buildGroupPttStorePacket(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
|
|
||||||
r, err := c.sendAndWait(seq, pkt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rsp := r.(pttUploadResponse)
|
|
||||||
if rsp.ResultCode != 0 {
|
|
||||||
return nil, errors.New(rsp.Message)
|
|
||||||
}
|
|
||||||
if rsp.IsExists {
|
|
||||||
goto ok
|
|
||||||
}
|
|
||||||
for i, ip := range rsp.UploadIp {
|
|
||||||
err := c.uploadGroupPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:], 2)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
goto ok
|
|
||||||
}
|
|
||||||
return nil, errors.New("upload failed")
|
|
||||||
ok:
|
|
||||||
return &message.GroupVoiceElement{
|
|
||||||
Ptt: &msg.Ptt{
|
|
||||||
FileType: 4,
|
|
||||||
SrcUin: c.Uin,
|
|
||||||
FileMd5: h[:],
|
|
||||||
FileName: hex.EncodeToString(h[:]) + ".amr",
|
|
||||||
FileSize: int32(len(voice)),
|
|
||||||
GroupFileKey: rsp.FileKey,
|
|
||||||
BoolValid: true,
|
|
||||||
PbReserve: []byte{8, 0, 40, 0, 56, 0},
|
|
||||||
}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) {
|
func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) {
|
||||||
r, err := c.sendAndWait(c.buildGroupImageStorePacket(groupCode, hash, size))
|
r, err := c.sendAndWait(c.buildGroupImageStorePacket(groupCode, hash, size))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -877,12 +840,16 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) {
|
|||||||
_ = c.send(pkt)
|
_ = c.send(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) getCookies() string {
|
func (c *QQClient) getSKey() string {
|
||||||
if c.sigInfo.sKeyExpiredTime < time.Now().Unix() {
|
if c.sigInfo.sKeyExpiredTime < time.Now().Unix() {
|
||||||
c.Debug("skey expired. refresh...")
|
c.Debug("skey expired. refresh...")
|
||||||
_, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket())
|
_, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket())
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("uin=o%d; skey=%s;", c.Uin, c.sigInfo.sKey)
|
return string(c.sigInfo.sKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) getCookies() string {
|
||||||
|
return fmt.Sprintf("uin=o%d; skey=%s;", c.Uin, c.getSKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) getCookiesWithDomain(domain string) string {
|
func (c *QQClient) getCookiesWithDomain(domain string) string {
|
||||||
@ -984,9 +951,10 @@ func (c *QQClient) connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) Disconnect() {
|
func (c *QQClient) Disconnect() {
|
||||||
if c.Online {
|
c.NetLooping = false
|
||||||
c.Online = false
|
c.Online = false
|
||||||
c.Conn.Close()
|
if c.Conn != nil {
|
||||||
|
_ = c.Conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1071,10 +1039,11 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) netLoop() {
|
func (c *QQClient) netLoop() {
|
||||||
|
c.NetLooping = true
|
||||||
reader := binary.NewNetworkReader(c.Conn)
|
reader := binary.NewNetworkReader(c.Conn)
|
||||||
retry := 0
|
retry := 0
|
||||||
errCount := 0
|
errCount := 0
|
||||||
for c.Online {
|
for c.NetLooping {
|
||||||
l, err := reader.ReadInt32()
|
l, err := reader.ReadInt32()
|
||||||
if err == io.EOF || err == io.ErrClosedPipe {
|
if err == io.EOF || err == io.ErrClosedPipe {
|
||||||
c.Error("connection dropped by server: %v", err)
|
c.Error("connection dropped by server: %v", err)
|
||||||
@ -1089,7 +1058,7 @@ func (c *QQClient) netLoop() {
|
|||||||
retry++
|
retry++
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
if retry > 10 {
|
if retry > 10 {
|
||||||
c.Online = false
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1099,7 +1068,7 @@ func (c *QQClient) netLoop() {
|
|||||||
c.Error("parse incoming packet error: %v", err)
|
c.Error("parse incoming packet error: %v", err)
|
||||||
errCount++
|
errCount++
|
||||||
if errCount > 5 {
|
if errCount > 5 {
|
||||||
c.Online = false
|
break
|
||||||
}
|
}
|
||||||
//log.Println("parse incoming packet error: " + err.Error())
|
//log.Println("parse incoming packet error: " + err.Error())
|
||||||
continue
|
continue
|
||||||
@ -1141,6 +1110,7 @@ func (c *QQClient) netLoop() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
c.NetLooping = false
|
||||||
c.Online = false
|
c.Online = false
|
||||||
_ = c.Conn.Close()
|
_ = c.Conn.Close()
|
||||||
if c.lastLostMsg == "" {
|
if c.lastLostMsg == "" {
|
||||||
|
@ -19,12 +19,9 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/binary/jce"
|
"github.com/Mrs4s/MiraiGo/binary/jce"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/longmsg"
|
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
"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/client/pb/oidb"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -88,8 +85,7 @@ func decodeLoginResponse(c *QQClient, _ uint16, payload []byte) (interface{}, er
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if t == 160 {
|
if t == 160 || t == 239 {
|
||||||
|
|
||||||
if t174, ok := m[0x174]; ok { // 短信验证
|
if t174, ok := m[0x174]; ok { // 短信验证
|
||||||
c.t104 = m[0x104]
|
c.t104 = m[0x104]
|
||||||
c.t174 = t174
|
c.t174 = t174
|
||||||
@ -427,7 +423,7 @@ func decodeSvcNotify(c *QQClient, _ uint16, _ []byte) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StatSvc.GetDevLoginInfo
|
// StatSvc.GetDevLoginInfo
|
||||||
func decodeDevListResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeDevListResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
request := &jce.RequestPacket{}
|
request := &jce.RequestPacket{}
|
||||||
request.ReadFrom(jce.NewJceReader(payload))
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
data := &jce.RequestDataVersion2{}
|
data := &jce.RequestDataVersion2{}
|
||||||
@ -440,7 +436,7 @@ func decodeDevListResponse(c *QQClient, _ uint16, payload []byte) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SummaryCard.ReqSummaryCard
|
// SummaryCard.ReqSummaryCard
|
||||||
func decodeSummaryCardResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeSummaryCardResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
request := &jce.RequestPacket{}
|
request := &jce.RequestPacket{}
|
||||||
request.ReadFrom(jce.NewJceReader(payload))
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
data := &jce.RequestDataVersion2{}
|
data := &jce.RequestDataVersion2{}
|
||||||
@ -648,33 +644,8 @@ func decodeGroupImageStoreResponse(_ *QQClient, _ uint16, payload []byte) (inter
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PttStore.GroupPttUp
|
|
||||||
func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
|
||||||
pkt := pb.D388RespBody{}
|
|
||||||
err := proto.Unmarshal(payload, &pkt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rsp := pkt.MsgTryUpPttRsp[0]
|
|
||||||
if rsp.Result != 0 {
|
|
||||||
return pttUploadResponse{
|
|
||||||
ResultCode: rsp.Result,
|
|
||||||
Message: rsp.FailMsg,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
if rsp.BoolFileExit {
|
|
||||||
return pttUploadResponse{IsExists: true}, nil
|
|
||||||
}
|
|
||||||
return pttUploadResponse{
|
|
||||||
UploadKey: rsp.UpUkey,
|
|
||||||
UploadIp: rsp.Uint32UpIp,
|
|
||||||
UploadPort: rsp.Uint32UpPort,
|
|
||||||
FileKey: rsp.FileKey,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LongConn.OffPicUp
|
// LongConn.OffPicUp
|
||||||
func decodeOffPicUpResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeOffPicUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
rsp := cmd0x352.RspBody{}
|
rsp := cmd0x352.RspBody{}
|
||||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1049,84 +1020,20 @@ func decodeForceOfflinePacket(c *QQClient, _ uint16, payload []byte) (interface{
|
|||||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:])
|
r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:])
|
||||||
tips := r.ReadString(2)
|
tips := r.ReadString(2)
|
||||||
if c.Online {
|
|
||||||
c.lastLostMsg = tips
|
c.lastLostMsg = tips
|
||||||
|
c.NetLooping = false
|
||||||
c.Online = false
|
c.Online = false
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatSvc.ReqMSFOffline
|
// StatSvc.ReqMSFOffline
|
||||||
func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error) {
|
func decodeMSFOfflinePacket(c *QQClient, _ uint16, _ []byte) (interface{}, error) {
|
||||||
if c.Online {
|
|
||||||
c.lastLostMsg = "服务器端强制下线."
|
c.lastLostMsg = "服务器端强制下线."
|
||||||
|
c.NetLooping = false
|
||||||
c.Online = false
|
c.Online = false
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiMsg.ApplyUp
|
|
||||||
func decodeMultiApplyUpResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
|
||||||
body := multimsg.MultiRspBody{}
|
|
||||||
if err := proto.Unmarshal(payload, &body); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(body.MultimsgApplyupRsp) == 0 {
|
|
||||||
return nil, errors.New("rsp is empty")
|
|
||||||
}
|
|
||||||
rsp := body.MultimsgApplyupRsp[0]
|
|
||||||
switch rsp.Result {
|
|
||||||
case 0:
|
|
||||||
return rsp, nil
|
|
||||||
case 193:
|
|
||||||
return nil, errors.New("too large")
|
|
||||||
}
|
|
||||||
return nil, errors.New("failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiMsg.ApplyDown
|
|
||||||
func decodeMultiApplyDownResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
|
||||||
body := multimsg.MultiRspBody{}
|
|
||||||
if err := proto.Unmarshal(payload, &body); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(body.MultimsgApplydownRsp) == 0 {
|
|
||||||
return nil, errors.New("not found")
|
|
||||||
}
|
|
||||||
rsp := body.MultimsgApplydownRsp[0]
|
|
||||||
prefix := func() string {
|
|
||||||
if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 {
|
|
||||||
return "https://ssl.htdata.qq.com"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0])
|
|
||||||
}()
|
|
||||||
b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if b[0] != 40 {
|
|
||||||
return nil, errors.New("unexpected body data")
|
|
||||||
}
|
|
||||||
tea := binary.NewTeaCipher(body.MultimsgApplydownRsp[0].MsgKey)
|
|
||||||
r := binary.NewReader(b[1:])
|
|
||||||
i1 := r.ReadInt32()
|
|
||||||
i2 := r.ReadInt32()
|
|
||||||
if i1 > 0 {
|
|
||||||
r.ReadBytes(int(i1)) // im msg head
|
|
||||||
}
|
|
||||||
data := tea.Decrypt(r.ReadBytes(int(i2)))
|
|
||||||
lb := longmsg.LongRspBody{}
|
|
||||||
if err = proto.Unmarshal(data, &lb); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
uc := binary.GZipUncompress(lb.MsgDownRsp[0].MsgContent)
|
|
||||||
mt := msg.PbMultiMsgTransmit{}
|
|
||||||
if err = proto.Unmarshal(uc, &mt); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &mt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OidbSvc.0xd79
|
// OidbSvc.0xd79
|
||||||
func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
pkg := oidb.OIDBSSOPkg{}
|
pkg := oidb.OIDBSSOPkg{}
|
||||||
@ -1144,7 +1051,7 @@ func decodeWordSegmentation(_ *QQClient, _ uint16, payload []byte) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OidbSvc.0x6d6_2
|
// OidbSvc.0x6d6_2
|
||||||
func decodeOIDB6d6Response(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeOIDB6d6Response(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
pkg := oidb.OIDBSSOPkg{}
|
pkg := oidb.OIDBSSOPkg{}
|
||||||
rsp := oidb.D6D6RspBody{}
|
rsp := oidb.D6D6RspBody{}
|
||||||
if err := proto.Unmarshal(payload, &pkg); err != nil {
|
if err := proto.Unmarshal(payload, &pkg); err != nil {
|
||||||
@ -1193,7 +1100,7 @@ func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PttCenterSvr.ShortVideoDownReq
|
// PttCenterSvr.ShortVideoDownReq
|
||||||
func decodePttShortVideoDownResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
rsp := pttcenter.ShortVideoRspBody{}
|
rsp := pttcenter.ShortVideoRspBody{}
|
||||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1205,7 +1112,7 @@ func decodePttShortVideoDownResponse(c *QQClient, _ uint16, payload []byte) (int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LightAppSvc.mini_app_info.GetAppInfoById
|
// LightAppSvc.mini_app_info.GetAppInfoById
|
||||||
func decodeAppInfoResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
pkg := qweb.QWebRsp{}
|
pkg := qweb.QWebRsp{}
|
||||||
rsp := qweb.GetAppInfoByIdRsp{}
|
rsp := qweb.GetAppInfoByIdRsp{}
|
||||||
if err := proto.Unmarshal(payload, &pkg); err != nil {
|
if err := proto.Unmarshal(payload, &pkg); err != nil {
|
||||||
|
@ -245,7 +245,7 @@ type (
|
|||||||
|
|
||||||
ResourceId string
|
ResourceId string
|
||||||
UploadKey []byte
|
UploadKey []byte
|
||||||
UploadIp []int32
|
UploadIp []string
|
||||||
UploadPort []int32
|
UploadPort []int32
|
||||||
FileKey []byte
|
FileKey []byte
|
||||||
}
|
}
|
||||||
|
@ -57,10 +57,10 @@ func (c *QQClient) highwayUpload(ip uint32, port int, updKey, data []byte, cmdId
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 只是为了写的跟上面一样长(bushi,当然也应该是最快的玩法
|
// 只是为了写的跟上面一样长(bushi,当然也应该是最快的玩法
|
||||||
func (c *QQClient) uploadGroupPtt(ip, port int32, updKey, fileKey, data, md5 []byte, codec int64) error {
|
func (c *QQClient) uploadPtt(ip string, port int32, updKey, fileKey, data, md5 []byte) error {
|
||||||
url := make([]byte, 512)[:0]
|
url := make([]byte, 512)[:0]
|
||||||
url = append(url, "http://"...)
|
url = append(url, "http://"...)
|
||||||
url = append(url, binary.UInt32ToIPV4Address(uint32(ip))...)
|
url = append(url, ip...)
|
||||||
url = append(url, ':')
|
url = append(url, ':')
|
||||||
url = strconv.AppendInt(url, int64(port), 10)
|
url = strconv.AppendInt(url, int64(port), 10)
|
||||||
url = append(url, "/?ver=4679&ukey="...)
|
url = append(url, "/?ver=4679&ukey="...)
|
||||||
@ -85,7 +85,7 @@ func (c *QQClient) uploadGroupPtt(ip, port int32, updKey, fileKey, data, md5 []b
|
|||||||
func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error {
|
func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error {
|
||||||
url := fmt.Sprintf(
|
url := fmt.Sprintf(
|
||||||
"http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v",
|
"http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v",
|
||||||
string(c.sigInfo.sKey),
|
c.getSKey(),
|
||||||
c.Uin,
|
c.Uin,
|
||||||
groupCode,
|
groupCode,
|
||||||
len(img),
|
len(img),
|
||||||
|
122
client/multimsg.go
Normal file
122
client/multimsg.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"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/protocol/packets"
|
||||||
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MultiMsg.ApplyUp
|
||||||
|
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &multimsg.MultiReqBody{
|
||||||
|
Subcmd: 1,
|
||||||
|
TermType: 5,
|
||||||
|
PlatformType: 9,
|
||||||
|
NetType: 3,
|
||||||
|
BuildVer: "8.2.0.1296",
|
||||||
|
MultimsgApplyupReq: []*multimsg.MultiMsgApplyUpReq{
|
||||||
|
{
|
||||||
|
DstUin: groupUin,
|
||||||
|
MsgSize: int64(len(data)),
|
||||||
|
MsgMd5: hash,
|
||||||
|
MsgType: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BuType: buType,
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiMsg.ApplyUp
|
||||||
|
func decodeMultiApplyUpResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
body := multimsg.MultiRspBody{}
|
||||||
|
if err := proto.Unmarshal(payload, &body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(body.MultimsgApplyupRsp) == 0 {
|
||||||
|
return nil, errors.New("rsp is empty")
|
||||||
|
}
|
||||||
|
rsp := body.MultimsgApplyupRsp[0]
|
||||||
|
switch rsp.Result {
|
||||||
|
case 0:
|
||||||
|
return rsp, nil
|
||||||
|
case 193:
|
||||||
|
return nil, errors.New("too large")
|
||||||
|
}
|
||||||
|
return nil, errors.New("failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiMsg.ApplyDown
|
||||||
|
func (c *QQClient) buildMultiApplyDownPacket(resId string) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &multimsg.MultiReqBody{
|
||||||
|
Subcmd: 2,
|
||||||
|
TermType: 5,
|
||||||
|
PlatformType: 9,
|
||||||
|
NetType: 3,
|
||||||
|
BuildVer: "8.2.0.1296",
|
||||||
|
MultimsgApplydownReq: []*multimsg.MultiMsgApplyDownReq{
|
||||||
|
{
|
||||||
|
MsgResid: []byte(resId),
|
||||||
|
MsgType: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BuType: 2,
|
||||||
|
ReqChannelType: 2,
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyDown", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiMsg.ApplyDown
|
||||||
|
func decodeMultiApplyDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
body := multimsg.MultiRspBody{}
|
||||||
|
if err := proto.Unmarshal(payload, &body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(body.MultimsgApplydownRsp) == 0 {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
|
rsp := body.MultimsgApplydownRsp[0]
|
||||||
|
prefix := func() string {
|
||||||
|
if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 {
|
||||||
|
return "https://ssl.htdata.qq.com"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0])
|
||||||
|
}()
|
||||||
|
b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b[0] != 40 {
|
||||||
|
return nil, errors.New("unexpected body data")
|
||||||
|
}
|
||||||
|
tea := binary.NewTeaCipher(body.MultimsgApplydownRsp[0].MsgKey)
|
||||||
|
r := binary.NewReader(b[1:])
|
||||||
|
i1 := r.ReadInt32()
|
||||||
|
i2 := r.ReadInt32()
|
||||||
|
if i1 > 0 {
|
||||||
|
r.ReadBytes(int(i1)) // im msg head
|
||||||
|
}
|
||||||
|
data := tea.Decrypt(r.ReadBytes(int(i2)))
|
||||||
|
lb := longmsg.LongRspBody{}
|
||||||
|
if err = proto.Unmarshal(data, &lb); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
uc := binary.GZipUncompress(lb.MsgDownRsp[0].MsgContent)
|
||||||
|
mt := msg.PbMultiMsgTransmit{}
|
||||||
|
if err = proto.Unmarshal(uc, &mt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mt, nil
|
||||||
|
}
|
@ -6238,6 +6238,69 @@ func (x *AnimationImageShow) GetAnimationParam() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UinTypeUserDef struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
FromUinType int32 `protobuf:"varint,1,opt,name=fromUinType,proto3" json:"fromUinType,omitempty"`
|
||||||
|
FromGroupCode int64 `protobuf:"varint,2,opt,name=fromGroupCode,proto3" json:"fromGroupCode,omitempty"`
|
||||||
|
FileUuid string `protobuf:"bytes,3,opt,name=fileUuid,proto3" json:"fileUuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) Reset() {
|
||||||
|
*x = UinTypeUserDef{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_msg_proto_msgTypes[60]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UinTypeUserDef) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_msg_proto_msgTypes[60]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use UinTypeUserDef.ProtoReflect.Descriptor instead.
|
||||||
|
func (*UinTypeUserDef) Descriptor() ([]byte, []int) {
|
||||||
|
return file_msg_proto_rawDescGZIP(), []int{60}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) GetFromUinType() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.FromUinType
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) GetFromGroupCode() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.FromGroupCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UinTypeUserDef) GetFileUuid() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.FileUuid
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_msg_proto protoreflect.FileDescriptor
|
var File_msg_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_msg_proto_rawDesc = []byte{
|
var file_msg_proto_rawDesc = []byte{
|
||||||
@ -7218,11 +7281,18 @@ var file_msg_proto_rawDesc = []byte{
|
|||||||
0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6e, 0x69, 0x6d,
|
0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6e, 0x69, 0x6d,
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x0c, 0x52, 0x0e, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61,
|
0x0c, 0x52, 0x0e, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61,
|
||||||
0x6d, 0x2a, 0x2e, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x61, 0x67, 0x12, 0x09, 0x0a,
|
0x6d, 0x22, 0x74, 0x0a, 0x0e, 0x55, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||||
0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x54,
|
0x44, 0x65, 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x55, 0x69, 0x6e, 0x54, 0x79,
|
||||||
0x49, 0x4e, 0x55, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10,
|
0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x55, 0x69,
|
||||||
0x02, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x6d, 0x73, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x47, 0x72, 0x6f,
|
||||||
0x6f, 0x33,
|
0x75, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x72,
|
||||||
|
0x6f, 0x6d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66,
|
||||||
|
0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66,
|
||||||
|
0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x2a, 0x2e, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x46,
|
||||||
|
0x6c, 0x61, 0x67, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x0d,
|
||||||
|
0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a,
|
||||||
|
0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x02, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x6d, 0x73, 0x67,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -7238,7 +7308,7 @@ func file_msg_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_msg_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_msg_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 60)
|
var file_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 61)
|
||||||
var file_msg_proto_goTypes = []interface{}{
|
var file_msg_proto_goTypes = []interface{}{
|
||||||
(SyncFlag)(0), // 0: SyncFlag
|
(SyncFlag)(0), // 0: SyncFlag
|
||||||
(*GetMessageRequest)(nil), // 1: GetMessageRequest
|
(*GetMessageRequest)(nil), // 1: GetMessageRequest
|
||||||
@ -7301,6 +7371,7 @@ var file_msg_proto_goTypes = []interface{}{
|
|||||||
(*SubMsgType0X4Body)(nil), // 58: SubMsgType0x4Body
|
(*SubMsgType0X4Body)(nil), // 58: SubMsgType0x4Body
|
||||||
(*ResvAttr)(nil), // 59: ResvAttr
|
(*ResvAttr)(nil), // 59: ResvAttr
|
||||||
(*AnimationImageShow)(nil), // 60: AnimationImageShow
|
(*AnimationImageShow)(nil), // 60: AnimationImageShow
|
||||||
|
(*UinTypeUserDef)(nil), // 61: UinTypeUserDef
|
||||||
}
|
}
|
||||||
var file_msg_proto_depIdxs = []int32{
|
var file_msg_proto_depIdxs = []int32{
|
||||||
0, // 0: GetMessageRequest.syncFlag:type_name -> SyncFlag
|
0, // 0: GetMessageRequest.syncFlag:type_name -> SyncFlag
|
||||||
@ -8099,6 +8170,18 @@ func file_msg_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_msg_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*UinTypeUserDef); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
@ -8106,7 +8189,7 @@ func file_msg_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_msg_proto_rawDesc,
|
RawDescriptor: file_msg_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 1,
|
||||||
NumMessages: 60,
|
NumMessages: 61,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@ -724,3 +724,9 @@ message AnimationImageShow {
|
|||||||
int32 effect_id = 1;
|
int32 effect_id = 1;
|
||||||
bytes animation_param = 2;
|
bytes animation_param = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message UinTypeUserDef {
|
||||||
|
int32 fromUinType = 1;
|
||||||
|
int64 fromGroupCode = 2;
|
||||||
|
string fileUuid = 3;
|
||||||
|
}
|
206
client/ptt.go
Normal file
206
client/ptt.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 语音相关处理逻辑
|
||||||
|
|
||||||
|
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
||||||
|
func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.GroupVoiceElement, error) {
|
||||||
|
h := md5.Sum(voice)
|
||||||
|
seq, pkt := c.buildGroupPttStorePacket(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
|
||||||
|
r, err := c.sendAndWait(seq, pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := r.(pttUploadResponse)
|
||||||
|
if rsp.ResultCode != 0 {
|
||||||
|
return nil, errors.New(rsp.Message)
|
||||||
|
}
|
||||||
|
if rsp.IsExists {
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
for i, ip := range rsp.UploadIp {
|
||||||
|
err := c.uploadPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
return nil, errors.New("upload failed")
|
||||||
|
ok:
|
||||||
|
return &message.GroupVoiceElement{
|
||||||
|
Ptt: &msg.Ptt{
|
||||||
|
FileType: 4,
|
||||||
|
SrcUin: c.Uin,
|
||||||
|
FileMd5: h[:],
|
||||||
|
FileName: hex.EncodeToString(h[:]) + ".amr",
|
||||||
|
FileSize: int32(len(voice)),
|
||||||
|
GroupFileKey: rsp.FileKey,
|
||||||
|
BoolValid: true,
|
||||||
|
PbReserve: []byte{8, 0, 40, 0, 56, 0},
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadPrivatePtt 将语音数据使用好友语音通道上传到服务器, 返回 message.PrivateVoiceElement 可直接发送
|
||||||
|
func (c *QQClient) UploadPrivatePtt(target int64, voice []byte) (*message.PrivateVoiceElement, error) {
|
||||||
|
h := md5.Sum(voice)
|
||||||
|
i, err := c.sendAndWait(c.buildPrivatePttStorePacket(target, h[:], int32(len(voice)), int32(len(voice))))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := i.(pttUploadResponse)
|
||||||
|
if rsp.IsExists {
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
for i, ip := range rsp.UploadIp {
|
||||||
|
err := c.uploadPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
return nil, errors.New("upload failed")
|
||||||
|
ok:
|
||||||
|
return &message.PrivateVoiceElement{
|
||||||
|
Ptt: &msg.Ptt{
|
||||||
|
FileType: 4,
|
||||||
|
SrcUin: c.Uin,
|
||||||
|
FileMd5: h[:],
|
||||||
|
FileName: hex.EncodeToString(h[:]) + ".amr",
|
||||||
|
FileSize: int32(len(voice)),
|
||||||
|
FileKey: rsp.FileKey,
|
||||||
|
BoolValid: true,
|
||||||
|
PbReserve: []byte{8, 0, 40, 0, 56, 0},
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttStore.GroupPttUp
|
||||||
|
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &pb.D388ReqBody{
|
||||||
|
NetType: 3,
|
||||||
|
Subcmd: 3,
|
||||||
|
MsgTryUpPttReq: []*pb.TryUpPttReq{
|
||||||
|
{
|
||||||
|
GroupCode: groupCode,
|
||||||
|
SrcUin: c.Uin,
|
||||||
|
FileMd5: md5,
|
||||||
|
FileSize: int64(size),
|
||||||
|
FileName: md5,
|
||||||
|
SrcTerm: 5,
|
||||||
|
PlatformType: 9,
|
||||||
|
BuType: 4,
|
||||||
|
InnerIp: 0,
|
||||||
|
BuildVer: "6.5.5.663",
|
||||||
|
VoiceLength: voiceLength,
|
||||||
|
Codec: codec,
|
||||||
|
VoiceType: 1,
|
||||||
|
BoolNewUpChan: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Extension: EmptyBytes,
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PttStore.GroupPttUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttStore.GroupPttUp
|
||||||
|
func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
pkt := pb.D388RespBody{}
|
||||||
|
err := proto.Unmarshal(payload, &pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := pkt.MsgTryUpPttRsp[0]
|
||||||
|
if rsp.Result != 0 {
|
||||||
|
return pttUploadResponse{
|
||||||
|
ResultCode: rsp.Result,
|
||||||
|
Message: rsp.FailMsg,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if rsp.BoolFileExit {
|
||||||
|
return pttUploadResponse{IsExists: true}, nil
|
||||||
|
}
|
||||||
|
var ip []string
|
||||||
|
for _, i := range rsp.Uint32UpIp {
|
||||||
|
ip = append(ip, binary.UInt32ToIPV4Address(uint32(i)))
|
||||||
|
}
|
||||||
|
return pttUploadResponse{
|
||||||
|
UploadKey: rsp.UpUkey,
|
||||||
|
UploadIp: ip,
|
||||||
|
UploadPort: rsp.Uint32UpPort,
|
||||||
|
FileKey: rsp.FileKey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500
|
||||||
|
func (c *QQClient) buildPrivatePttStorePacket(target int64, md5 []byte, size, voiceLength int32) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &cmd0x346.C346ReqBody{
|
||||||
|
Cmd: 500,
|
||||||
|
Seq: int32(seq),
|
||||||
|
ApplyUploadReq: &cmd0x346.ApplyUploadReq{
|
||||||
|
SenderUin: c.Uin,
|
||||||
|
RecverUin: target,
|
||||||
|
FileType: 2,
|
||||||
|
FileSize: int64(size),
|
||||||
|
FileName: hex.EncodeToString(md5),
|
||||||
|
Bytes_10MMd5: md5, // 超过10M可能会炸
|
||||||
|
},
|
||||||
|
BusinessId: 17,
|
||||||
|
ClientType: 104,
|
||||||
|
ExtensionReq: &cmd0x346.ExtensionReq{
|
||||||
|
Id: 3,
|
||||||
|
PttFormat: 1,
|
||||||
|
NetType: 3,
|
||||||
|
VoiceType: 2,
|
||||||
|
PttTime: voiceLength,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500
|
||||||
|
func decodePrivatePttStoreResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
rsp := cmd0x346.C346RspBody{}
|
||||||
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||||
|
c.Error("unmarshal cmd0x346 rsp body error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rsp.ApplyUploadRsp == nil {
|
||||||
|
c.Error("decode apply upload 500 error: apply rsp is nil.")
|
||||||
|
return nil, errors.New("apply rsp is nil")
|
||||||
|
}
|
||||||
|
if rsp.ApplyUploadRsp.RetCode != 0 {
|
||||||
|
c.Error("decode apply upload 500 error: %v", rsp.ApplyUploadRsp.RetCode)
|
||||||
|
return nil, errors.New(fmt.Sprint(rsp.ApplyUploadRsp.RetCode))
|
||||||
|
}
|
||||||
|
if rsp.ApplyUploadRsp.BoolFileExist {
|
||||||
|
return pttUploadResponse{IsExists: true}, nil
|
||||||
|
}
|
||||||
|
var port []int32
|
||||||
|
for range rsp.ApplyUploadRsp.UploadipList {
|
||||||
|
port = append(port, rsp.ApplyUploadRsp.UploadPort)
|
||||||
|
}
|
||||||
|
return pttUploadResponse{
|
||||||
|
UploadKey: rsp.ApplyUploadRsp.UploadKey,
|
||||||
|
UploadIp: rsp.ApplyUploadRsp.UploadipList,
|
||||||
|
UploadPort: port,
|
||||||
|
FileKey: rsp.ApplyUploadRsp.Uuid,
|
||||||
|
}, nil
|
||||||
|
}
|
66
client/recall.go
Normal file
66
client/recall.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 撤回相关处理逻辑
|
||||||
|
|
||||||
|
func (c *QQClient) RecallGroupMessage(groupCode int64, msgId, msgInternalId int32) {
|
||||||
|
_, pkt := c.buildGroupRecallPacket(groupCode, msgId, msgInternalId)
|
||||||
|
_ = c.send(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) RecallPrivateMessage(uin, ts int64, msgId, msgInternalId int32) {
|
||||||
|
_, pkt := c.buildPrivateRecallPacket(uin, ts, msgId, msgInternalId)
|
||||||
|
_ = c.send(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PbMessageSvc.PbMsgWithDraw
|
||||||
|
func (c *QQClient) buildGroupRecallPacket(groupCode int64, msgSeq, msgRan int32) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &msg.MsgWithDrawReq{
|
||||||
|
GroupWithDraw: []*msg.GroupMsgWithDrawReq{
|
||||||
|
{
|
||||||
|
SubCmd: 1,
|
||||||
|
GroupCode: groupCode,
|
||||||
|
MsgList: []*msg.GroupMsgInfo{
|
||||||
|
{
|
||||||
|
MsgSeq: msgSeq,
|
||||||
|
MsgRandom: msgRan,
|
||||||
|
MsgType: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UserDef: []byte{0x08, 0x00},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PbMessageSvc.PbMsgWithDraw", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildPrivateRecallPacket(uin, ts int64, msgSeq, random int32) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &msg.MsgWithDrawReq{C2CWithDraw: []*msg.C2CMsgWithDrawReq{
|
||||||
|
{
|
||||||
|
MsgInfo: []*msg.C2CMsgInfo{
|
||||||
|
{
|
||||||
|
FromUin: c.Uin,
|
||||||
|
ToUin: uin,
|
||||||
|
MsgTime: ts,
|
||||||
|
MsgUid: int64(random),
|
||||||
|
MsgSeq: msgSeq,
|
||||||
|
MsgRandom: random,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Reserved: []byte{0x08, 0x00},
|
||||||
|
SubCmd: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PbMessageSvc.PbMsgWithDraw", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
@ -47,6 +47,11 @@ type GroupVoiceElement struct {
|
|||||||
Ptt *msg.Ptt
|
Ptt *msg.Ptt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PrivateVoiceElement struct {
|
||||||
|
Data []byte
|
||||||
|
Ptt *msg.Ptt
|
||||||
|
}
|
||||||
|
|
||||||
type FriendImageElement struct {
|
type FriendImageElement struct {
|
||||||
ImageId string
|
ImageId string
|
||||||
Md5 []byte
|
Md5 []byte
|
||||||
@ -267,6 +272,10 @@ func (e *GroupVoiceElement) Type() ElementType {
|
|||||||
return Voice
|
return Voice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *PrivateVoiceElement) Type() ElementType {
|
||||||
|
return Voice
|
||||||
|
}
|
||||||
|
|
||||||
func (e *VoiceElement) Type() ElementType {
|
func (e *VoiceElement) Type() ElementType {
|
||||||
return Voice
|
return Voice
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user