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:
parent
f5aa81ba50
commit
dd94cc86c5
@ -70,6 +70,8 @@ type QQClient struct {
|
|||||||
timeDiff int64
|
timeDiff int64
|
||||||
sigInfo *loginSigInfo
|
sigInfo *loginSigInfo
|
||||||
highwaySession *highwaySessionInfo
|
highwaySession *highwaySessionInfo
|
||||||
|
srvSsoAddrs []string
|
||||||
|
otherSrvAddrs []string
|
||||||
fileStorageInfo *jce.FileStoragePushFSSvcList
|
fileStorageInfo *jce.FileStoragePushFSSvcList
|
||||||
pwdFlag bool
|
pwdFlag bool
|
||||||
|
|
||||||
|
@ -256,7 +256,18 @@ func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, er
|
|||||||
SigSession: rsp.RspBody.SigSession,
|
SigSession: rsp.RspBody.SigSession,
|
||||||
SessionKey: rsp.RspBody.SessionKey,
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,16 @@ import (
|
|||||||
binary2 "encoding/binary"
|
binary2 "encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"google.golang.org/protobuf/proto"
|
"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 {
|
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
|
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,当然也应该是最快的玩法
|
// 只是为了写的跟上面一样长(bushi,当然也应该是最快的玩法
|
||||||
func (c *QQClient) uploadPtt(ip string, port int32, updKey, fileKey, data, md5 []byte) 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]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
@ -19,6 +20,22 @@ import (
|
|||||||
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
||||||
func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.GroupVoiceElement, error) {
|
func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.GroupVoiceElement, error) {
|
||||||
h := md5.Sum(voice)
|
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)))
|
seq, pkt := c.buildGroupPttStorePacket(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
|
||||||
r, err := c.sendAndWait(seq, pkt)
|
r, err := c.sendAndWait(seq, pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -40,6 +57,7 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice []byte) (*message.Group
|
|||||||
}
|
}
|
||||||
return nil, errors.New("upload failed")
|
return nil, errors.New("upload failed")
|
||||||
ok:
|
ok:
|
||||||
|
*/
|
||||||
return &message.GroupVoiceElement{
|
return &message.GroupVoiceElement{
|
||||||
Ptt: &msg.Ptt{
|
Ptt: &msg.Ptt{
|
||||||
FileType: proto.Int32(4),
|
FileType: proto.Int32(4),
|
||||||
@ -47,7 +65,7 @@ ok:
|
|||||||
FileMd5: h[:],
|
FileMd5: h[:],
|
||||||
FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"),
|
FileName: proto.String(hex.EncodeToString(h[:]) + ".amr"),
|
||||||
FileSize: proto.Int32(int32(len(voice))),
|
FileSize: proto.Int32(int32(len(voice))),
|
||||||
GroupFileKey: rsp.FileKey,
|
GroupFileKey: pkt.MsgTryUpPttRsp[0].FileKey,
|
||||||
BoolValid: proto.Bool(true),
|
BoolValid: proto.Bool(true),
|
||||||
PbReserve: []byte{8, 0, 40, 0, 56, 0},
|
PbReserve: []byte{8, 0, 40, 0, 56, 0},
|
||||||
}}, nil
|
}}, nil
|
||||||
@ -89,6 +107,12 @@ ok:
|
|||||||
// PttStore.GroupPttUp
|
// PttStore.GroupPttUp
|
||||||
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
|
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
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{
|
req := &pb.D388ReqBody{
|
||||||
NetType: 3,
|
NetType: 3,
|
||||||
Subcmd: 3,
|
Subcmd: 3,
|
||||||
@ -110,11 +134,9 @@ func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, c
|
|||||||
BoolNewUpChan: true,
|
BoolNewUpChan: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Extension: EmptyBytes,
|
|
||||||
}
|
}
|
||||||
payload, _ := proto.Marshal(req)
|
payload, _ := proto.Marshal(req)
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttStore.GroupPttUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
return payload
|
||||||
return seq, packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PttStore.GroupPttUp
|
// PttStore.GroupPttUp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user