mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
feature short video upload & sending.
This commit is contained in:
parent
72521dec9b
commit
7605dc4635
@ -16,7 +16,6 @@ import (
|
|||||||
"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/oidb"
|
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||||
@ -1010,31 +1009,6 @@ func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, hei
|
|||||||
return seq, packet
|
return seq, packet
|
||||||
}
|
}
|
||||||
|
|
||||||
// PttCenterSvr.ShortVideoDownReq
|
|
||||||
func (c *QQClient) buildPttShortVideoDownReqPacket(uuid, md5 []byte) (uint16, []byte) {
|
|
||||||
seq := c.nextSeq()
|
|
||||||
body := &pttcenter.ShortVideoReqBody{
|
|
||||||
Cmd: 400,
|
|
||||||
Seq: int32(seq),
|
|
||||||
PttShortVideoDownloadReq: &pttcenter.ShortVideoDownloadReq{
|
|
||||||
FromUin: c.Uin,
|
|
||||||
ToUin: c.Uin,
|
|
||||||
ChatType: 1,
|
|
||||||
ClientType: 7,
|
|
||||||
FileId: string(uuid),
|
|
||||||
GroupCode: 1,
|
|
||||||
FileMd5: md5,
|
|
||||||
BusinessType: 1,
|
|
||||||
FileType: 2,
|
|
||||||
DownType: 2,
|
|
||||||
SceneType: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
payload, _ := proto.Marshal(body)
|
|
||||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.ShortVideoDownReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
|
||||||
return seq, packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// LightAppSvc.mini_app_info.GetAppInfoById
|
// LightAppSvc.mini_app_info.GetAppInfoById
|
||||||
func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) {
|
func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) {
|
||||||
seq := c.nextSeq()
|
seq := c.nextSeq()
|
||||||
|
@ -139,7 +139,6 @@ var decoders = map[string]func(*QQClient, uint16, []byte) (interface{}, error){
|
|||||||
"OidbSvc.0xd79": decodeWordSegmentation,
|
"OidbSvc.0xd79": decodeWordSegmentation,
|
||||||
"OidbSvc.0x990": decodeTranslateResponse,
|
"OidbSvc.0x990": decodeTranslateResponse,
|
||||||
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
|
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
|
||||||
"PttCenterSvr.ShortVideoDownReq": decodePttShortVideoDownResponse,
|
|
||||||
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
|
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
|
||||||
"PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse,
|
"PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse,
|
||||||
}
|
}
|
||||||
@ -421,14 +420,6 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) GetShortVideoUrl(uuid, md5 []byte) string {
|
|
||||||
i, err := c.sendAndWait(c.buildPttShortVideoDownReqPacket(uuid, md5))
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return i.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *message.PrivateMessage {
|
func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *message.PrivateMessage {
|
||||||
mr := int32(rand.Uint32())
|
mr := int32(rand.Uint32())
|
||||||
seq := c.nextFriendSeq()
|
seq := c.nextFriendSeq()
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/notify"
|
"github.com/Mrs4s/MiraiGo/client/pb/notify"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/qweb"
|
"github.com/Mrs4s/MiraiGo/client/pb/qweb"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@ -992,18 +991,6 @@ func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PttCenterSvr.ShortVideoDownReq
|
|
||||||
func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
|
||||||
rsp := pttcenter.ShortVideoRspBody{}
|
|
||||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
|
||||||
}
|
|
||||||
if rsp.PttShortVideoDownloadRsp == nil || rsp.PttShortVideoDownloadRsp.DownloadAddr == nil {
|
|
||||||
return nil, errors.New("resp error")
|
|
||||||
}
|
|
||||||
return rsp.PttShortVideoDownloadRsp.DownloadAddr.Host[0] + rsp.PttShortVideoDownloadRsp.DownloadAddr.UrlArgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LightAppSvc.mini_app_info.GetAppInfoById
|
// LightAppSvc.mini_app_info.GetAppInfoById
|
||||||
func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
pkg := qweb.QWebRsp{}
|
pkg := qweb.QWebRsp{}
|
||||||
|
@ -97,11 +97,16 @@ func (c *QQClient) highwayUploadStream(ip uint32, port int, updKey []byte, strea
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ticket, ext []byte) ([]byte, error) {
|
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ticket, ext []byte, encrypt bool) ([]byte, error) {
|
||||||
// TODO: encrypted upload support.
|
|
||||||
if len(c.srvSsoAddrs) == 0 {
|
if len(c.srvSsoAddrs) == 0 {
|
||||||
return nil, errors.New("srv addrs not found. maybe miss some packet?")
|
return nil, errors.New("srv addrs not found. maybe miss some packet?")
|
||||||
}
|
}
|
||||||
|
if encrypt {
|
||||||
|
if c.highwaySession == nil || len(c.highwaySession.SessionKey) == 0 {
|
||||||
|
return nil, errors.New("session key not found. maybe miss some packet?")
|
||||||
|
}
|
||||||
|
ext = binary.NewTeaCipher(c.highwaySession.SessionKey).Encrypt(ext)
|
||||||
|
}
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
length, _ := io.Copy(h, stream)
|
length, _ := io.Copy(h, stream)
|
||||||
fh := h.Sum(nil)
|
fh := h.Sum(nil)
|
||||||
@ -194,7 +199,7 @@ func (c *QQClient) highwayUploadFileMultiThreadingByBDH(path string, cmdId int32
|
|||||||
return nil, errors.Wrap(err, "open file error")
|
return nil, errors.Wrap(err, "open file error")
|
||||||
}
|
}
|
||||||
if stat.Size() < 1024*1024*3 {
|
if stat.Size() < 1024*1024*3 {
|
||||||
return c.highwayUploadByBDH(file, cmdId, ticket, ext)
|
return c.highwayUploadByBDH(file, cmdId, ticket, ext, false)
|
||||||
}
|
}
|
||||||
type BlockMetaData struct {
|
type BlockMetaData struct {
|
||||||
Id int
|
Id int
|
||||||
|
@ -44,7 +44,7 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
|
|||||||
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i]))
|
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes); err == nil {
|
if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes, true); err == nil {
|
||||||
goto ok
|
goto ok
|
||||||
}
|
}
|
||||||
return nil, errors.New("upload failed")
|
return nil, errors.New("upload failed")
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,15 +5,29 @@ option go_package = ".;pttcenter";
|
|||||||
message ShortVideoReqBody {
|
message ShortVideoReqBody {
|
||||||
int32 cmd = 1;
|
int32 cmd = 1;
|
||||||
int32 seq = 2;
|
int32 seq = 2;
|
||||||
|
ShortVideoUploadReq pttShortVideoUploadReq = 3;
|
||||||
ShortVideoDownloadReq pttShortVideoDownloadReq = 4;
|
ShortVideoDownloadReq pttShortVideoDownloadReq = 4;
|
||||||
|
repeated ShortVideoExtensionReq extensionReq = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ShortVideoRspBody {
|
message ShortVideoRspBody {
|
||||||
int32 cmd = 1;
|
int32 cmd = 1;
|
||||||
int32 seq = 2;
|
int32 seq = 2;
|
||||||
|
ShortVideoUploadRsp pttShortVideoUploadRsp = 3;
|
||||||
ShortVideoDownloadRsp pttShortVideoDownloadRsp = 4;
|
ShortVideoDownloadRsp pttShortVideoDownloadRsp = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ShortVideoUploadReq {
|
||||||
|
int64 fromUin = 1;
|
||||||
|
int64 toUin = 2;
|
||||||
|
int32 chatType = 3;
|
||||||
|
int32 clientType = 4;
|
||||||
|
ShortVideoFileInfo info = 5;
|
||||||
|
int64 groupCode = 6;
|
||||||
|
int32 agentType = 7;
|
||||||
|
int32 businessType = 8;
|
||||||
|
int32 supportLargeSize = 20;
|
||||||
|
}
|
||||||
message ShortVideoDownloadReq {
|
message ShortVideoDownloadReq {
|
||||||
int64 fromUin = 1;
|
int64 fromUin = 1;
|
||||||
int64 toUin = 2;
|
int64 toUin = 2;
|
||||||
@ -42,6 +56,36 @@ message ShortVideoDownloadRsp {
|
|||||||
bytes encryptKey = 10;
|
bytes encryptKey = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ShortVideoUploadRsp {
|
||||||
|
int32 retCode = 1;
|
||||||
|
string retMsg = 2;
|
||||||
|
repeated ShortVideoIpList sameAreaOutAddr = 3;
|
||||||
|
repeated ShortVideoIpList diffAreaOutAddr = 4;
|
||||||
|
string fileId = 5;
|
||||||
|
bytes uKey = 6;
|
||||||
|
int32 fileExists = 7;
|
||||||
|
repeated ShortVideoIpList sameAreaInnerAddr = 8;
|
||||||
|
repeated ShortVideoIpList diffAreaInnerAddr = 9;
|
||||||
|
repeated DataHole dataHole = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ShortVideoFileInfo {
|
||||||
|
string fileName = 1;
|
||||||
|
bytes fileMd5 = 2;
|
||||||
|
bytes thumbFileMd5 = 3;
|
||||||
|
int64 fileSize = 4;
|
||||||
|
int32 fileResLength = 5;
|
||||||
|
int32 fileResWidth = 6;
|
||||||
|
int32 fileFormat = 7;
|
||||||
|
int32 fileTime = 8;
|
||||||
|
int64 thumbFileSize = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DataHole {
|
||||||
|
int64 begin = 1;
|
||||||
|
int64 end = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ShortVideoIpList {
|
message ShortVideoIpList {
|
||||||
int32 ip = 1;
|
int32 ip = 1;
|
||||||
int32 port = 2;
|
int32 port = 2;
|
||||||
@ -51,4 +95,9 @@ message ShortVideoAddr {
|
|||||||
repeated string host = 10;
|
repeated string host = 10;
|
||||||
string urlArgs = 11;
|
string urlArgs = 11;
|
||||||
//repeated string domain = 13;
|
//repeated string domain = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ShortVideoExtensionReq {
|
||||||
|
int32 subBusiType = 1;
|
||||||
|
int32 userCnt = 2;
|
||||||
}
|
}
|
174
client/ptt.go
174
client/ptt.go
@ -3,19 +3,23 @@ package client
|
|||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
|
||||||
|
|
||||||
"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/client/pb/cmd0x346"
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||||
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 语音相关处理逻辑
|
func init() {
|
||||||
|
decoders["PttCenterSvr.ShortVideoDownReq"] = decodePttShortVideoDownResponse
|
||||||
|
decoders["PttCenterSvr.GroupShortVideoUpReq"] = decodeGroupShortVideoUploadResponse
|
||||||
|
}
|
||||||
|
|
||||||
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
||||||
func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*message.GroupVoiceElement, error) {
|
func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*message.GroupVoiceElement, error) {
|
||||||
@ -24,7 +28,7 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag
|
|||||||
fh := h.Sum(nil)
|
fh := h.Sum(nil)
|
||||||
_, _ = voice.Seek(0, io.SeekStart)
|
_, _ = voice.Seek(0, io.SeekStart)
|
||||||
ext := c.buildGroupPttStoreBDHExt(groupCode, fh[:], int32(length), 0, int32(length))
|
ext := c.buildGroupPttStoreBDHExt(groupCode, fh[:], int32(length), 0, int32(length))
|
||||||
rsp, err := c.highwayUploadByBDH(voice, 29, c.highwaySession.SigSession, ext)
|
rsp, err := c.highwayUploadByBDH(voice, 29, c.highwaySession.SigSession, ext, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -38,29 +42,6 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag
|
|||||||
if len(pkt.MsgTryUpPttRsp) == 0 {
|
if len(pkt.MsgTryUpPttRsp) == 0 {
|
||||||
return nil, errors.New("miss try up rsp")
|
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 {
|
|
||||||
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{
|
return &message.GroupVoiceElement{
|
||||||
Ptt: &msg.Ptt{
|
Ptt: &msg.Ptt{
|
||||||
FileType: proto.Int32(4),
|
FileType: proto.Int32(4),
|
||||||
@ -107,6 +88,52 @@ ok:
|
|||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) UploadGroupShortVideo(groupCode int64, video, thumb io.ReadSeeker) (*message.ShortVideoElement, error) {
|
||||||
|
videoHash, videoLen := utils.GetMd5AndLength(video)
|
||||||
|
thumbHash, thumbLen := utils.GetMd5AndLength(thumb)
|
||||||
|
i, err := c.sendAndWait(c.buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash, groupCode, videoLen, thumbLen))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "upload req error")
|
||||||
|
}
|
||||||
|
rsp := i.(*pttcenter.ShortVideoUploadRsp)
|
||||||
|
if rsp.FileExists == 1 {
|
||||||
|
return &message.ShortVideoElement{
|
||||||
|
Uuid: []byte(rsp.FileId),
|
||||||
|
Size: int32(videoLen),
|
||||||
|
ThumbSize: int32(thumbLen),
|
||||||
|
Md5: videoHash,
|
||||||
|
ThumbMd5: thumbHash,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
ext, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, groupCode, videoLen, thumbLen).PttShortVideoUploadReq)
|
||||||
|
hwRsp, err := c.highwayUploadByBDH(utils.MultiReadSeeker(thumb, video), 25, c.highwaySession.SigSession, ext, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "upload video file error")
|
||||||
|
}
|
||||||
|
if len(hwRsp) == 0 {
|
||||||
|
return nil, errors.New("resp is empty")
|
||||||
|
}
|
||||||
|
rsp = &pttcenter.ShortVideoUploadRsp{}
|
||||||
|
if err = proto.Unmarshal(hwRsp, rsp); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "decode error")
|
||||||
|
}
|
||||||
|
return &message.ShortVideoElement{
|
||||||
|
Uuid: []byte(rsp.FileId),
|
||||||
|
Size: int32(videoLen),
|
||||||
|
ThumbSize: int32(thumbLen),
|
||||||
|
Md5: videoHash,
|
||||||
|
ThumbMd5: thumbHash,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) GetShortVideoUrl(uuid, md5 []byte) string {
|
||||||
|
i, err := c.sendAndWait(c.buildPttShortVideoDownReqPacket(uuid, md5))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return i.(string)
|
||||||
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
@ -142,6 +169,72 @@ func (c *QQClient) buildGroupPttStoreBDHExt(groupCode int64, md5 []byte, size, c
|
|||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.ShortVideoDownReq
|
||||||
|
func (c *QQClient) buildPttShortVideoDownReqPacket(uuid, md5 []byte) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
body := &pttcenter.ShortVideoReqBody{
|
||||||
|
Cmd: 400,
|
||||||
|
Seq: int32(seq),
|
||||||
|
PttShortVideoDownloadReq: &pttcenter.ShortVideoDownloadReq{
|
||||||
|
FromUin: c.Uin,
|
||||||
|
ToUin: c.Uin,
|
||||||
|
ChatType: 1,
|
||||||
|
ClientType: 7,
|
||||||
|
FileId: string(uuid),
|
||||||
|
GroupCode: 1,
|
||||||
|
FileMd5: md5,
|
||||||
|
BusinessType: 1,
|
||||||
|
FileType: 2,
|
||||||
|
DownType: 2,
|
||||||
|
SceneType: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(body)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.ShortVideoDownReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) *pttcenter.ShortVideoReqBody {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
return &pttcenter.ShortVideoReqBody{
|
||||||
|
Cmd: 300,
|
||||||
|
Seq: int32(seq),
|
||||||
|
PttShortVideoUploadReq: &pttcenter.ShortVideoUploadReq{
|
||||||
|
FromUin: c.Uin,
|
||||||
|
ToUin: toUin,
|
||||||
|
ChatType: 1,
|
||||||
|
ClientType: 2,
|
||||||
|
Info: &pttcenter.ShortVideoFileInfo{
|
||||||
|
FileName: hex.EncodeToString(videoHash) + ".mp4",
|
||||||
|
FileMd5: videoHash,
|
||||||
|
ThumbFileMd5: thumbHash,
|
||||||
|
FileSize: videoSize,
|
||||||
|
FileResLength: 1280,
|
||||||
|
FileResWidth: 720,
|
||||||
|
FileFormat: 3,
|
||||||
|
FileTime: 120,
|
||||||
|
ThumbFileSize: thumbSize,
|
||||||
|
},
|
||||||
|
GroupCode: toUin,
|
||||||
|
SupportLargeSize: 1,
|
||||||
|
},
|
||||||
|
ExtensionReq: []*pttcenter.ShortVideoExtensionReq{
|
||||||
|
{
|
||||||
|
SubBusiType: 0,
|
||||||
|
UserCnt: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.GroupShortVideoUpReq
|
||||||
|
func (c *QQClient) buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
payload, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, toUin, videoSize, thumbSize))
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.GroupShortVideoUpReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
// PttStore.GroupPttUp
|
// PttStore.GroupPttUp
|
||||||
func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
pkt := pb.D388RespBody{}
|
pkt := pb.D388RespBody{}
|
||||||
@ -230,3 +323,30 @@ func decodePrivatePttStoreResponse(c *QQClient, _ uint16, payload []byte) (inter
|
|||||||
FileKey: rsp.ApplyUploadRsp.Uuid,
|
FileKey: rsp.ApplyUploadRsp.Uuid,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.ShortVideoDownReq
|
||||||
|
func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
rsp := pttcenter.ShortVideoRspBody{}
|
||||||
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||||
|
}
|
||||||
|
if rsp.PttShortVideoDownloadRsp == nil || rsp.PttShortVideoDownloadRsp.DownloadAddr == nil {
|
||||||
|
return nil, errors.New("resp error")
|
||||||
|
}
|
||||||
|
return rsp.PttShortVideoDownloadRsp.DownloadAddr.Host[0] + rsp.PttShortVideoDownloadRsp.DownloadAddr.UrlArgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PttCenterSvr.GroupShortVideoUpReq
|
||||||
|
func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||||
|
rsp := pttcenter.ShortVideoRspBody{}
|
||||||
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||||
|
}
|
||||||
|
if rsp.PttShortVideoUploadRsp == nil {
|
||||||
|
return nil, errors.New("resp error")
|
||||||
|
}
|
||||||
|
if rsp.PttShortVideoUploadRsp.RetCode != 0 {
|
||||||
|
return nil, errors.Errorf("ret code error: %v", rsp.PttShortVideoUploadRsp.RetCode)
|
||||||
|
}
|
||||||
|
return rsp.PttShortVideoUploadRsp, nil
|
||||||
|
}
|
||||||
|
@ -88,11 +88,13 @@ type ReplyElement struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ShortVideoElement struct {
|
type ShortVideoElement struct {
|
||||||
Name string
|
Name string
|
||||||
Uuid []byte
|
Uuid []byte
|
||||||
Size int32
|
Size int32
|
||||||
Md5 []byte
|
ThumbSize int32
|
||||||
Url string
|
Md5 []byte
|
||||||
|
ThumbMd5 []byte
|
||||||
|
Url string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceElement struct {
|
type ServiceElement struct {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
@ -256,3 +257,32 @@ func (e *GroupShowPicElement) Pack() (r []*msg.Elem) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ShortVideoElement) Pack() (r []*msg.Elem) {
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
Text: &msg.Text{
|
||||||
|
Str: proto.String("你的QQ暂不支持查看视频短片,请期待后续版本。"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
VideoFile: &msg.VideoFile{
|
||||||
|
FileUuid: e.Uuid,
|
||||||
|
FileMd5: e.Md5,
|
||||||
|
FileName: []byte(hex.EncodeToString(e.Md5) + ".mp4"),
|
||||||
|
FileFormat: proto.Int32(3),
|
||||||
|
FileTime: proto.Int32(10),
|
||||||
|
FileSize: proto.Int32(e.Size),
|
||||||
|
ThumbWidth: proto.Int32(1280),
|
||||||
|
ThumbHeight: proto.Int32(720),
|
||||||
|
ThumbFileMd5: e.ThumbMd5,
|
||||||
|
ThumbFileSize: proto.Int32(e.ThumbSize),
|
||||||
|
BusiType: proto.Int32(0),
|
||||||
|
FromChatType: proto.Int32(-1),
|
||||||
|
ToChatType: proto.Int32(-1),
|
||||||
|
BoolSupportProgressive: proto.Bool(true),
|
||||||
|
FileWidth: proto.Int32(1280),
|
||||||
|
FileHeight: proto.Int32(720),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
41
utils/sys.go
41
utils/sys.go
@ -1,10 +1,18 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type multiReadSeeker struct {
|
||||||
|
readers []io.ReadSeeker
|
||||||
|
multiReader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
func IsChanClosed(ch interface{}) bool {
|
func IsChanClosed(ch interface{}) bool {
|
||||||
if reflect.TypeOf(ch).Kind() != reflect.Chan {
|
if reflect.TypeOf(ch).Kind() != reflect.Chan {
|
||||||
panic("object is not a channel.")
|
panic("object is not a channel.")
|
||||||
@ -15,3 +23,36 @@ func IsChanClosed(ch interface{}) bool {
|
|||||||
ptr += unsafe.Sizeof(uint16(0))
|
ptr += unsafe.Sizeof(uint16(0))
|
||||||
return *(*uint32)(unsafe.Pointer(ptr)) > 0
|
return *(*uint32)(unsafe.Pointer(ptr)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMd5AndLength(r io.Reader) ([]byte, int64) {
|
||||||
|
h := md5.New()
|
||||||
|
length, _ := io.Copy(h, r)
|
||||||
|
fh := h.Sum(nil)
|
||||||
|
return fh[:], length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) Read(p []byte) (int, error) {
|
||||||
|
if r.multiReader == nil {
|
||||||
|
var readers []io.Reader
|
||||||
|
for i := range r.readers {
|
||||||
|
_, _ = r.readers[i].Seek(0, io.SeekStart)
|
||||||
|
readers = append(readers, r.readers[i])
|
||||||
|
}
|
||||||
|
r.multiReader = io.MultiReader(readers...)
|
||||||
|
}
|
||||||
|
return r.multiReader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
if whence != 0 || offset != 0 {
|
||||||
|
return -1, errors.New("unsupported offset")
|
||||||
|
}
|
||||||
|
r.multiReader = nil
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MultiReadSeeker(r ...io.ReadSeeker) io.ReadSeeker {
|
||||||
|
return &multiReadSeeker{
|
||||||
|
readers: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user