mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-06-18 13:35:03 +08:00
client: add FileResource
This commit is contained in:
parent
c92096e7ae
commit
2a1fd324ca
@ -1,10 +1,8 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
@ -171,75 +169,71 @@ func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error {
|
||||
return errors.Wrap(err, "open file error")
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
md5Hash, size := utils.ComputeMd5AndLength(file)
|
||||
_, _ = file.Seek(0, io.SeekStart)
|
||||
sha1H := sha1.New()
|
||||
_, _ = io.Copy(sha1H, file)
|
||||
sha1Hash := sha1H.Sum(nil)
|
||||
_, _ = file.Seek(0, io.SeekStart)
|
||||
i, err := fs.client.sendAndWait(fs.client.buildGroupFileUploadReqPacket(folderId, name, fs.GroupCode, size, md5Hash, sha1Hash))
|
||||
f := &FileResource{
|
||||
FileName: name,
|
||||
Body: file,
|
||||
}
|
||||
f.init()
|
||||
i, err := fs.client.sendAndWait(fs.client.buildGroupFileUploadReqPacket(folderId, fs.GroupCode, f))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "query upload failed")
|
||||
}
|
||||
rsp := i.(*oidb.UploadFileRspBody)
|
||||
if rsp.BoolFileExist.Unwrap() {
|
||||
_, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31())
|
||||
return fs.client.sendPacket(pkt)
|
||||
}
|
||||
if len(rsp.UploadIpLanV4) == 0 {
|
||||
return errors.New("server requires unsupported ftn upload")
|
||||
}
|
||||
ext, _ := proto.Marshal(&exciting.GroupFileUploadExt{
|
||||
Unknown1: proto.Int32(100),
|
||||
Unknown2: proto.Int32(1),
|
||||
Entry: &exciting.GroupFileUploadEntry{
|
||||
BusiBuff: &exciting.ExcitingBusiInfo{
|
||||
BusId: rsp.BusId,
|
||||
SenderUin: proto.Some(fs.client.Uin),
|
||||
ReceiverUin: proto.Some(fs.GroupCode),
|
||||
GroupCode: proto.Some(fs.GroupCode),
|
||||
},
|
||||
FileEntry: &exciting.ExcitingFileEntry{
|
||||
FileSize: proto.Some(size),
|
||||
Md5: md5Hash,
|
||||
Sha1: sha1Hash,
|
||||
FileId: []byte(rsp.FileId.Unwrap()),
|
||||
UploadKey: rsp.CheckKey,
|
||||
},
|
||||
ClientInfo: &exciting.ExcitingClientInfo{
|
||||
ClientType: proto.Int32(2),
|
||||
AppId: proto.String(fmt.Sprint(fs.client.version.AppId)),
|
||||
TerminalType: proto.Int32(2),
|
||||
ClientVer: proto.String("9e9c09dc"),
|
||||
Unknown: proto.Int32(4),
|
||||
},
|
||||
FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: proto.Some(name)},
|
||||
Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{
|
||||
{
|
||||
Url: &exciting.ExcitingUrlInfo{
|
||||
Unknown: proto.Int32(1),
|
||||
Host: proto.Some(rsp.UploadIpLanV4[0]),
|
||||
},
|
||||
Port: rsp.UploadPort,
|
||||
if !rsp.BoolFileExist.Unwrap() {
|
||||
if len(rsp.UploadIpLanV4) == 0 {
|
||||
return errors.New("server requires unsupported ftn upload")
|
||||
}
|
||||
ext, _ := proto.Marshal(&exciting.FileUploadExt{
|
||||
Unknown1: proto.Int32(100),
|
||||
Unknown2: proto.Int32(1),
|
||||
Entry: &exciting.FileUploadEntry{
|
||||
BusiBuff: &exciting.ExcitingBusiInfo{
|
||||
BusId: rsp.BusId,
|
||||
SenderUin: proto.Some(fs.client.Uin),
|
||||
ReceiverUin: proto.Some(fs.GroupCode),
|
||||
GroupCode: proto.Some(fs.GroupCode),
|
||||
},
|
||||
}},
|
||||
},
|
||||
Unknown3: proto.Int32(0),
|
||||
})
|
||||
client := fs.client
|
||||
input := highway.Transaction{
|
||||
CommandID: 71,
|
||||
Body: file,
|
||||
Size: size,
|
||||
Sum: md5Hash,
|
||||
Ticket: fs.client.highwaySession.SigSession,
|
||||
Ext: ext,
|
||||
FileEntry: &exciting.ExcitingFileEntry{
|
||||
FileSize: proto.Some(f.size),
|
||||
Md5: f.md5,
|
||||
Sha1: f.sha1,
|
||||
FileId: []byte(rsp.FileId.Unwrap()),
|
||||
UploadKey: rsp.CheckKey,
|
||||
},
|
||||
ClientInfo: &exciting.ExcitingClientInfo{
|
||||
ClientType: proto.Int32(2),
|
||||
AppId: proto.String(fmt.Sprint(fs.client.version.AppId)),
|
||||
TerminalType: proto.Int32(2),
|
||||
ClientVer: proto.String("9e9c09dc"),
|
||||
Unknown: proto.Int32(4),
|
||||
},
|
||||
FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: proto.Some(name)},
|
||||
Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{
|
||||
{
|
||||
Url: &exciting.ExcitingUrlInfo{
|
||||
Unknown: proto.Int32(1),
|
||||
Host: proto.Some(rsp.UploadIpLanV4[0]),
|
||||
},
|
||||
Port: rsp.UploadPort,
|
||||
},
|
||||
}},
|
||||
},
|
||||
Unknown3: proto.Int32(0),
|
||||
})
|
||||
input := highway.Transaction{
|
||||
CommandID: 71,
|
||||
Body: file,
|
||||
Size: f.size,
|
||||
Sum: f.md5,
|
||||
Ticket: fs.client.highwaySession.SigSession,
|
||||
Ext: ext,
|
||||
}
|
||||
if _, err = fs.client.highwaySession.UploadExciting(input); err != nil {
|
||||
return errors.Wrap(err, "upload failed")
|
||||
}
|
||||
}
|
||||
if _, err = fs.client.highwaySession.UploadExciting(input); err != nil {
|
||||
return errors.Wrap(err, "upload failed")
|
||||
}
|
||||
_, pkt := client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31())
|
||||
return client.sendPacket(pkt)
|
||||
_, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId.Unwrap(), rsp.BusId.Unwrap(), rand.Int31())
|
||||
return fs.client.sendPacket(pkt)
|
||||
}
|
||||
|
||||
func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
|
||||
@ -277,18 +271,18 @@ func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32
|
||||
return i.(string)
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) (uint16, []byte) {
|
||||
func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID string, groupCode int64, file *FileResource) (uint16, []byte) {
|
||||
body := &oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{
|
||||
GroupCode: proto.Some(groupCode),
|
||||
AppId: proto.Int32(3),
|
||||
BusId: proto.Int32(102),
|
||||
Entrance: proto.Int32(5),
|
||||
ParentFolderId: proto.Some(parentFolderID),
|
||||
FileName: proto.Some(fileName),
|
||||
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + fileName),
|
||||
Int64FileSize: proto.Some(fileSize),
|
||||
Sha: sha1,
|
||||
Md5: md5,
|
||||
FileName: proto.Some(file.FileName),
|
||||
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + file.FileName),
|
||||
Int64FileSize: proto.Some(file.size),
|
||||
Sha: file.sha1,
|
||||
Md5: file.md5,
|
||||
SupportMultiUpload: proto.Bool(true),
|
||||
}}
|
||||
payload := c.packOIDBPackageProto(1750, 0, body)
|
||||
|
@ -142,9 +142,8 @@ func (s *Session) UploadExciting(trans Transaction) ([]byte, error) {
|
||||
r := binary.NewReader(body)
|
||||
r.ReadByte()
|
||||
hl := r.ReadInt32()
|
||||
a2 := r.ReadInt32()
|
||||
_ = r.ReadInt32()
|
||||
h := r.ReadBytes(int(hl))
|
||||
r.ReadBytes(int(a2))
|
||||
rspHead := new(pb.RspDataHighwayHead)
|
||||
if err = proto.Unmarshal(h, rspHead); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||
|
@ -221,6 +221,8 @@ type ApplyUploadReqV3 struct {
|
||||
LocalFilepath string `protobuf:"bytes,70,opt"`
|
||||
DangerLevel int32 `protobuf:"varint,80,opt"`
|
||||
TotalSpace int64 `protobuf:"varint,90,opt"`
|
||||
Md5 []byte `protobuf:"bytes,110,opt"`
|
||||
X3Sha []byte `protobuf:"bytes,120,opt"`
|
||||
}
|
||||
|
||||
type ApplyUploadRsp struct {
|
||||
@ -258,23 +260,24 @@ type ApplyUploadRspV2 struct {
|
||||
}
|
||||
|
||||
type ApplyUploadRspV3 struct {
|
||||
RetCode int32 `protobuf:"varint,10,opt"`
|
||||
RetMsg string `protobuf:"bytes,20,opt"`
|
||||
TotalSpace int64 `protobuf:"varint,30,opt"`
|
||||
UsedSpace int64 `protobuf:"varint,40,opt"`
|
||||
UploadedSize int64 `protobuf:"varint,50,opt"`
|
||||
UploadIp string `protobuf:"bytes,60,opt"`
|
||||
UploadDomain string `protobuf:"bytes,70,opt"`
|
||||
UploadPort int32 `protobuf:"varint,80,opt"`
|
||||
Uuid []byte `protobuf:"bytes,90,opt"`
|
||||
UploadKey []byte `protobuf:"bytes,100,opt"`
|
||||
BoolFileExist bool `protobuf:"varint,110,opt"`
|
||||
PackSize int32 `protobuf:"varint,120,opt"`
|
||||
UploadIpList []string `protobuf:"bytes,130,rep"`
|
||||
UploadHttpsPort int32 `protobuf:"varint,140,opt"`
|
||||
UploadHttpsDomain string `protobuf:"bytes,150,opt"`
|
||||
UploadDns string `protobuf:"bytes,160,opt"`
|
||||
UploadLanip string `protobuf:"bytes,170,opt"`
|
||||
RetCode int32 `protobuf:"varint,10,opt"`
|
||||
RetMsg string `protobuf:"bytes,20,opt"`
|
||||
TotalSpace int64 `protobuf:"varint,30,opt"`
|
||||
UsedSpace int64 `protobuf:"varint,40,opt"`
|
||||
UploadedSize int64 `protobuf:"varint,50,opt"`
|
||||
UploadIp string `protobuf:"bytes,60,opt"`
|
||||
UploadDomain string `protobuf:"bytes,70,opt"`
|
||||
UploadPort int32 `protobuf:"varint,80,opt"`
|
||||
Uuid []byte `protobuf:"bytes,90,opt"`
|
||||
UploadKey []byte `protobuf:"bytes,100,opt"`
|
||||
BoolFileExist bool `protobuf:"varint,110,opt"`
|
||||
PackSize int32 `protobuf:"varint,120,opt"`
|
||||
UploadIpList []string `protobuf:"bytes,130,rep"`
|
||||
UploadHttpsPort int32 `protobuf:"varint,140,opt"`
|
||||
UploadHttpsDomain string `protobuf:"bytes,150,opt"`
|
||||
UploadDns string `protobuf:"bytes,160,opt"`
|
||||
UploadLanip string `protobuf:"bytes,170,opt"`
|
||||
MediaPlateformUploadKey []byte `protobuf:"bytes,220,opt"`
|
||||
}
|
||||
|
||||
type DelMessageReq struct {
|
||||
@ -404,30 +407,31 @@ type RenewFileRsp struct {
|
||||
}
|
||||
|
||||
type C346ReqBody struct {
|
||||
Cmd int32 `protobuf:"varint,1,opt"`
|
||||
Seq int32 `protobuf:"varint,2,opt"`
|
||||
RecvListQueryReq *RecvListQueryReq `protobuf:"bytes,3,opt"`
|
||||
SendListQueryReq *SendListQueryReq `protobuf:"bytes,4,opt"`
|
||||
RenewFileReq *RenewFileReq `protobuf:"bytes,5,opt"`
|
||||
RecallFileReq *RecallFileReq `protobuf:"bytes,6,opt"`
|
||||
ApplyUploadReq *ApplyUploadReq `protobuf:"bytes,7,opt"`
|
||||
ApplyUploadHitReq *ApplyUploadHitReq `protobuf:"bytes,8,opt"`
|
||||
ApplyForwardFileReq *ApplyForwardFileReq `protobuf:"bytes,9,opt"`
|
||||
UploadSuccReq *UploadSuccReq `protobuf:"bytes,10,opt"`
|
||||
DeleteFileReq *DeleteFileReq `protobuf:"bytes,11,opt"`
|
||||
DownloadSuccReq *DownloadSuccReq `protobuf:"bytes,12,opt"`
|
||||
ApplyDownloadAbsReq *ApplyDownloadAbsReq `protobuf:"bytes,13,opt"`
|
||||
ApplyDownloadReq *ApplyDownloadReq `protobuf:"bytes,14,opt"`
|
||||
ApplyListDownloadReq *ApplyListDownloadReq `protobuf:"bytes,15,opt"`
|
||||
FileQueryReq *FileQueryReq `protobuf:"bytes,16,opt"`
|
||||
ApplyCopyFromReq *ApplyCopyFromReq `protobuf:"bytes,17,opt"`
|
||||
ApplyUploadReqV2 *ApplyUploadReqV2 `protobuf:"bytes,18,opt"`
|
||||
ApplyUploadReqV3 *ApplyUploadReqV3 `protobuf:"bytes,19,opt"`
|
||||
ApplyUploadHitReqV2 *ApplyUploadHitReqV2 `protobuf:"bytes,20,opt"`
|
||||
ApplyUploadHitReqV3 *ApplyUploadHitReqV3 `protobuf:"bytes,21,opt"`
|
||||
BusinessId int32 `protobuf:"varint,101,opt"`
|
||||
ClientType int32 `protobuf:"varint,102,opt"`
|
||||
ApplyCopyToReq *ApplyCopyToReq `protobuf:"bytes,90000,opt"`
|
||||
Cmd int32 `protobuf:"varint,1,opt"`
|
||||
Seq int32 `protobuf:"varint,2,opt"`
|
||||
RecvListQueryReq *RecvListQueryReq `protobuf:"bytes,3,opt"`
|
||||
SendListQueryReq *SendListQueryReq `protobuf:"bytes,4,opt"`
|
||||
RenewFileReq *RenewFileReq `protobuf:"bytes,5,opt"`
|
||||
RecallFileReq *RecallFileReq `protobuf:"bytes,6,opt"`
|
||||
ApplyUploadReq *ApplyUploadReq `protobuf:"bytes,7,opt"`
|
||||
ApplyUploadHitReq *ApplyUploadHitReq `protobuf:"bytes,8,opt"`
|
||||
ApplyForwardFileReq *ApplyForwardFileReq `protobuf:"bytes,9,opt"`
|
||||
UploadSuccReq *UploadSuccReq `protobuf:"bytes,10,opt"`
|
||||
DeleteFileReq *DeleteFileReq `protobuf:"bytes,11,opt"`
|
||||
DownloadSuccReq *DownloadSuccReq `protobuf:"bytes,12,opt"`
|
||||
ApplyDownloadAbsReq *ApplyDownloadAbsReq `protobuf:"bytes,13,opt"`
|
||||
ApplyDownloadReq *ApplyDownloadReq `protobuf:"bytes,14,opt"`
|
||||
ApplyListDownloadReq *ApplyListDownloadReq `protobuf:"bytes,15,opt"`
|
||||
FileQueryReq *FileQueryReq `protobuf:"bytes,16,opt"`
|
||||
ApplyCopyFromReq *ApplyCopyFromReq `protobuf:"bytes,17,opt"`
|
||||
ApplyUploadReqV2 *ApplyUploadReqV2 `protobuf:"bytes,18,opt"`
|
||||
ApplyUploadReqV3 *ApplyUploadReqV3 `protobuf:"bytes,19,opt"`
|
||||
ApplyUploadHitReqV2 *ApplyUploadHitReqV2 `protobuf:"bytes,20,opt"`
|
||||
ApplyUploadHitReqV3 *ApplyUploadHitReqV3 `protobuf:"bytes,21,opt"`
|
||||
BusinessId int32 `protobuf:"varint,101,opt"`
|
||||
ClientType int32 `protobuf:"varint,102,opt"`
|
||||
FlagSupportMediaplatform uint32 `protobuf:"varint,200,opt"`
|
||||
ApplyCopyToReq *ApplyCopyToReq `protobuf:"bytes,90000,opt"`
|
||||
//ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message
|
||||
ApplyGetTrafficReq *ApplyGetTrafficReq `protobuf:"bytes,90002,opt"`
|
||||
ExtensionReq *ExtensionReq `protobuf:"bytes,99999,opt"`
|
||||
|
@ -197,6 +197,8 @@ message ApplyUploadReqV3 {
|
||||
string localFilepath = 70;
|
||||
int32 dangerLevel = 80;
|
||||
int64 totalSpace = 90;
|
||||
bytes md5 = 110;
|
||||
bytes _3Sha = 120;
|
||||
}
|
||||
message ApplyUploadRsp {
|
||||
int32 retCode = 10;
|
||||
@ -248,6 +250,7 @@ message ApplyUploadRspV3 {
|
||||
string uploadHttpsDomain = 150;
|
||||
string uploadDns = 160;
|
||||
string uploadLanip = 170;
|
||||
bytes mediaPlateformUploadKey = 220;
|
||||
}
|
||||
message DelMessageReq {
|
||||
int64 uinSender = 1;
|
||||
@ -382,6 +385,7 @@ message C346ReqBody {
|
||||
ApplyUploadHitReqV3 applyUploadHitReqV3 = 21;
|
||||
int32 businessId = 101;
|
||||
int32 clientType = 102;
|
||||
uint32 flagSupportMediaplatform = 200;
|
||||
ApplyCopyToReq applyCopyToReq = 90000;
|
||||
//ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message
|
||||
ApplyGetTrafficReq applyGetTrafficReq = 90002;
|
||||
|
@ -7,14 +7,15 @@ import (
|
||||
proto "github.com/RomiChan/protobuf/proto"
|
||||
)
|
||||
|
||||
type GroupFileUploadExt struct {
|
||||
Unknown1 proto.Option[int32] `protobuf:"varint,1,opt"`
|
||||
Unknown2 proto.Option[int32] `protobuf:"varint,2,opt"`
|
||||
Entry *GroupFileUploadEntry `protobuf:"bytes,100,opt"`
|
||||
Unknown3 proto.Option[int32] `protobuf:"varint,3,opt"`
|
||||
type FileUploadExt struct {
|
||||
Unknown1 proto.Option[int32] `protobuf:"varint,1,opt"`
|
||||
Unknown2 proto.Option[int32] `protobuf:"varint,2,opt"`
|
||||
Unknown3 proto.Option[int32] `protobuf:"varint,3,opt"`
|
||||
Entry *FileUploadEntry `protobuf:"bytes,100,opt"`
|
||||
Unknown200 proto.Option[int32] `protobuf:"varint,200,opt"`
|
||||
}
|
||||
|
||||
type GroupFileUploadEntry struct {
|
||||
type FileUploadEntry struct {
|
||||
BusiBuff *ExcitingBusiInfo `protobuf:"bytes,100,opt"`
|
||||
FileEntry *ExcitingFileEntry `protobuf:"bytes,200,opt"`
|
||||
ClientInfo *ExcitingClientInfo `protobuf:"bytes,300,opt"`
|
||||
|
@ -2,14 +2,15 @@ syntax = "proto2";
|
||||
|
||||
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/exciting";
|
||||
|
||||
message GroupFileUploadExt {
|
||||
message FileUploadExt {
|
||||
optional int32 unknown1 = 1;
|
||||
optional int32 unknown2 = 2;
|
||||
optional GroupFileUploadEntry entry = 100;
|
||||
optional int32 unknown3 = 3;
|
||||
optional FileUploadEntry entry = 100;
|
||||
optional int32 unknown200 = 200;
|
||||
}
|
||||
|
||||
message GroupFileUploadEntry {
|
||||
message FileUploadEntry {
|
||||
optional ExcitingBusiInfo busiBuff = 100;
|
||||
optional ExcitingFileEntry fileEntry = 200;
|
||||
optional ExcitingClientInfo clientInfo = 300;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Code generated by protoc-gen-golite. DO NOT EDIT.
|
||||
// source: oidb0xeb7.proto
|
||||
// source: pb/oidb/oidb0xeb7.proto
|
||||
|
||||
package oidb
|
||||
|
||||
|
176
client/upload_file.go
Normal file
176
client/upload_file.go
Normal file
@ -0,0 +1,176 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/client/internal/highway"
|
||||
"github.com/Mrs4s/MiraiGo/client/internal/network"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/exciting"
|
||||
"github.com/Mrs4s/MiraiGo/internal/proto"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
decoders["OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700"] = decodePrivateFileUploadReq
|
||||
}
|
||||
|
||||
type FileResource struct {
|
||||
FileName string
|
||||
Body io.ReadSeeker // FileResource content body
|
||||
size int64
|
||||
md5 []byte
|
||||
sha1 []byte
|
||||
}
|
||||
|
||||
type fileUploadRsp struct {
|
||||
existed bool
|
||||
busid int32
|
||||
uuid []byte
|
||||
uploadKey []byte
|
||||
|
||||
// upload group file need
|
||||
UploadIpLanV4 []string
|
||||
UploadPort int32
|
||||
}
|
||||
|
||||
func (f *FileResource) init() {
|
||||
md5H := md5.New()
|
||||
sha1H := sha1.New()
|
||||
|
||||
whence, _ := f.Body.Seek(0, io.SeekCurrent)
|
||||
f.size, _ = io.Copy(io.MultiWriter(md5H, sha1H), f.Body)
|
||||
_, _ = f.Body.Seek(whence, io.SeekStart) // restore
|
||||
|
||||
// calculate md5&sha1 hash
|
||||
f.md5 = md5H.Sum(nil)
|
||||
f.sha1 = sha1H.Sum(nil)
|
||||
}
|
||||
|
||||
func (c *QQClient) _UploadFile(target message.Source, file *FileResource) error {
|
||||
file.init()
|
||||
// 同文件等待其他线程上传
|
||||
fkey := string(file.sha1)
|
||||
fsWaiter.Wait(fkey)
|
||||
defer fsWaiter.Done(fkey)
|
||||
|
||||
var rsp *fileUploadRsp
|
||||
if target.SourceType == message.SourcePrivate {
|
||||
i, err := c.sendAndWait(c.buildPrivateFileUploadReqPacket(target, file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rsp = i.(*fileUploadRsp)
|
||||
}
|
||||
if !rsp.existed {
|
||||
ext := &exciting.FileUploadExt{
|
||||
Unknown1: proto.Int32(100),
|
||||
Unknown2: proto.Int32(2),
|
||||
Entry: &exciting.FileUploadEntry{
|
||||
BusiBuff: &exciting.ExcitingBusiInfo{
|
||||
BusId: proto.Int32(rsp.busid),
|
||||
SenderUin: proto.Some(c.Uin),
|
||||
ReceiverUin: proto.Some(target.PrimaryID),
|
||||
GroupCode: proto.Int64(0),
|
||||
},
|
||||
FileEntry: &exciting.ExcitingFileEntry{
|
||||
FileSize: proto.Some(file.size),
|
||||
Md5: file.md5,
|
||||
Sha1: file.sha1,
|
||||
FileId: rsp.uuid,
|
||||
UploadKey: rsp.uploadKey,
|
||||
},
|
||||
ClientInfo: &exciting.ExcitingClientInfo{
|
||||
ClientType: proto.Int32(2),
|
||||
AppId: proto.String(fmt.Sprint(c.version.AppId)),
|
||||
TerminalType: proto.Int32(2),
|
||||
ClientVer: proto.String("d92615c5"),
|
||||
Unknown: proto.Int32(4),
|
||||
},
|
||||
FileNameInfo: &exciting.ExcitingFileNameInfo{
|
||||
FileName: proto.Some(file.FileName),
|
||||
},
|
||||
},
|
||||
Unknown200: proto.Int32(1),
|
||||
}
|
||||
if target.SourceType == message.SourceGroup {
|
||||
if len(rsp.UploadIpLanV4) == 0 {
|
||||
return errors.New("server requires unsupported ftn upload")
|
||||
}
|
||||
ext.Unknown3 = proto.Int32(0)
|
||||
ext.Unknown200 = proto.None[int32]()
|
||||
ext.Entry.BusiBuff.GroupCode = proto.Int64(target.PrimaryID)
|
||||
ext.Entry.Host = &exciting.ExcitingHostConfig{
|
||||
Hosts: []*exciting.ExcitingHostInfo{
|
||||
{
|
||||
Url: &exciting.ExcitingUrlInfo{
|
||||
Unknown: proto.Int32(1),
|
||||
Host: proto.Some(rsp.UploadIpLanV4[0]),
|
||||
},
|
||||
Port: proto.Some(rsp.UploadPort),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
extPkt, _ := proto.Marshal(ext)
|
||||
input := highway.Transaction{
|
||||
CommandID: 71,
|
||||
Body: file.Body,
|
||||
Size: file.size,
|
||||
Sum: file.md5,
|
||||
Ticket: c.highwaySession.SigSession,
|
||||
Ext: extPkt,
|
||||
}
|
||||
if target.SourceType == message.SourcePrivate {
|
||||
input.CommandID = 69
|
||||
}
|
||||
if _, err := c.highwaySession.UploadExciting(input); err != nil {
|
||||
return errors.Wrap(err, "upload failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *QQClient) buildPrivateFileUploadReqPacket(target message.Source, file *FileResource) (uint16, []byte) {
|
||||
req := cmd0x346.C346ReqBody{
|
||||
Cmd: 1700,
|
||||
Seq: c.nextFriendSeq(),
|
||||
ApplyUploadReqV3: &cmd0x346.ApplyUploadReqV3{
|
||||
SenderUin: c.Uin,
|
||||
RecverUin: target.PrimaryID,
|
||||
FileSize: file.size,
|
||||
FileName: file.FileName,
|
||||
Bytes_10MMd5: file.md5, // TODO: investigate this
|
||||
Sha: file.sha1,
|
||||
LocalFilepath: "/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/" + file.FileName,
|
||||
Md5: file.md5,
|
||||
},
|
||||
BusinessId: 3,
|
||||
ClientType: 104,
|
||||
FlagSupportMediaplatform: 1,
|
||||
}
|
||||
pkg, _ := proto.Marshal(&req)
|
||||
return c.uniPacket("OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700", pkg)
|
||||
}
|
||||
|
||||
// OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700
|
||||
func decodePrivateFileUploadReq(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) {
|
||||
var rsp cmd0x346.C346RspBody
|
||||
err := proto.Unmarshal(payload, &rsp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v3 := rsp.ApplyUploadRspV3
|
||||
r := &fileUploadRsp{
|
||||
existed: v3.BoolFileExist,
|
||||
busid: 3,
|
||||
uuid: v3.Uuid,
|
||||
uploadKey: v3.MediaPlateformUploadKey,
|
||||
}
|
||||
return r, nil
|
||||
}
|
@ -38,6 +38,10 @@ func Some[T any](val T) proto.Option[T] {
|
||||
return proto.Some(val)
|
||||
}
|
||||
|
||||
func None[T any]() proto.Option[T] {
|
||||
return proto.None[T]()
|
||||
}
|
||||
|
||||
// Bool stores v in a new bool value and returns a pointer to it.
|
||||
func Bool(v bool) proto.Option[bool] { return proto.Some(v) }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user