Files
wechat_ipad_pro/clientsdk/cdnrequest.go
2026-02-17 13:06:23 +08:00

1203 lines
39 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package clientsdk
import (
"encoding/hex"
"errors"
"fmt"
"log"
"math/rand"
"strconv"
"strings"
"time"
"xiawan/wx/api/req"
"xiawan/wx/clientsdk/mmtls"
"github.com/gogo/protobuf/proto"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/protobuf/wechat"
)
// ---------------------创建请求---------------------------------------------------------------
// CreateCDNDownloadRequest 创建Cdn资源下载请求
func CreateCDNDownloadRequest(userInfo *baseinfo.UserInfo, dnsInfo *wechat.CDNDnsInfo, aesKeyBytes []byte, imageURL string, fileType uint32) *baseinfo.CdnImageDownloadRequest {
request := &baseinfo.CdnImageDownloadRequest{}
request.Ver = 1
request.WeiXinNum = dnsInfo.GetUin()
request.Seq = 1
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
if userInfo.DeviceInfo == nil {
request.ClientOsType = baseinfo.AndroidDeviceType
} else {
request.ClientOsType = userInfo.DeviceInfo.OsType
}
// authkey
authKey := dnsInfo.GetAuthKey().GetBuffer()
request.AuthKey = authKey
request.NetType = 1
request.AcceptDupack = 1
request.RsaVer = 1
rsaBytes, _ := baseutils.CdnRsaEncrypt(aesKeyBytes)
request.RsaValue = rsaBytes
request.FileType = fileType
request.WxChatType = 0
request.FileID = imageURL
request.LastRetCode = 0
request.IPSeq = 0
request.CliQuicFlag = 0
request.WxMsgFlag = nil
request.WxAutoStart = 1
request.DownPicFormat = 1
request.Offset = 0
request.LargesVideo = 0
request.SourceFlag = 0
return request
}
// CreateVideoUploadRequest 创建视频上传请求
func CreateVideoUploadRequest(userInfo *baseinfo.UserInfo, videoItem *baseinfo.UploadVideoItem) (*baseinfo.CdnVideoUploadRequest, error) {
request := &baseinfo.CdnVideoUploadRequest{}
request.Ver = 1
request.WeiXinNum = videoItem.CDNDns.GetUin()
request.Seq = videoItem.Seq
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
if userInfo.DeviceInfo == nil {
request.ClientOSType = baseinfo.AndroidDeviceType
} else {
request.ClientOSType = userInfo.DeviceInfo.OsType
}
request.AutoKey = videoItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDuPack = 1
request.SafeProto = 1
request.FileType = 4
request.WeChatType = 0
request.LastRetCode = 0
request.IpSeq = 0
request.HastHumb = 1
// ToUser
retBytes := baseutils.AesEncrypt([]byte(videoItem.ToUser), []byte("wxusrname2016cdn"))
request.ToUSerName = "@cdn2_" + baseutils.BytesToHexString(retBytes, false)
request.CompressType = 0
request.NoCheckAesKey = 1
request.EnaBleHit = 1
request.ExistAnceCheck = 0
request.AppType = 0
// 加密视频
videoEncodeData := baseutils.AesEncryptECB(videoItem.VideoData, videoItem.AesKey)
// 加密微缩图
thumbEncodeData := baseutils.AesEncryptECB(videoItem.ThumbData, videoItem.AesKey)
userMd5 := baseutils.Md5ValueByte([]byte(userInfo.GetUserName()+"-"+videoItem.ToUser), false)
timeStamp := fmt.Sprintf("%d", time.Now().UnixNano()/1000/1000)
videoKey := timeStamp + "baed6285091"
fileKey := fmt.Sprintf("aupvideo_%s_%s_%s", userMd5, timeStamp, videoKey) // guid
request.FileKey = fileKey
request.LocalName = baseutils.Md5ValueByte(videoItem.VideoData, false) + ".jpg"
request.Offset = 0
request.LargesVideo = 1
request.SourceFlag = 0
request.DropRateFlag = 1
request.ClientRsaVer = 1
//rsaValue, _ := baseutils.RsaEncryptByVer(videoItem.AesKey, userInfo.GetLoginRsaVer())
rsaValue, _ := baseutils.CdnRsaEncrypt(videoItem.AesKey)
request.ClientRsaVal = rsaValue
request.AdVideoFlag = 0
request.FileMd5 = baseutils.Md5ValueByte(videoItem.VideoData, false)
request.RawFileMd5 = baseutils.Md5ValueByte(videoItem.VideoData, false)
request.DataCheckSum = baseutils.Adler32(0, videoEncodeData)
request.EncThumbCrc = baseutils.Adler32(0, thumbEncodeData)
request.ThumbData = thumbEncodeData
request.RawThumbMd5 = baseutils.Md5ValueByte(videoItem.ThumbData, false)
request.TotalSize = uint32(len(videoEncodeData))
request.RawTotalSize = uint32(len(videoItem.VideoData))
request.ThumbTotalSize = uint32(len(thumbEncodeData))
request.RawThumbSize = uint32(len(videoItem.ThumbData))
request.FileCrc = baseutils.Adler32(0, videoItem.VideoData)
request.FileData = videoEncodeData
request.Mp4identify = "a79c98ca478c707db3c80d28766f89e0"
return request, nil
}
// CreateImageUploadRequest 创建上传高清图请求
func CreateImageUploadRequest(userInfo *baseinfo.UserInfo, imgItem *baseinfo.UploadImgItem) (*baseinfo.CdnImageUploadRequest, error) {
request := &baseinfo.CdnImageUploadRequest{}
request.Ver = 1
request.WeiXinNum = uint32(imgItem.CDNDns.GetUin())
request.Seq = imgItem.Seq
if !strings.HasPrefix(userInfo.LoginDataInfo.LoginData, "A") && userInfo.DeviceInfo != nil {
request.ClientOsType = userInfo.DeviceInfo.OsType
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
} else {
request.ClientOsType = baseinfo.AndroidDeviceType
request.ClientVersion = baseinfo.AndroidClientVersion
}
request.AuthKey = imgItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDupack = 1
request.SafeProto = 1
request.FileType = 2
request.WxChatType = 0
request.LastRetCode = 0
request.IPSeq = 0
request.CliQuicFlag = 0
request.CompressType = 0
request.NoCheckAesKey = 1
request.EnableHit = 1
request.ExistAnceCheck = 0
request.AppType = 1
request.LocalName = imgItem.LocalName + imgItem.ExtName
request.Offset = 0
request.LargesVideo = 0
request.SourceFlag = 0
request.AdVideoFlag = 0
// ToUser
retBytes := baseutils.AesEncrypt([]byte(imgItem.ToUser), []byte("wxusrname2016cdn"))
request.ToUser = "@cdn2_" + baseutils.BytesToHexString(retBytes, false)
// ThumbData
thumbItem := CreateThumbImage(imgItem.ImageData)
if thumbItem == nil {
return nil, errors.New("生成缩略图失败")
}
thumbEncodeData := baseutils.AesEncryptECB(thumbItem.Data, []byte(imgItem.AesKey))
request.HasThumb = 1
request.ThumbTotalSize = uint32(len(thumbEncodeData))
request.RawThumbSize = uint32(len(thumbItem.Data))
request.RawThumbMD5 = baseutils.Md5ValueByte(thumbItem.Data, false)
request.EncThumbCRC = baseutils.Adler32(0, thumbEncodeData)
request.ThumbData = thumbEncodeData
// FileKey
request.FileKey = "wxupload_" + imgItem.ToUser + imgItem.LocalName + "_" + strconv.Itoa(int(imgItem.CreateTime))
// FileData
fileEncodeData := baseutils.AesEncryptECB(imgItem.ImageData, []byte(imgItem.AesKey))
request.TotalSize = uint32(len(fileEncodeData))
request.RawTotalSize = uint32(len(imgItem.ImageData))
request.FileMD5 = baseutils.Md5ValueByte(fileEncodeData, false)
request.RawFileMD5 = baseutils.Md5ValueByte(imgItem.ImageData, false)
request.FileCRC = baseutils.Adler32(0, imgItem.ImageData)
request.DataCheckSum = baseutils.Adler32(0, fileEncodeData)
request.FileData = fileEncodeData
request.SetOfPicFormat = "011000"
// prepareRequestItem
var prepareRequestItem baseinfo.CDNUploadMsgImgPrepareRequestItem
prepareRequestItem.ToUser = imgItem.ToUser
prepareRequestItem.LocalName = imgItem.LocalName
prepareRequestItem.CreateTime = imgItem.CreateTime
prepareRequestItem.ThumbWidth = thumbItem.Width
prepareRequestItem.ThumbHeight = thumbItem.Height
prepareRequestItem.AesKey = imgItem.AesKey
prepareRequestItem.Crc32 = request.FileCRC
// SessionBuf
request.SessionBuf = CreateCDNUploadMsgImgPrepareRequest(userInfo, &prepareRequestItem)
return request, nil
}
// CreateSnsImageUploadRequest 创建Cdn上传朋友圈高清图请求
func CreateSnsImageUploadRequest(userInfo *baseinfo.UserInfo, snsImgItem *baseinfo.SnsUploadImgItem) (*baseinfo.CdnSnsImageUploadRequest, error) {
request := &baseinfo.CdnSnsImageUploadRequest{}
request.Ver = 1
request.WeiXinNum = uint32(snsImgItem.CDNDns.GetUin())
request.Seq = snsImgItem.Seq
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
if userInfo.DeviceInfo == nil {
request.ClientOsType = baseinfo.AndroidDeviceType
} else {
request.ClientOsType = userInfo.DeviceInfo.OsType
}
request.AuthKey = snsImgItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDupack = 1
request.RsaVer = 1
rsaValue, _ := baseutils.CdnRsaEncrypt(snsImgItem.AesKey)
request.RsaValue = rsaValue
request.FileType = 20201
request.WxChatType = 0
request.LastRetCode = 0
request.IPSeq = 0
request.CliQuicFlag = 0
request.HasThumb = 0
request.ToUser = ""
request.CompressType = 1
request.NoCheckAesKey = 1
request.EnableHit = 1
request.ExistAnceCheck = 0
request.AppType = 108
request.LocalName = "[TEMP]" + strconv.Itoa(int(snsImgItem.ImageID)) + "_" + strconv.Itoa(int(snsImgItem.CreateTime))
request.FileKey = request.LocalName + "_" + strconv.Itoa(int(snsImgItem.CreateTime)+rand.Intn(1000))
request.Offset = 0
request.ThumbTotalSize = 0
request.RawThumbSize = 0
request.RawThumbMD5 = ""
request.ThumbCRC = 0
request.LargesVideo = 0
request.SourceFlag = 0
request.AdVideoFlag = 0
// FileData
fileEncodeData := baseutils.AesEncryptECB(snsImgItem.ImageData, []byte(snsImgItem.AesKey))
request.TotalSize = uint32(len(snsImgItem.ImageData))
request.RawTotalSize = request.TotalSize
request.FileMD5 = baseutils.Md5ValueByte(snsImgItem.ImageData, false)
request.RawFileMD5 = request.FileMD5
request.FileCRC = baseutils.Adler32(0, snsImgItem.ImageData)
request.DataCheckSum = baseutils.Adler32(0, fileEncodeData)
request.FileData = snsImgItem.ImageData
return request, nil
}
// CreateSnsVideoDownloadRequest 创建Cdn下载朋友圈视频请求
func CreateSnsVideoDownloadRequest(userInfo *baseinfo.UserInfo, snsVideoItem *baseinfo.SnsVideoDownloadItem) (*baseinfo.CdnSnsVideoDownloadRequest, error) {
request := &baseinfo.CdnSnsVideoDownloadRequest{}
request.Ver = 1
request.WeiXinNum = uint32(snsVideoItem.CDNDns.GetUin())
request.Seq = snsVideoItem.Seq
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
if userInfo.DeviceInfo == nil {
request.ClientOsType = baseinfo.AndroidDeviceType
} else {
request.ClientOsType = userInfo.DeviceInfo.OsType
}
request.AuthKey = snsVideoItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDupack = 1
request.Signal = ""
request.Scene = ""
request.URL = snsVideoItem.URL
request.RangeStart = snsVideoItem.RangeStart
request.RangeEnd = snsVideoItem.RangeEnd
request.LastRetCode = 0
request.IPSeq = 0
request.RedirectType = 0
request.LastVideoFormat = 0
request.VideoFormat = 2
request.XSnsVideoFlag = snsVideoItem.XSnsVideoFlag
return request, nil
}
// CreateCdnSnsVideoUploadRequest 上传朋友圈视频
func CreateCdnSnsVideoUploadRequest(userInfo *baseinfo.UserInfo, videoUploadItem *baseinfo.SnsVideoUploadItem) (*baseinfo.CdnSnsVideoUploadRequest, error) {
request := &baseinfo.CdnSnsVideoUploadRequest{}
filekey := fmt.Sprintf("aupimg_%s_%d", userInfo.GetUserName(), time.Now().UnixNano()/1000/1000)
request.Ver = 1
request.WeiXinNum = uint32(videoUploadItem.CDNDns.GetUin())
request.Seq = videoUploadItem.Seq
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
if userInfo.DeviceInfo == nil {
request.ClientOsType = baseinfo.AndroidDeviceType
} else {
request.ClientOsType = userInfo.DeviceInfo.OsType
}
request.AuthKey = videoUploadItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDupack = 1
request.RsaVer = 1
fileAESKey := "42807213a18c421db34bad77219f5172"
bFileAESKey, _ := hex.DecodeString(fileAESKey)
rsaValue, _ := baseutils.CdnRsaEncrypt(bFileAESKey)
request.RsaValue = rsaValue
request.FileType = 20202 //20202
request.WxChatType = 0
request.LastRetCode = 0
request.IPSeq = 0
request.CliQuicFlag = 0
request.IsStoreVideo = 0
request.NoCheckAesKey = 1
request.EnableHit = 1
request.ExistAnceCheck = 0
request.AppType = 102
totalSize := uint32(len(videoUploadItem.VideoData))
request.TotalSize = totalSize
request.RawTotalSize = totalSize
// tmpLocalNameNoExt := "[TEMP]" + strconv.Itoa(int(videoUploadItem.VideoID)) + "_" + strconv.Itoa(int(videoUploadItem.CreateTime))
request.LocalName = "sns_tmps_Locall_path4"
request.FileKey = filekey
request.Offset = 0
// 暂时不设置Thumb数据
thumbDataLen := uint32(len(videoUploadItem.ThumbData))
request.HasThumb = 1
request.ThumbTotalSize = thumbDataLen
request.RawThumbSize = thumbDataLen
request.RawThumbMD5 = baseutils.Md5ValueByte(videoUploadItem.ThumbData, false)
request.ThumbCRC = baseutils.Adler32(0, videoUploadItem.ThumbData)
request.ThumbData = videoUploadItem.ThumbData
request.LargesVideo = 80
request.SourceFlag = 0
request.AdVideoFlag = 0
fileEncodeData := baseutils.AesEncryptECB(videoUploadItem.VideoData, videoUploadItem.AesKey)
request.Mp4Identify = baseutils.Md5ValueByte(fileEncodeData, false)
md5Value := baseutils.Md5ValueByte(videoUploadItem.VideoData, false)
request.FileMD5 = md5Value
request.RawFileMD5 = md5Value
request.FileCRC = baseutils.Adler32(0, videoUploadItem.VideoData)
request.DataCheckSum = baseutils.Adler32(0, fileEncodeData)
request.FileData = videoUploadItem.VideoData
//request.UserLargeFileApi=true
return request, nil
}
// ---------------------发送请求---------------------------------------------------------------
// SendCdnDownloadReuqest 发送CDN下载请求
func SendCdnDownloadReuqest(userInfo *baseinfo.UserInfo, downItem *baseinfo.DownMediaItem) (*baseinfo.CdnDownloadResponse, error) {
dnsInfo := userInfo.DNSInfo
aesKeyBytes := baseutils.HexStringToBytes(downItem.AesKey)
request := CreateCDNDownloadRequest(userInfo, dnsInfo, aesKeyBytes, downItem.FileURL, downItem.FileType)
sendData := PackCdnImageDownloadRequest(request)
// 连接Cdn服务器
serverIP := dnsInfo.FrontIplist[0].GetStr()
serverPort := dnsInfo.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, err
}
defer conn.Close()
// 发送请求数据
conn.Write(sendData)
retData := CDNRecvData(conn)
// 返回解密图片数据
response, err := DecodeImageDownloadResponse(retData)
if err != nil {
return nil, err
}
response.FileData = baseutils.AesDecryptECB(response.FileData, aesKeyBytes)
return response, nil
}
// 获取图片
func GetMsgBigImg(userInfo *baseinfo.UserInfo, m req.DownloadParam) (*baseinfo.PackHeader, error) {
if m.Section.DataLen > 65535 {
m.Section.DataLen = 65535
}
reqData := wechat.GetMsgImgRequest{
BaseRequest: GetBaseRequest(userInfo),
MsgId: proto.Uint32(m.MsgId),
FromUserName: &wechat.SKBuiltinString{
Str: proto.String(m.FromUserName),
},
ToUserName: &wechat.SKBuiltinString{
Str: proto.String(m.ToUserName),
//Str: proto.String(userInfo.WxId), // 别人发送给 "我" 的图片数据
},
TotalLen: proto.Uint32(uint32(m.TotalLen)),
StartPos: proto.Uint32(m.Section.StartPos),
DataLen: proto.Uint32(m.Section.DataLen),
CompressType: proto.Uint32(uint32(m.CompressType)),
}
// 打包发送数据
srcData, _ := proto.Marshal(&reqData)
sendEncodeData := Pack(userInfo, srcData, 109, 5)
resp, err := mmtls.MMHTTPPostData(userInfo.MMInfo, "/cgi-bin/micromsg-bin/getmsgimg", sendEncodeData)
if err != nil {
return nil, err
}
return DecodePackHeader(resp, nil)
}
// GetMsgCdnVideo 获取视频(下载视频数据)
func GetMsgCdnVideo(userInfo *baseinfo.UserInfo, m req.DownloadParam) (*baseinfo.PackHeader, error) {
req := wechat.DownloadVideoRequest{
BaseRequest: GetBaseRequest(userInfo),
MsgId: proto.Uint32(m.MsgId),
TotalLen: proto.Uint32(uint32(m.TotalLen)),
StartPos: proto.Uint32(m.Section.StartPos),
NetworkEnv: proto.Uint32(1),
MxPackSize: proto.Uint32(m.Section.DataLen),
}
// 打包发送数据
srcData, _ := proto.Marshal(&req)
sendEncodeData := Pack(userInfo, srcData, baseinfo.MMRequestTypeForwardCdnVideo, 5)
resp, err := mmtls.MMHTTPPostData(userInfo.MMInfo, "/cgi-bin/micromsg-bin/downloadvideo", sendEncodeData)
if err != nil {
return nil, err
}
return DecodePackHeader(resp, nil)
}
func SendCdnUploadVideoRequest(userInfo *baseinfo.UserInfo, toUser string, imgData []byte, videoData []byte) (*baseinfo.CdnMsgVideoUploadResponse, error) {
videoItem := &baseinfo.UploadVideoItem{}
videoItem.ThumbData = imgData
videoItem.VideoData = videoData
videoItem.ToUser = toUser
videoItem.CDNDns = userInfo.DNSInfo
videoItem.Seq = uint32(rand.Intn(10))
videoItem.AesKey, _ = hex.DecodeString("1f22e78fd07d46a68b889aa222e93563")
request, err := CreateVideoUploadRequest(userInfo, videoItem)
if err != nil {
return nil, err
}
sendData := PackCdnVideoUploadRequest(request)
// 连接Cdn服务器
serverIP := videoItem.CDNDns.FrontIplist[0].GetStr()
serverPort := videoItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeVideoUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, errors.New("上传视频失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.FileID != "" {
response.FileAesKey = "1f22e78fd07d46a68b889aa222e93563"
response.ThumbDataSize = uint32(len(videoItem.ThumbData))
response.VideoDataSize = uint32(len(videoItem.VideoData))
response.Mp4identify = "a79c98ca478c707db3c80d28766f89e0"
response.VideoDataMD5 = request.RawFileMd5
response.ThumbWidth = 200
response.ThumbHeight = 200
return response, nil
}
// 设置请求数据
//response.ReqData = request
}
}
// SendCdnUploadImageReuqest 发送CDN上传图片请求
func SendCdnUploadImageReuqest(userInfo *baseinfo.UserInfo, toUser string, imgData []byte) (bool, error) {
imgItem := &baseinfo.UploadImgItem{}
imgItem.ToUser = toUser
imgItem.Seq = uint32(rand.Intn(10))
imgItem.LocalName = strconv.Itoa(rand.Intn(100))
imgItem.ExtName = ".pic"
// 随机生成AesKey
imgItem.AesKey = []byte(baseutils.RandomStringByLength(16))
imgItem.ImageData = imgData
imgItem.CreateTime = uint32(time.Now().UnixNano() / 1000000000)
// 设置Dns路由信息
imgItem.CDNDns = userInfo.FAKEDnsInfo
// 创建上传图片请求
request, err := CreateImageUploadRequest(userInfo, imgItem)
if err != nil {
return false, err
}
// 打包请求
sendData := PackCdnImageUploadRequest(request)
// 连接Cdn服务器
serverIP := imgItem.CDNDns.FrontIplist[0].GetStr()
serverPort := imgItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return false, err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
sendImgLen := uint32(len(request.FileData))
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeImageUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return false, err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return false, errors.New("发送图片失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.RecvLen < sendImgLen {
continue
}
// SKeyResp CDNUploadMsgImgPrepareResponse的错误码
if response.SKeyResp != 0 {
return false, errors.New("发送图片失败: SKeyResp = " + strconv.Itoa(int(response.SKeyResp)))
}
// SKeyBuf
skbufLen := len(response.SKeyBuf)
if skbufLen <= 0 {
continue
}
break
}
return true, nil
}
// SendCdnUploadImageReuqestWithDetails 发送CDN上传图片请求并返回详细信息
func SendCdnUploadImageReuqestWithDetails(userInfo *baseinfo.UserInfo, toUser string, imgData []byte) (*baseinfo.CdnImageUploadResponse, string, error) {
imgItem := &baseinfo.UploadImgItem{}
imgItem.ToUser = toUser
imgItem.Seq = uint32(rand.Intn(10))
imgItem.LocalName = strconv.Itoa(rand.Intn(100))
imgItem.ExtName = ".pic"
// 随机生成AesKey
imgItem.AesKey = []byte(baseutils.RandomStringByLength(16))
aesKeyHex := hex.EncodeToString(imgItem.AesKey)
imgItem.ImageData = imgData
imgItem.CreateTime = uint32(time.Now().UnixNano() / 1000000000)
// 设置Dns路由信息
imgItem.CDNDns = userInfo.FAKEDnsInfo
// 创建上传图片请求
request, err := CreateImageUploadRequest(userInfo, imgItem)
if err != nil {
return nil, "", err
}
// 打包请求
sendData := PackCdnImageUploadRequest(request)
// 连接Cdn服务器
serverIP := imgItem.CDNDns.FrontIplist[0].GetStr()
serverPort := imgItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, "", err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
sendImgLen := uint32(len(request.FileData))
var finalResponse *baseinfo.CdnImageUploadResponse
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeImageUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, "", err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, "", errors.New("发送图片失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.RecvLen < sendImgLen {
continue
}
// SKeyResp CDNUploadMsgImgPrepareResponse的错误码
if response.SKeyResp != 0 {
return nil, "", errors.New("发送图片失败: SKeyResp = " + strconv.Itoa(int(response.SKeyResp)))
}
// SKeyBuf
skbufLen := len(response.SKeyBuf)
if skbufLen <= 0 {
continue
}
finalResponse = response
break
}
log.Printf("CDN上传图片响应: Ver=%d, Seq=%d, RetCode=%d, FileKey=%s, RecvLen=%d, FileID=%s",
finalResponse.Ver, finalResponse.Seq, finalResponse.RetCode, finalResponse.FileKey,
finalResponse.RecvLen, finalResponse.FileID)
// 解析SKeyBuf获取FileID
log.Printf("SKeyBuf 长度: %d, SKeyResp: %d", len(finalResponse.SKeyBuf), finalResponse.SKeyResp)
// 如果 CDN 响应中已经有 FileID直接使用
if finalResponse.FileID == "" && len(finalResponse.SKeyBuf) > 0 {
var cdnPrepareResp wechat.CDNUploadMsgImgPrepareResponse
// SKeyBuf 可能是加密的,尝试解密
decryptedSKeyBuf, err := baseutils.AesDecrypt(finalResponse.SKeyBuf, userInfo.SessionKey)
if err != nil {
// 尝试直接解析(可能不需要解密)
err = proto.Unmarshal(finalResponse.SKeyBuf, &cdnPrepareResp)
if err == nil && cdnPrepareResp.FileId != nil {
finalResponse.FileID = cdnPrepareResp.GetFileId()
}
} else {
// 尝试解析 protobuf
err = proto.Unmarshal(decryptedSKeyBuf, &cdnPrepareResp)
if err == nil && cdnPrepareResp.FileId != nil {
finalResponse.FileID = cdnPrepareResp.GetFileId()
}
}
} else if finalResponse.FileID != "" {
log.Printf("响应中已包含 FileID: %s", finalResponse.FileID)
}
// 设置 CDN URL
finalResponse.CdnBigImgUrl = finalResponse.FileID
finalResponse.CdnMidImgUrl = finalResponse.FileID
finalResponse.CdnThumbImgUrl = finalResponse.FileID
return finalResponse, aesKeyHex, nil
}
// SendCdnSnsUploadImageReuqest 发送CDN朋友圈上传图片请求
func SendCdnSnsUploadImageReuqest(userInfo *baseinfo.UserInfo, imgData []byte) (*baseinfo.CdnSnsImageUploadResponse, error) {
// 生产SnsImgItem
snsImgItem := &baseinfo.SnsUploadImgItem{}
snsImgItem.Seq = uint32(rand.Intn(50))
snsImgItem.AesKey = []byte(baseutils.RandomStringByLength(16))
snsImgItem.ImageData = imgData
snsImgItem.ImageID = CreateID(imgData)
snsImgItem.CreateTime = uint32(time.Now().UnixNano() / 1000000000)
if userInfo.SNSDnsInfo == nil {
return nil, errors.New("SendCdnSnsUploadImageReuqest err:userInfo.SNSDnsInfo == nil")
}
snsImgItem.CDNDns = userInfo.SNSDnsInfo
// 创建上传图片请求
request, err := CreateSnsImageUploadRequest(userInfo, snsImgItem)
if err != nil {
return nil, err
}
// 打包请求
sendData := PackCdnSnsImageUploadRequest(request)
// 连接Cdn服务器
serverIP := snsImgItem.CDNDns.FrontIplist[0].GetStr()
serverPort := snsImgItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
sendImgLen := uint32(len(request.FileData))
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeSnsImageUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, errors.New("发送图片失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.RecvLen < sendImgLen {
continue
}
response.ImageMD5 = request.FileMD5
width, height := GetImageBounds(request.FileData)
response.ImageWidth = width
response.ImageHeight = height
return response, nil
}
}
// SendCdnSnsVideoUploadReuqest 发送CDN朋友圈上传视频请求
func SendCdnSnsVideoUploadReuqest(userInfo *baseinfo.UserInfo, videoData []byte, thumbData []byte) (*baseinfo.CdnSnsVideoUploadResponse, error) {
// 生产SnsImgItem
snsVideoItem := &baseinfo.SnsVideoUploadItem{}
snsVideoItem.Seq = uint32(rand.Intn(10))
snsVideoItem.AesKey = []byte(baseutils.RandomStringByLength(16))
snsVideoItem.VideoData = videoData
snsVideoItem.ThumbData = thumbData
snsVideoItem.VideoID = CreateID(videoData)
snsVideoItem.CreateTime = uint32(time.Now().UnixNano() / 1000000000)
snsVideoItem.CDNDns = userInfo.SNSDnsInfo
// 创建上传朋友圈视频请求
request, err := CreateCdnSnsVideoUploadRequest(userInfo, snsVideoItem)
if err != nil {
return nil, err
}
// 打包请求
sendData := PackCdnSnsVideoUploadRequest(request)
// 连接Cdn服务器
serverIP := snsVideoItem.CDNDns.FrontIplist[0].GetStr()
serverPort := snsVideoItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeSnsVideoUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, errors.New("上传朋友圈视频失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.RecvLen < request.TotalSize {
continue
}
// 设置请求数据
response.ReqData = request
return response, nil
}
}
// SendCdnSnsVideoDownloadReuqestPiece 分片下载
func SendCdnSnsVideoDownloadReuqestPiece(userInfo *baseinfo.UserInfo, snsVideoItem *baseinfo.SnsVideoDownloadItem) (*baseinfo.CdnSnsVideoDownloadResponse, error) {
// 创建朋友圈视频下载请求
request, err := CreateSnsVideoDownloadRequest(userInfo, snsVideoItem)
if err != nil {
return nil, err
}
// 打包请求
sendData := PackCdnSnsVideoDownloadRequest(request)
// 连接Cdn服务器
serverIP := snsVideoItem.CDNDns.FrontIplist[0].GetStr()
serverPort := snsVideoItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
// 接收响应信息
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeSnsVideoDownloadResponse(retData)
if err != nil {
return nil, err
}
// 判断错误码
if response.RetCode != 0 {
return nil, errors.New("下载朋友圈视频失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
return response, nil
}
// SendCdnSnsVideoDownloadReuqest 发送CDN朋友圈视频下载请求
func SendCdnSnsVideoDownloadReuqest(userInfo *baseinfo.UserInfo, encKey uint64, tmpURL string) ([]byte, error) {
retFileData := []byte{}
lessLength := uint32(2000000)
encLen := uint32(0)
videoFlag := string("V2")
retryCount := uint32(0)
for {
// 生产SnsImgItem
var snsVideoItem baseinfo.SnsVideoDownloadItem
snsVideoItem.Seq = uint32(rand.Intn(10))
snsVideoItem.URL = tmpURL
snsVideoItem.RangeStart = uint32(len(retFileData))
snsVideoItem.RangeEnd = snsVideoItem.RangeStart + lessLength
snsVideoItem.XSnsVideoFlag = videoFlag
snsVideoItem.CDNDns = userInfo.SNSDnsInfo
// 发送分片下载请求
response, err := SendCdnSnsVideoDownloadReuqestPiece(userInfo, &snsVideoItem)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, errors.New("SendCdnSnsVideoDownloadReuqest err: response.RetCode != 0")
}
// 设置加密的字节数
if encLen == 0 {
encLen = response.XEncLen
}
// 合并数据
retFileData = append(retFileData, response.FileData[0:]...)
currentLen := uint32(len(retFileData))
if currentLen >= response.TotalSize {
break
}
// 如果没有读取完
lessLength = response.TotalSize - currentLen
videoFlag = response.XSnsVideoFlag
}
if encKey != 0 {
retFileData = baseutils.DecryptSnsVideoData(retFileData, encLen, encKey)
}
// ioutil.WriteFile("log/1.mp4", retFileData, 0777)
// 解密数据
return retFileData, nil
}
// ForwardCdnImageRequest 转发Cdn图片
func ForwardCdnImageRequest(userInfo *baseinfo.UserInfo, item baseinfo.ForwardImageItem) (*baseinfo.PackHeader, error) {
req := &wechat.UploadMsgImgRequest{
BaseRequest: GetBaseRequest(userInfo),
AesKey: proto.String(item.AesKey),
CdnThumbAesKey: proto.String(item.AesKey),
CdnMidImgSize: proto.Uint32(uint32(item.CdnMidImgSize)),
CdnThumbImgSize: proto.Uint32(uint32(item.CdnThumbImgSize)),
CdnSmallImgUrl: proto.String(item.CdnMidImgUrl),
CdnThumbImgUrl: proto.String(item.CdnMidImgUrl),
TotalLen: proto.Uint32(uint32(item.CdnMidImgSize)),
DataLen: proto.Uint32(uint32(item.CdnMidImgSize)),
ClientImgId: &wechat.SKBuiltinString{
Str: proto.String(func() string {
//logger.Println(cli.GetAcctSect().GetUserName())
userMd5 := strings.ToLower(baseutils.Md5Value(userInfo.GetUserName() + "-" + item.ToUserName)[:16])
filekey := fmt.Sprintf("aupimg_%s_%d", userMd5, time.Now().UnixNano()/10000)
return filekey
}()),
},
RecvWxid: &wechat.SKBuiltinString{
Str: proto.String(item.ToUserName),
},
MsgType: proto.Uint32(3),
SenderWxid: &wechat.SKBuiltinString{
Str: proto.String(userInfo.GetUserName()),
},
StartPos: proto.Uint32(0),
ReqTime: proto.Uint32(uint32(time.Now().Unix())),
EncryVer: proto.Uint32(1),
Data: &wechat.SKBuiltinString_{
Len: proto.Uint32(0),
Buffer: []byte{},
},
MessageExt: proto.String("png"),
}
// 打包发送数据
srcData, _ := proto.Marshal(req)
sendEncodeData := Pack(userInfo, srcData, baseinfo.MMRequestTypeForwardCdnImage, 5)
resp, err := mmtls.MMHTTPPostData(userInfo.MMInfo, "/cgi-bin/micromsg-bin/uploadmsgimg", sendEncodeData)
if err != nil {
return nil, err
}
return DecodePackHeader(resp, nil)
}
// ForwardCdnVideoRequest 转发Cdn视频
func ForwardCdnVideoRequest(userInfo *baseinfo.UserInfo, item baseinfo.ForwardVideoItem) (*baseinfo.PackHeader, error) {
req := &wechat.UploadVideoRequest{
BaseRequest: GetBaseRequest(userInfo),
ClientMsgId: proto.String(func() string {
userMd5 := strings.ToLower(baseutils.Md5Value(userInfo.GetUserName() + "-" + item.ToUserName)[:16])
videokey := strconv.FormatInt(time.Now().UnixNano()/10000, 10) + "baed6285091"
filekey := fmt.Sprintf("aupvideo_%s_%d_%s", userMd5, time.Now().UnixNano()/10000, videokey)
return filekey
}()),
FromUserName: proto.String(userInfo.GetUserName()),
ToUserName: proto.String(item.ToUserName),
AESKey: proto.String(item.AesKey),
CDNThumbAESKey: proto.String(item.AesKey),
CDNThumbImgHeight: proto.Int32(120),
CDNThumbImgWidth: proto.Int32(120),
CDNThumbUrl: proto.String(item.CdnVideoUrl),
CDNVideoUrl: proto.String(item.CdnVideoUrl),
EncryVer: proto.Int32(1),
ThumbData: &wechat.BufferT{
ILen: proto.Uint32(0),
Buffer: []byte{},
},
VideoData: &wechat.BufferT{
ILen: proto.Uint32(0),
Buffer: []byte{},
},
MsgForwardType: proto.Uint32(1),
CameraType: proto.Uint32(2),
MsgSource: proto.String(""),
FuncFlag: proto.Uint32(0),
ThumbTotalLen: proto.Uint32(uint32(item.CdnThumbLength)),
VideoTotalLen: proto.Uint32(uint32(item.Length)),
VideoStartPos: proto.Uint32(0),
ThumbStartPos: proto.Uint32(0),
PlayLength: proto.Uint32(uint32(item.PlayLength)),
VideoFrom: proto.Int32(0),
NetworkEnv: proto.Uint32(1),
ReqTime: proto.Uint32(uint32(time.Now().Unix())),
}
// 打包发送数据
srcData, _ := proto.Marshal(req)
sendEncodeData := Pack(userInfo, srcData, baseinfo.MMRequestTypeForwardCdnVideo, 5)
resp, err := mmtls.MMHTTPPostData(userInfo.MMInfo, "/cgi-bin/micromsg-bin/uploadvideo", sendEncodeData)
if err != nil {
return nil, err
}
return DecodePackHeader(resp, nil)
}
// SendCdnUploadImageReuqestWithSource 发送CDN上传图片请求
func SendCdnUploadImageReuqestWithSource(userInfo *baseinfo.UserInfo, toUser string, imgData []byte, sourceValue uint32) (*baseinfo.CdnImageUploadResponse, string, error) {
imgItem := &baseinfo.UploadImgItem{}
imgItem.ToUser = toUser
imgItem.Seq = uint32(rand.Intn(10))
imgItem.LocalName = strconv.Itoa(rand.Intn(100))
imgItem.ExtName = ".pic"
// 随机生成AesKey
imgItem.AesKey = []byte(baseutils.RandomStringByLength(16))
aesKeyHex := hex.EncodeToString(imgItem.AesKey)
imgItem.ImageData = imgData
imgItem.CreateTime = uint32(time.Now().UnixNano() / 1000000000)
// 设置Dns路由信息
imgItem.CDNDns = userInfo.FAKEDnsInfo
// 创建上传图片请求
request := &baseinfo.CdnImageUploadRequest{}
request.Ver = 1
request.WeiXinNum = uint32(imgItem.CDNDns.GetUin())
request.Seq = imgItem.Seq
if !strings.HasPrefix(userInfo.LoginDataInfo.LoginData, "A") && userInfo.DeviceInfo != nil {
request.ClientOsType = userInfo.DeviceInfo.OsType
request.ClientVersion = userInfo.ClientVersion
if !(int(request.ClientVersion) > 0) {
request.ClientVersion = baseinfo.ClientVersion
}
} else {
request.ClientOsType = baseinfo.AndroidDeviceType
request.ClientVersion = baseinfo.AndroidClientVersion
}
request.AuthKey = imgItem.CDNDns.AuthKey.GetBuffer()
request.NetType = 1
request.AcceptDupack = 1
request.SafeProto = 1
request.FileType = 2
request.WxChatType = 0
request.LastRetCode = 0
request.IPSeq = 0
request.CliQuicFlag = 0
request.CompressType = 0
request.NoCheckAesKey = 1
request.EnableHit = 1
request.ExistAnceCheck = 0
request.AppType = 1
request.LocalName = imgItem.LocalName + imgItem.ExtName
request.Offset = 0
request.LargesVideo = 0
request.SourceFlag = 0
request.AdVideoFlag = 0
// ToUser
retBytes := baseutils.AesEncrypt([]byte(imgItem.ToUser), []byte("wxusrname2016cdn"))
request.ToUser = "@cdn2_" + baseutils.BytesToHexString(retBytes, false)
// request.HasThumb = 0
// request.ThumbData = nil
// ThumbData
thumbItem := CreateThumbImage(imgItem.ImageData)
if thumbItem == nil {
return nil, "", errors.New("生成缩略图失败")
}
thumbEncodeData := baseutils.AesEncryptECB(imgItem.ImageData, []byte(imgItem.AesKey))
request.HasThumb = 1
request.ThumbTotalSize = uint32(len(thumbEncodeData))
request.RawThumbSize = uint32(len(imgItem.ImageData))
request.RawThumbMD5 = baseutils.Md5ValueByte(imgItem.ImageData, false)
request.EncThumbCRC = baseutils.Adler32(0, thumbEncodeData)
request.ThumbData = thumbEncodeData
// FileKey
request.FileKey = "wxupload_" + imgItem.ToUser + imgItem.LocalName + "_" + strconv.Itoa(int(imgItem.CreateTime))
// FileData
fileEncodeData := baseutils.AesEncryptECB(imgItem.ImageData, []byte(imgItem.AesKey))
request.TotalSize = uint32(len(fileEncodeData))
request.RawTotalSize = uint32(len(imgItem.ImageData))
request.FileMD5 = baseutils.Md5ValueByte(fileEncodeData, false)
request.RawFileMD5 = baseutils.Md5ValueByte(imgItem.ImageData, false)
request.FileCRC = baseutils.Adler32(0, imgItem.ImageData)
request.DataCheckSum = baseutils.Adler32(0, fileEncodeData)
request.FileData = fileEncodeData
request.SetOfPicFormat = "111000"
// prepareRequestItem
var prepareRequestItem baseinfo.CDNUploadMsgImgPrepareRequestItem
prepareRequestItem.ToUser = imgItem.ToUser
prepareRequestItem.LocalName = imgItem.LocalName
prepareRequestItem.CreateTime = imgItem.CreateTime
prepareRequestItem.ThumbWidth = thumbItem.Width
prepareRequestItem.ThumbHeight = thumbItem.Height
prepareRequestItem.AesKey = imgItem.AesKey
prepareRequestItem.Crc32 = request.FileCRC
// SessionBuf - 使用自定义 source 值
request.SessionBuf = CreateCDNUploadMsgImgPrepareRequestWithSource(userInfo, &prepareRequestItem, sourceValue)
// 打包请求
sendData := PackCdnImageUploadRequest(request)
// 连接Cdn服务器
serverIP := imgItem.CDNDns.FrontIplist[0].GetStr()
serverPort := imgItem.CDNDns.FrontIpportList[0].PortList[0]
conn, err := ConnectCdnServer(serverIP, serverPort)
if err != nil {
return nil, "", err
}
// 发送数据
conn.Write(sendData)
defer conn.Close()
retryCount := uint32(0)
// 接收响应信息
sendImgLen := uint32(len(request.FileData))
var finalResponse *baseinfo.CdnImageUploadResponse
for {
// 接收响应信息,解析
retData := CDNRecvData(conn)
response, err := DecodeImageUploadResponse(retData)
if err != nil {
if retryCount < 3 {
retryCount++
continue
}
return nil, "", err
}
retryCount = 0
// 判断错误码
if response.RetCode != 0 {
return nil, "", errors.New("发送图片失败: ErrCode = " + GetErrStringByRetCode(response.RetCode))
}
// 判断 服务器是否接收完毕
if response.RecvLen < sendImgLen {
continue
}
// SKeyResp CDNUploadMsgImgPrepareResponse的错误码
if response.SKeyResp != 0 {
return nil, "", errors.New("发送图片失败: SKeyResp = " + strconv.Itoa(int(response.SKeyResp)))
}
// SKeyBuf
skbufLen := len(response.SKeyBuf)
if skbufLen <= 0 {
continue
}
finalResponse = response
break
}
log.Printf("CDN上传图片响应 (source=%d): Ver=%d, Seq=%d, RetCode=%d, FileKey=%s, RecvLen=%d, FileID=%s",
sourceValue, finalResponse.Ver, finalResponse.Seq, finalResponse.RetCode, finalResponse.FileKey,
finalResponse.RecvLen, finalResponse.FileID)
// 设置 CDN URL
finalResponse.CdnBigImgUrl = finalResponse.FileID
finalResponse.CdnMidImgUrl = finalResponse.FileID
finalResponse.CdnThumbImgUrl = finalResponse.FileID
return finalResponse, aesKeyHex, nil
}