Files

1203 lines
39 KiB
Go
Raw Permalink Normal View History

2026-02-17 13:06:23 +08:00
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
}