1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00

feature upload ptt by highway.

This commit is contained in:
Mrs4s 2020-12-31 15:43:06 +08:00
parent f5aa81ba50
commit dd94cc86c5
4 changed files with 187 additions and 24 deletions

View File

@ -70,6 +70,8 @@ type QQClient struct {
timeDiff int64
sigInfo *loginSigInfo
highwaySession *highwaySessionInfo
srvSsoAddrs []string
otherSrvAddrs []string
fileStorageInfo *jce.FileStoragePushFSSvcList
pwdFlag bool

View File

@ -256,7 +256,18 @@ func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, er
SigSession: rsp.RspBody.SigSession,
SessionKey: rsp.RspBody.SessionKey,
}
c.Debug("highway session updated.")
for _, srv := range rsp.RspBody.Addrs {
if srv.GetServiceType() == 10 {
for _, addr := range srv.Addrs {
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort()))
}
}
if srv.GetServiceType() == 21 {
for _, addr := range srv.Addrs {
c.otherSrvAddrs = append(c.otherSrvAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort()))
}
}
}
}
}
}

View File

@ -6,15 +6,16 @@ import (
binary2 "encoding/binary"
"encoding/hex"
"fmt"
"net"
"net/http"
"strconv"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"io"
"net"
"net/http"
"strconv"
"time"
)
func (c *QQClient) highwayUpload(ip uint32, port int, updKey, data []byte, cmdId int32) error {
@ -57,6 +58,133 @@ func (c *QQClient) highwayUpload(ip uint32, port int, updKey, data []byte, cmdId
return nil
}
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ext []byte) ([]byte, error) {
// TODO: encrypted upload support.
if len(c.srvSsoAddrs) == 0 {
return nil, errors.New("srv addrs not found. maybe miss some packet?")
}
if c.highwaySession == nil {
return nil, errors.New("highway session not found. maybe miss some packet?")
}
h := md5.New()
length, _ := io.Copy(h, stream)
chunkSize := 8192 * 8
fh := h.Sum(nil)
_, _ = stream.Seek(0, io.SeekStart)
conn, err := net.DialTimeout("tcp", c.srvSsoAddrs[0], time.Second*20)
if err != nil {
return nil, errors.Wrap(err, "connect error")
}
offset := 0
reader := binary.NewNetworkReader(conn)
ticket := c.highwaySession.SigSession
if err = c.highwaySendHeartbreak(conn); err != nil {
return nil, errors.Wrap(err, "echo error")
}
if _, _, err = highwayReadResponse(reader); err != nil {
return nil, errors.Wrap(err, "echo error")
}
var rspExt []byte
for {
chunk := make([]byte, chunkSize)
rl, err := io.ReadFull(stream, chunk)
if err == io.EOF {
break
}
if err == io.ErrUnexpectedEOF {
chunk = chunk[:rl]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: cmdId,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: length,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: ticket,
Md5: ch[:],
FileMd5: fh[:],
},
ReqExtendinfo: ext,
})
offset += rl
_, err = conn.Write(binary.NewWriterF(func(w *binary.Writer) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(chunk)))
w.Write(head)
w.Write(chunk)
w.WriteByte(41)
}))
if err != nil {
return nil, errors.Wrap(err, "write conn error")
}
rspHead, _, err := highwayReadResponse(reader)
if err != nil {
return nil, errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return nil, errors.New("upload failed")
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil {
ticket = rspHead.MsgSeghead.Serviceticket
}
}
return rspExt, nil
}
func (c *QQClient) highwaySendHeartbreak(conn net.Conn) error {
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.Echo",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: 0,
LocaleId: 2052,
},
})
_, err := conn.Write(binary.NewWriterF(func(w *binary.Writer) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(0)
w.Write(head)
w.WriteByte(41)
}))
return err
}
func highwayReadResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, []byte, error) {
_, err := r.ReadByte()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
head, _ := r.ReadBytes(int(hl))
payload, _ := r.ReadBytes(int(a2))
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp, payload, nil
}
// 只是为了写的跟上面一样长(bushi当然也应该是最快的玩法
func (c *QQClient) uploadPtt(ip string, port int32, updKey, fileKey, data, md5 []byte) error {
url := make([]byte, 512)[:0]

View File

@ -1,6 +1,7 @@
package client
import (
"bytes"
"crypto/md5"
"encoding/hex"
@ -19,6 +20,22 @@ import (
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.GroupVoiceElement, error) {
h := md5.Sum(voice)
ext := c.buildGroupPttStoreBDHExt(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
rsp, err := c.highwayUploadByBDH(bytes.NewReader(voice), 29, ext)
if err != nil {
return nil, err
}
if len(rsp) == 0 {
return nil, errors.New("miss rsp")
}
pkt := pb.D388RespBody{}
if err = proto.Unmarshal(rsp, &pkt); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if len(pkt.MsgTryUpPttRsp) == 0 {
return nil, errors.New("miss try up rsp")
}
/*
seq, pkt := c.buildGroupPttStorePacket(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
r, err := c.sendAndWait(seq, pkt)
if err != nil {
@ -39,7 +56,8 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.Group
goto ok
}
return nil, errors.New("upload failed")
ok:
ok:
*/
return &message.GroupVoiceElement{
Ptt: &msg.Ptt{
FileType: proto.Int32(4),
@ -47,7 +65,7 @@ ok:
FileMd5: h[:],
FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"),
FileSize: proto.Int32(int32(len(voice))),
GroupFileKey: rsp.FileKey,
GroupFileKey: pkt.MsgTryUpPttRsp[0].FileKey,
BoolValid: proto.Bool(true),
PbReserve: []byte{8, 0, 40, 0, 56, 0},
}}, nil
@ -89,6 +107,12 @@ ok:
// PttStore.GroupPttUp
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
seq := c.nextSeq()
packet := packets.BuildUniPacket(c.Uin, seq, "PttStore.GroupPttUp", 1, c.OutGoingPacketSessionId,
EmptyBytes, c.sigInfo.d2Key, c.buildGroupPttStoreBDHExt(groupCode, md5, size, codec, voiceLength))
return seq, packet
}
func (c *QQClient) buildGroupPttStoreBDHExt(groupCode int64, md5 []byte, size, codec, voiceLength int32) []byte {
req := &pb.D388ReqBody{
NetType: 3,
Subcmd: 3,
@ -110,11 +134,9 @@ func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, c
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
return payload
}
// PttStore.GroupPttUp