mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
add file reply voice
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
wxproto "github.com/yincongcyincong/weixin-macos/onebot/proto"
|
||||
)
|
||||
|
||||
// BuildFileUploadMsgProto 构建 /cgi-bin/micromsg-bin/sendfileuploadmsg 请求的protobuf
|
||||
func BuildFileUploadMsgProto(targetId string, fileInfo *FileInfo) (string, error) {
|
||||
// 复用上传时生成的 clientMsgId(去掉 _1 后缀),保证与 upload body 里的 filekey 一致
|
||||
clientMsgId := fileInfo.ClientMsgId
|
||||
if clientMsgId == "" {
|
||||
now := time.Now().Unix()
|
||||
clientMsgId = fmt.Sprintf("%s_%d_%d", targetId, now, rand.Intn(1000))
|
||||
fileInfo.ClientMsgId = clientMsgId
|
||||
}
|
||||
|
||||
version := NextVersion()
|
||||
msgType := uint32(1)
|
||||
field7 := uint32(0)
|
||||
field12 := uint32(0)
|
||||
fileSize := uint32(fileInfo.TotalLen)
|
||||
|
||||
msg := &wxproto.SendFileUploadMsgRequest{
|
||||
BaseRequest: &wxproto.ReplyMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: &globalSessionId,
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: &globalDeviceId,
|
||||
Platform: proto.String("UnifiedPCMac 26 arm64"),
|
||||
Version: &version,
|
||||
},
|
||||
ToUserName: &targetId,
|
||||
ClientMsgId: &clientMsgId,
|
||||
MsgType: &msgType,
|
||||
FileInfo: &wxproto.FileUploadFileInfo{
|
||||
FileName: &fileInfo.FileName,
|
||||
FileExt: &fileInfo.FileExt,
|
||||
FileMd5: &fileInfo.Md5,
|
||||
FileSize: &fileSize,
|
||||
},
|
||||
Field7: &field7,
|
||||
Field12: &field12,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal file upload msg proto failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("[file-upload-proto] final protobuf hex dump:\n%s\n", HexDump(data, 0))
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
// ParseFileUploadMsgResponse 解析 sendfileuploadmsg 响应,提取 fileUploadToken 和 msgSvrId
|
||||
func ParseFileUploadMsgResponse(data []byte) (string, string, error) {
|
||||
resp := &wxproto.SendFileUploadMsgResponse{}
|
||||
err := proto.Unmarshal(data, resp)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unmarshal file upload response failed: %w", err)
|
||||
}
|
||||
|
||||
if resp.BaseResponse != nil && resp.BaseResponse.Ret != nil && *resp.BaseResponse.Ret != 0 {
|
||||
errMsg := ""
|
||||
if resp.BaseResponse.ErrMsg != nil {
|
||||
errMsg = resp.BaseResponse.ErrMsg.GetMsg()
|
||||
}
|
||||
return "", "", fmt.Errorf("sendfileuploadmsg failed, ret=%d, errMsg=%s", *resp.BaseResponse.Ret, errMsg)
|
||||
}
|
||||
|
||||
token := resp.GetFileUploadToken()
|
||||
msgSvrId := fmt.Sprintf("%d", resp.GetMsgSvrId())
|
||||
return token, msgSvrId, nil
|
||||
}
|
||||
|
||||
// FileInfo 文件发送消息需要的信息
|
||||
type FileInfo struct {
|
||||
FileName string // 文件名 (title)
|
||||
TotalLen int64 // 文件大小
|
||||
AttachId string // 附件ID (来自upload完成回调)
|
||||
FileExt string // 文件扩展名
|
||||
CdnAttachURL string // CDN附件URL
|
||||
AesKey string // AES密钥
|
||||
Md5 string // 文件MD5
|
||||
OverwriteMsgId string // overwrite_newmsgid
|
||||
FileUploadToken string // fileuploadtoken
|
||||
ClientMsgId string // 由 BuildFileUploadMsgProto 生成,BuildFileMsgProto 复用
|
||||
}
|
||||
|
||||
// BuildFileMsgProto 构建发送文件消息的protobuf并返回hex编码的字符串
|
||||
// 复用reply_msg.proto (WxSendReplyMsg),msg_type=6
|
||||
func BuildFileMsgProto(sender, receiver string, fileInfo *FileInfo) (string, error) {
|
||||
now := time.Now().Unix()
|
||||
|
||||
// 构建文件appmsg XML
|
||||
appmsgXml := buildFileAppmsgXml(sender, fileInfo)
|
||||
|
||||
// 构建客户端消息ID: 复用 BuildFileUploadMsgProto 的 clientMsgId + 后缀
|
||||
clientMsgId := fileInfo.ClientMsgId + "_xwechat_1"
|
||||
|
||||
// msgsource
|
||||
msgsource := "<msgsource><alnode><fr>1</fr><cf>2</cf></alnode></msgsource>"
|
||||
|
||||
// proto2 需要使用指针
|
||||
var (
|
||||
unknown2 = []byte("wx6618f1cfc6c132f8") // appid
|
||||
unknown3 = int32(0)
|
||||
msgType = int32(6) // 文件消息类型
|
||||
flag = int32(1)
|
||||
unknown13 = []byte{}
|
||||
unknown14 = []byte{}
|
||||
unknown15 = []byte{}
|
||||
version = NextVersion()
|
||||
)
|
||||
|
||||
msg := &wxproto.WxSendReplyMsg{
|
||||
Header: &wxproto.ReplyMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: &globalSessionId,
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: &globalDeviceId,
|
||||
Platform: proto.String("UnifiedPCMac 26 arm64"),
|
||||
Version: &version,
|
||||
},
|
||||
Body: &wxproto.ReplyMsgBody{
|
||||
Sender: &sender,
|
||||
Unknown2: unknown2,
|
||||
Unknown3: &unknown3,
|
||||
Receiver: &receiver,
|
||||
MsgType: &msgType,
|
||||
Content: []byte(appmsgXml),
|
||||
SendTimestamp: proto.Int64(now),
|
||||
ClientMsgId: &clientMsgId,
|
||||
Flag: &flag,
|
||||
Msgsource: []byte(msgsource),
|
||||
Unknown13: unknown13,
|
||||
Unknown14: unknown14,
|
||||
Unknown15: unknown15,
|
||||
},
|
||||
Unknown5: []byte(fileInfo.Md5),
|
||||
Unknown9: proto.Int32(1),
|
||||
Unknown10: proto.Uint64(uint64(rand.Uint32())),
|
||||
Unknown11: proto.Int32(2),
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal file proto failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("[file-proto] final protobuf hex dump:\n%s\n", HexDump(data, 0))
|
||||
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
// buildFileAppmsgXml 构建文件消息的appmsg XML
|
||||
func buildFileAppmsgXml(selfWxid string, info *FileInfo) string {
|
||||
xml := `<appmsg appid="wx6618f1cfc6c132f8" sdkver="0">`
|
||||
xml += `<title>` + escapeXmlStr(info.FileName) + `</title>`
|
||||
xml += `<des></des>`
|
||||
xml += `<action></action>`
|
||||
xml += `<type>6</type>`
|
||||
xml += `<showtype>0</showtype>`
|
||||
xml += `<soundtype>0</soundtype>`
|
||||
xml += `<mediatagname></mediatagname>`
|
||||
xml += `<messageext></messageext>`
|
||||
xml += `<messageaction></messageaction>`
|
||||
xml += `<content></content>`
|
||||
xml += `<contentattr>0</contentattr>`
|
||||
xml += `<url></url>`
|
||||
xml += `<lowurl></lowurl>`
|
||||
xml += `<dataurl></dataurl>`
|
||||
xml += `<lowdataurl></lowdataurl>`
|
||||
xml += `<songalbumurl></songalbumurl>`
|
||||
xml += `<songlyric></songlyric>`
|
||||
xml += `<template_id></template_id>`
|
||||
xml += `<appattach>`
|
||||
xml += `<totallen>` + fmt.Sprintf("%d", info.TotalLen) + `</totallen>`
|
||||
xml += `<attachid>` + escapeXmlStr(info.AttachId) + `</attachid>`
|
||||
xml += `<emoticonmd5></emoticonmd5>`
|
||||
xml += `<fileext>` + escapeXmlStr(info.FileExt) + `</fileext>`
|
||||
xml += `<cdnattachurl>` + escapeXmlStr(info.CdnAttachURL) + `</cdnattachurl>`
|
||||
xml += `<aeskey>` + escapeXmlStr(info.AesKey) + `</aeskey>`
|
||||
xml += `<encryver>0</encryver>`
|
||||
xml += `<overwrite_newmsgid>` + escapeXmlStr(info.OverwriteMsgId) + `</overwrite_newmsgid>`
|
||||
xml += `<fileuploadtoken>` + escapeXmlStr(info.FileUploadToken) + `</fileuploadtoken>`
|
||||
xml += `</appattach>`
|
||||
xml += `<extinfo></extinfo>`
|
||||
xml += `<sourceusername></sourceusername>`
|
||||
xml += `<sourcedisplayname></sourcedisplayname>`
|
||||
xml += `<thumburl></thumburl>`
|
||||
xml += `<md5>` + escapeXmlStr(info.Md5) + `</md5>`
|
||||
xml += `<statextstr></statextstr>`
|
||||
xml += `</appmsg>`
|
||||
xml += `<fromusername>` + escapeXmlStr(selfWxid) + `</fromusername>`
|
||||
|
||||
return xml
|
||||
}
|
||||
|
||||
// BuildCheckMd5Proto 构建 /cgi-bin/micromsg-bin/checkmd5 请求的protobuf
|
||||
func BuildCheckMd5Proto(fileInfo *FileInfo) (string, error) {
|
||||
version := NextVersion()
|
||||
field5 := uint32(0)
|
||||
|
||||
msg := &wxproto.CheckMd5Request{
|
||||
BaseRequest: &wxproto.ReplyMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: &globalSessionId,
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: &globalDeviceId,
|
||||
Platform: proto.String("UnifiedPCMac 26 arm64"),
|
||||
Version: &version,
|
||||
},
|
||||
FileKey: &fileInfo.CdnAttachURL,
|
||||
FileMd5: &fileInfo.Md5,
|
||||
Field4: []byte{},
|
||||
Field5: &field5,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal checkmd5 proto failed: %w", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("[checkmd5-proto] final protobuf hex dump:\n%s\n", HexDump(data, 0))
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
// ParseCheckMd5Response 解析 checkmd5 响应
|
||||
// ret=0 表示文件已存在(秒传),ret=102 表示文件不存在需要正常发送,两者都视为成功
|
||||
func ParseCheckMd5Response(data []byte) error {
|
||||
resp := &wxproto.CheckMd5Response{}
|
||||
err := proto.Unmarshal(data, resp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal checkmd5 response failed: %w", err)
|
||||
}
|
||||
|
||||
if resp.BaseResponse != nil && resp.BaseResponse.Ret != nil {
|
||||
ret := *resp.BaseResponse.Ret
|
||||
// 0=文件已存在(秒传), 102=文件不存在(正常发送), 都可以继续
|
||||
if ret != 0 {
|
||||
errMsg := ""
|
||||
if resp.BaseResponse.ErrMsg != nil {
|
||||
errMsg = resp.BaseResponse.ErrMsg.GetMsg()
|
||||
}
|
||||
return fmt.Errorf("checkmd5 failed, ret=%d, errMsg=%s", ret, errMsg)
|
||||
}
|
||||
Info("checkmd5 ret", "ret", ret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -4,6 +4,7 @@ go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/frida/frida-go v1.0.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
|
||||
github.com/rs/zerolog v1.35.1
|
||||
@@ -14,7 +15,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
|
||||
+48
-2
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -49,13 +50,21 @@ func sendHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
} else if v.Type == "image" || v.Type == "video" {
|
||||
} else if v.Type == "image" || v.Type == "video" || v.Type == "record" || v.Type == "voice" || v.Type == "file" {
|
||||
msgType := v.Type
|
||||
if msgType == "record" || msgType == "voice" {
|
||||
msgType = "voice"
|
||||
}
|
||||
// file: 走 iPad860 风格 uploadappattach 直传(不走 CDN)
|
||||
if msgType == "file" {
|
||||
msgType = "send_file_simple"
|
||||
}
|
||||
ch := make(chan error, 1)
|
||||
msg := &SendMsg{
|
||||
UserId: req.UserID,
|
||||
GroupID: req.GroupID,
|
||||
Content: v.Data.File,
|
||||
Type: v.Type,
|
||||
Type: msgType,
|
||||
ResultChan: ch,
|
||||
}
|
||||
msgChan <- msg
|
||||
@@ -212,3 +221,40 @@ func jsonUnescapeString(s string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// getFileExt 从文件名中提取扩展名(不含.)
|
||||
func getFileExt(fileName string) string {
|
||||
idx := strings.LastIndex(fileName, ".")
|
||||
if idx == -1 || idx == len(fileName)-1 {
|
||||
return ""
|
||||
}
|
||||
return fileName[idx+1:]
|
||||
}
|
||||
|
||||
// extractFileName 从路径或URL中提取文件名
|
||||
func extractFileName(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
// 跳过 base64 数据
|
||||
if strings.HasPrefix(s, "base64://") || strings.Contains(s, ";base64,") {
|
||||
return ""
|
||||
}
|
||||
// URL 路径
|
||||
if strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") || strings.HasPrefix(s, "file://") {
|
||||
if u, err := url.Parse(s); err == nil {
|
||||
p := u.Path
|
||||
if idx := strings.LastIndex(p, "/"); idx != -1 {
|
||||
return p[idx+1:]
|
||||
}
|
||||
return p
|
||||
}
|
||||
}
|
||||
// 本地文件路径
|
||||
if strings.Contains(s, "/") || strings.Contains(s, "\\") {
|
||||
if idx := strings.LastIndexAny(s, "/\\"); idx != -1 {
|
||||
return s[idx+1:]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
+34
-1
@@ -61,7 +61,7 @@ func initFlag() {
|
||||
flag.StringVar(&config.FridaGadgetAddr, "gadget_addr", "127.0.0.1:27042", "Gadget 地址: 127.0.0.1:27042 仅当 type 为 gadget 时有效")
|
||||
flag.StringVar(&config.OnebotToken, "token", "MuseBot", "OneBot Token: MuseBot")
|
||||
flag.StringVar(&config.ImagePath, "image_path", "", "图片路径: /Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/xxx/temp/xxx/2026-01/Img/")
|
||||
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_9_58_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
|
||||
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_10_53_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
|
||||
flag.StringVar(&config.ConnType, "conn_type", "http", "连接类型: http | websocket")
|
||||
flag.IntVar(&config.SendInterval, "send_interval", 1000, "发送间隔: ms")
|
||||
flag.IntVar(&config.WechatPid, "wechat_pid", 0, "微信进程 PID,不设置则自动查找")
|
||||
@@ -294,6 +294,39 @@ func loadJs() {
|
||||
m.ResultChan = ch.(chan error)
|
||||
}
|
||||
msgChan <- m
|
||||
case "upload_voice_finish":
|
||||
m := &SendMsg{
|
||||
Type: "send_voice",
|
||||
}
|
||||
targetId := ""
|
||||
if targetIdInter, ok := pMap["target_id"]; ok {
|
||||
targetId = targetIdInter.(string)
|
||||
if strings.Contains(targetId, "wxid_") {
|
||||
m.UserId = targetId
|
||||
} else {
|
||||
m.GroupID = targetId
|
||||
}
|
||||
}
|
||||
if cdnKey, ok := pMap["cdn_key"]; ok {
|
||||
m.CdnKey = cdnKey.(string)
|
||||
}
|
||||
if aesKey, ok := pMap["aes_key"]; ok {
|
||||
m.AesKey = aesKey.(string)
|
||||
}
|
||||
if voiceDuration, ok := pMap["voice_duration"]; ok {
|
||||
if vd, ok := voiceDuration.(float64); ok {
|
||||
m.VoiceDuration = int32(vd)
|
||||
}
|
||||
}
|
||||
if silkDataLen, ok := pMap["silk_data_len"]; ok {
|
||||
if sdl, ok := silkDataLen.(float64); ok {
|
||||
m.SilkDataLen = int32(sdl)
|
||||
}
|
||||
}
|
||||
if ch, ok := pendingResultMap.LoadAndDelete(targetId); ok {
|
||||
m.ResultChan = ch.(chan error)
|
||||
}
|
||||
msgChan <- m
|
||||
case "download":
|
||||
err = Download(payloadJson)
|
||||
if err != nil {
|
||||
|
||||
+16
-4
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/frida/frida-go/frida"
|
||||
)
|
||||
@@ -27,12 +28,19 @@ var (
|
||||
|
||||
config = &Config{}
|
||||
|
||||
userID2NicknameMap sync.Map
|
||||
userID2FileMsgMap sync.Map
|
||||
videoInfoMap sync.Map // targetId -> *VideoInfo
|
||||
buf2RespChan = make(chan *Buf2RespData, 10)
|
||||
userID2NicknameMap sync.Map
|
||||
userID2FileMsgMap sync.Map
|
||||
videoInfoMap sync.Map // targetId -> *VideoInfo
|
||||
buf2RespChan = make(chan *Buf2RespData, 10)
|
||||
debugRespChan = make(chan []byte, 1)
|
||||
appAttachRespChan = make(chan []byte, 1)
|
||||
)
|
||||
|
||||
// NextVersion 获取当前taskId作为版本号
|
||||
func NextVersion() uint32 {
|
||||
return uint32(atomic.LoadInt64(&taskId))
|
||||
}
|
||||
|
||||
type WechatMessage struct {
|
||||
GroupId string `json:"group_id"`
|
||||
SelfID string `json:"self_id"`
|
||||
@@ -73,6 +81,10 @@ type SendMsg struct {
|
||||
Duration int32
|
||||
VideoSize int32
|
||||
|
||||
VoiceDuration int32
|
||||
SilkDataLen int32
|
||||
Unknown13 int32
|
||||
|
||||
ReferMsgId string
|
||||
ReferMsgSender string
|
||||
ReferMsgType int
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.5.1
|
||||
// source: check_md5.proto
|
||||
|
||||
package wxproto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// /cgi-bin/micromsg-bin/checkmd5 请求
|
||||
type CheckMd5Request struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseRequest *ReplyMsgHeader `protobuf:"bytes,1,opt,name=baseRequest" json:"baseRequest,omitempty"`
|
||||
FileKey *string `protobuf:"bytes,2,opt,name=fileKey" json:"fileKey,omitempty"` // CDN file key (attachid)
|
||||
FileMd5 *string `protobuf:"bytes,3,opt,name=fileMd5" json:"fileMd5,omitempty"` // 文件MD5 (32位hex字符串)
|
||||
Field4 []byte `protobuf:"bytes,4,opt,name=field4" json:"field4,omitempty"` // 空
|
||||
Field5 *uint32 `protobuf:"varint,5,opt,name=field5" json:"field5,omitempty"` // 0
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) Reset() {
|
||||
*x = CheckMd5Request{}
|
||||
mi := &file_check_md5_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckMd5Request) ProtoMessage() {}
|
||||
|
||||
func (x *CheckMd5Request) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_check_md5_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckMd5Request.ProtoReflect.Descriptor instead.
|
||||
func (*CheckMd5Request) Descriptor() ([]byte, []int) {
|
||||
return file_check_md5_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) GetBaseRequest() *ReplyMsgHeader {
|
||||
if x != nil {
|
||||
return x.BaseRequest
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) GetFileKey() string {
|
||||
if x != nil && x.FileKey != nil {
|
||||
return *x.FileKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) GetFileMd5() string {
|
||||
if x != nil && x.FileMd5 != nil {
|
||||
return *x.FileMd5
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) GetField4() []byte {
|
||||
if x != nil {
|
||||
return x.Field4
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CheckMd5Request) GetField5() uint32 {
|
||||
if x != nil && x.Field5 != nil {
|
||||
return *x.Field5
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// /cgi-bin/micromsg-bin/checkmd5 响应
|
||||
type CheckMd5Response struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseResponse *BaseResponse `protobuf:"bytes,1,opt,name=baseResponse" json:"baseResponse,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckMd5Response) Reset() {
|
||||
*x = CheckMd5Response{}
|
||||
mi := &file_check_md5_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckMd5Response) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckMd5Response) ProtoMessage() {}
|
||||
|
||||
func (x *CheckMd5Response) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_check_md5_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckMd5Response.ProtoReflect.Descriptor instead.
|
||||
func (*CheckMd5Response) Descriptor() ([]byte, []int) {
|
||||
return file_check_md5_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *CheckMd5Response) GetBaseResponse() *BaseResponse {
|
||||
if x != nil {
|
||||
return x.BaseResponse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_check_md5_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_check_md5_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x0fcheck_md5.proto\x12\awxproto\x1a\rapp_msg.proto\x1a\x11common_resp.proto\"\xb0\x01\n" +
|
||||
"\x0fCheckMd5Request\x129\n" +
|
||||
"\vbaseRequest\x18\x01 \x01(\v2\x17.wxproto.ReplyMsgHeaderR\vbaseRequest\x12\x18\n" +
|
||||
"\afileKey\x18\x02 \x01(\tR\afileKey\x12\x18\n" +
|
||||
"\afileMd5\x18\x03 \x01(\tR\afileMd5\x12\x16\n" +
|
||||
"\x06field4\x18\x04 \x01(\fR\x06field4\x12\x16\n" +
|
||||
"\x06field5\x18\x05 \x01(\rR\x06field5\"M\n" +
|
||||
"\x10CheckMd5Response\x129\n" +
|
||||
"\fbaseResponse\x18\x01 \x01(\v2\x15.wxproto.BaseResponseR\fbaseResponseB>Z<github.com/yincongcyincong/weixin-macos/onebot/proto;wxproto"
|
||||
|
||||
var (
|
||||
file_check_md5_proto_rawDescOnce sync.Once
|
||||
file_check_md5_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_check_md5_proto_rawDescGZIP() []byte {
|
||||
file_check_md5_proto_rawDescOnce.Do(func() {
|
||||
file_check_md5_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_check_md5_proto_rawDesc), len(file_check_md5_proto_rawDesc)))
|
||||
})
|
||||
return file_check_md5_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_check_md5_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_check_md5_proto_goTypes = []any{
|
||||
(*CheckMd5Request)(nil), // 0: wxproto.CheckMd5Request
|
||||
(*CheckMd5Response)(nil), // 1: wxproto.CheckMd5Response
|
||||
(*ReplyMsgHeader)(nil), // 2: wxproto.ReplyMsgHeader
|
||||
(*BaseResponse)(nil), // 3: wxproto.BaseResponse
|
||||
}
|
||||
var file_check_md5_proto_depIdxs = []int32{
|
||||
2, // 0: wxproto.CheckMd5Request.baseRequest:type_name -> wxproto.ReplyMsgHeader
|
||||
3, // 1: wxproto.CheckMd5Response.baseResponse:type_name -> wxproto.BaseResponse
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_check_md5_proto_init() }
|
||||
func file_check_md5_proto_init() {
|
||||
if File_check_md5_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_msg_proto_init()
|
||||
file_common_resp_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_check_md5_proto_rawDesc), len(file_check_md5_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_check_md5_proto_goTypes,
|
||||
DependencyIndexes: file_check_md5_proto_depIdxs,
|
||||
MessageInfos: file_check_md5_proto_msgTypes,
|
||||
}.Build()
|
||||
File_check_md5_proto = out.File
|
||||
file_check_md5_proto_goTypes = nil
|
||||
file_check_md5_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.5.1
|
||||
// source: file_msg.proto
|
||||
|
||||
package wxproto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// /cgi-bin/micromsg-bin/sendfileuploadmsg 请求
|
||||
type SendFileUploadMsgRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseRequest *ReplyMsgHeader `protobuf:"bytes,1,opt,name=baseRequest" json:"baseRequest,omitempty"`
|
||||
ToUserName *string `protobuf:"bytes,2,opt,name=toUserName" json:"toUserName,omitempty"`
|
||||
ClientMsgId *string `protobuf:"bytes,3,opt,name=clientMsgId" json:"clientMsgId,omitempty"`
|
||||
MsgType *uint32 `protobuf:"varint,4,opt,name=msgType" json:"msgType,omitempty"`
|
||||
FileInfo *FileUploadFileInfo `protobuf:"bytes,5,opt,name=fileInfo" json:"fileInfo,omitempty"`
|
||||
Field7 *uint32 `protobuf:"varint,7,opt,name=field7" json:"field7,omitempty"`
|
||||
Field12 *uint32 `protobuf:"varint,12,opt,name=field12" json:"field12,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) Reset() {
|
||||
*x = SendFileUploadMsgRequest{}
|
||||
mi := &file_file_msg_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SendFileUploadMsgRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_file_msg_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SendFileUploadMsgRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SendFileUploadMsgRequest) Descriptor() ([]byte, []int) {
|
||||
return file_file_msg_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetBaseRequest() *ReplyMsgHeader {
|
||||
if x != nil {
|
||||
return x.BaseRequest
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetToUserName() string {
|
||||
if x != nil && x.ToUserName != nil {
|
||||
return *x.ToUserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetClientMsgId() string {
|
||||
if x != nil && x.ClientMsgId != nil {
|
||||
return *x.ClientMsgId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetMsgType() uint32 {
|
||||
if x != nil && x.MsgType != nil {
|
||||
return *x.MsgType
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetFileInfo() *FileUploadFileInfo {
|
||||
if x != nil {
|
||||
return x.FileInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetField7() uint32 {
|
||||
if x != nil && x.Field7 != nil {
|
||||
return *x.Field7
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendFileUploadMsgRequest) GetField12() uint32 {
|
||||
if x != nil && x.Field12 != nil {
|
||||
return *x.Field12
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type FileUploadFileInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
FileName *string `protobuf:"bytes,1,opt,name=fileName" json:"fileName,omitempty"`
|
||||
FileExt *string `protobuf:"bytes,2,opt,name=fileExt" json:"fileExt,omitempty"`
|
||||
FileMd5 *string `protobuf:"bytes,3,opt,name=fileMd5" json:"fileMd5,omitempty"`
|
||||
FileSize *uint32 `protobuf:"varint,4,opt,name=fileSize" json:"fileSize,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) Reset() {
|
||||
*x = FileUploadFileInfo{}
|
||||
mi := &file_file_msg_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FileUploadFileInfo) ProtoMessage() {}
|
||||
|
||||
func (x *FileUploadFileInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_file_msg_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FileUploadFileInfo.ProtoReflect.Descriptor instead.
|
||||
func (*FileUploadFileInfo) Descriptor() ([]byte, []int) {
|
||||
return file_file_msg_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) GetFileName() string {
|
||||
if x != nil && x.FileName != nil {
|
||||
return *x.FileName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) GetFileExt() string {
|
||||
if x != nil && x.FileExt != nil {
|
||||
return *x.FileExt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) GetFileMd5() string {
|
||||
if x != nil && x.FileMd5 != nil {
|
||||
return *x.FileMd5
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FileUploadFileInfo) GetFileSize() uint32 {
|
||||
if x != nil && x.FileSize != nil {
|
||||
return *x.FileSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_file_msg_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_file_msg_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x0efile_msg.proto\x12\awxproto\x1a\rapp_msg.proto\"\x9c\x02\n" +
|
||||
"\x18SendFileUploadMsgRequest\x129\n" +
|
||||
"\vbaseRequest\x18\x01 \x01(\v2\x17.wxproto.ReplyMsgHeaderR\vbaseRequest\x12\x1e\n" +
|
||||
"\n" +
|
||||
"toUserName\x18\x02 \x01(\tR\n" +
|
||||
"toUserName\x12 \n" +
|
||||
"\vclientMsgId\x18\x03 \x01(\tR\vclientMsgId\x12\x18\n" +
|
||||
"\amsgType\x18\x04 \x01(\rR\amsgType\x127\n" +
|
||||
"\bfileInfo\x18\x05 \x01(\v2\x1b.wxproto.FileUploadFileInfoR\bfileInfo\x12\x16\n" +
|
||||
"\x06field7\x18\a \x01(\rR\x06field7\x12\x18\n" +
|
||||
"\afield12\x18\f \x01(\rR\afield12\"\x80\x01\n" +
|
||||
"\x12FileUploadFileInfo\x12\x1a\n" +
|
||||
"\bfileName\x18\x01 \x01(\tR\bfileName\x12\x18\n" +
|
||||
"\afileExt\x18\x02 \x01(\tR\afileExt\x12\x18\n" +
|
||||
"\afileMd5\x18\x03 \x01(\tR\afileMd5\x12\x1a\n" +
|
||||
"\bfileSize\x18\x04 \x01(\rR\bfileSizeB>Z<github.com/yincongcyincong/weixin-macos/onebot/proto;wxproto"
|
||||
|
||||
var (
|
||||
file_file_msg_proto_rawDescOnce sync.Once
|
||||
file_file_msg_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_file_msg_proto_rawDescGZIP() []byte {
|
||||
file_file_msg_proto_rawDescOnce.Do(func() {
|
||||
file_file_msg_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_file_msg_proto_rawDesc), len(file_file_msg_proto_rawDesc)))
|
||||
})
|
||||
return file_file_msg_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_file_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_file_msg_proto_goTypes = []any{
|
||||
(*SendFileUploadMsgRequest)(nil), // 0: wxproto.SendFileUploadMsgRequest
|
||||
(*FileUploadFileInfo)(nil), // 1: wxproto.FileUploadFileInfo
|
||||
(*ReplyMsgHeader)(nil), // 2: wxproto.ReplyMsgHeader
|
||||
}
|
||||
var file_file_msg_proto_depIdxs = []int32{
|
||||
2, // 0: wxproto.SendFileUploadMsgRequest.baseRequest:type_name -> wxproto.ReplyMsgHeader
|
||||
1, // 1: wxproto.SendFileUploadMsgRequest.fileInfo:type_name -> wxproto.FileUploadFileInfo
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_file_msg_proto_init() }
|
||||
func file_file_msg_proto_init() {
|
||||
if File_file_msg_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_msg_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_file_msg_proto_rawDesc), len(file_file_msg_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_file_msg_proto_goTypes,
|
||||
DependencyIndexes: file_file_msg_proto_depIdxs,
|
||||
MessageInfos: file_file_msg_proto_msgTypes,
|
||||
}.Build()
|
||||
File_file_msg_proto = out.File
|
||||
file_file_msg_proto_goTypes = nil
|
||||
file_file_msg_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.5.1
|
||||
// source: send_app_msg.proto
|
||||
|
||||
package wxproto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// /cgi-bin/micromsg-bin/sendappmsg 请求
|
||||
// 字段号严格对齐 wechat7016 SendAppMsgRequest,
|
||||
// 但 baseRequest 使用本项目 macOS 风格的 ReplyMsgHeader。
|
||||
type SendAppMsgReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseRequest *ReplyMsgHeader `protobuf:"bytes,1,opt,name=baseRequest" json:"baseRequest,omitempty"`
|
||||
Msg *AppMsgBody `protobuf:"bytes,2,opt,name=msg" json:"msg,omitempty"`
|
||||
CommentUrl *string `protobuf:"bytes,3,opt,name=commentUrl" json:"commentUrl,omitempty"`
|
||||
ReqTime *uint32 `protobuf:"varint,4,opt,name=reqTime" json:"reqTime,omitempty"`
|
||||
Md5 *string `protobuf:"bytes,5,opt,name=md5" json:"md5,omitempty"`
|
||||
FileType *uint32 `protobuf:"varint,6,opt,name=fileType" json:"fileType,omitempty"`
|
||||
Signature *string `protobuf:"bytes,7,opt,name=signature" json:"signature,omitempty"`
|
||||
FromSence *string `protobuf:"bytes,8,opt,name=fromSence" json:"fromSence,omitempty"`
|
||||
HitMd5 *uint32 `protobuf:"varint,9,opt,name=hitMd5" json:"hitMd5,omitempty"`
|
||||
Crc32 *uint32 `protobuf:"varint,10,opt,name=crc32" json:"crc32,omitempty"`
|
||||
MsgForwardType *uint32 `protobuf:"varint,11,opt,name=msgForwardType" json:"msgForwardType,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) Reset() {
|
||||
*x = SendAppMsgReq{}
|
||||
mi := &file_send_app_msg_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SendAppMsgReq) ProtoMessage() {}
|
||||
|
||||
func (x *SendAppMsgReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_send_app_msg_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SendAppMsgReq.ProtoReflect.Descriptor instead.
|
||||
func (*SendAppMsgReq) Descriptor() ([]byte, []int) {
|
||||
return file_send_app_msg_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetBaseRequest() *ReplyMsgHeader {
|
||||
if x != nil {
|
||||
return x.BaseRequest
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetMsg() *AppMsgBody {
|
||||
if x != nil {
|
||||
return x.Msg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetCommentUrl() string {
|
||||
if x != nil && x.CommentUrl != nil {
|
||||
return *x.CommentUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetReqTime() uint32 {
|
||||
if x != nil && x.ReqTime != nil {
|
||||
return *x.ReqTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetMd5() string {
|
||||
if x != nil && x.Md5 != nil {
|
||||
return *x.Md5
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetFileType() uint32 {
|
||||
if x != nil && x.FileType != nil {
|
||||
return *x.FileType
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetSignature() string {
|
||||
if x != nil && x.Signature != nil {
|
||||
return *x.Signature
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetFromSence() string {
|
||||
if x != nil && x.FromSence != nil {
|
||||
return *x.FromSence
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetHitMd5() uint32 {
|
||||
if x != nil && x.HitMd5 != nil {
|
||||
return *x.HitMd5
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetCrc32() uint32 {
|
||||
if x != nil && x.Crc32 != nil {
|
||||
return *x.Crc32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgReq) GetMsgForwardType() uint32 {
|
||||
if x != nil && x.MsgForwardType != nil {
|
||||
return *x.MsgForwardType
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// AppMsg 消息体 (wechat7016 AppMsg)
|
||||
type AppMsgBody struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
FromUserName *string `protobuf:"bytes,1,opt,name=fromUserName" json:"fromUserName,omitempty"`
|
||||
AppId *string `protobuf:"bytes,2,opt,name=appId" json:"appId,omitempty"`
|
||||
SdkVersion *uint32 `protobuf:"varint,3,opt,name=sdkVersion" json:"sdkVersion,omitempty"`
|
||||
ToUserName *string `protobuf:"bytes,4,opt,name=toUserName" json:"toUserName,omitempty"`
|
||||
Type *uint32 `protobuf:"varint,5,opt,name=type" json:"type,omitempty"` // 6 = 文件
|
||||
Content *string `protobuf:"bytes,6,opt,name=content" json:"content,omitempty"` // appmsg XML
|
||||
CreateTime *int64 `protobuf:"varint,7,opt,name=createTime" json:"createTime,omitempty"`
|
||||
ClientMsgId *string `protobuf:"bytes,8,opt,name=clientMsgId" json:"clientMsgId,omitempty"`
|
||||
MsgSource *string `protobuf:"bytes,12,opt,name=msgSource" json:"msgSource,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) Reset() {
|
||||
*x = AppMsgBody{}
|
||||
mi := &file_send_app_msg_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AppMsgBody) ProtoMessage() {}
|
||||
|
||||
func (x *AppMsgBody) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_send_app_msg_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AppMsgBody.ProtoReflect.Descriptor instead.
|
||||
func (*AppMsgBody) Descriptor() ([]byte, []int) {
|
||||
return file_send_app_msg_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetFromUserName() string {
|
||||
if x != nil && x.FromUserName != nil {
|
||||
return *x.FromUserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetAppId() string {
|
||||
if x != nil && x.AppId != nil {
|
||||
return *x.AppId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetSdkVersion() uint32 {
|
||||
if x != nil && x.SdkVersion != nil {
|
||||
return *x.SdkVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetToUserName() string {
|
||||
if x != nil && x.ToUserName != nil {
|
||||
return *x.ToUserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetType() uint32 {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetContent() string {
|
||||
if x != nil && x.Content != nil {
|
||||
return *x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetCreateTime() int64 {
|
||||
if x != nil && x.CreateTime != nil {
|
||||
return *x.CreateTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetClientMsgId() string {
|
||||
if x != nil && x.ClientMsgId != nil {
|
||||
return *x.ClientMsgId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppMsgBody) GetMsgSource() string {
|
||||
if x != nil && x.MsgSource != nil {
|
||||
return *x.MsgSource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// /cgi-bin/micromsg-bin/sendappmsg 响应
|
||||
type SendAppMsgResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseResponse *BaseResponse `protobuf:"bytes,1,opt,name=baseResponse" json:"baseResponse,omitempty"`
|
||||
AppId *string `protobuf:"bytes,2,opt,name=appId" json:"appId,omitempty"`
|
||||
FromUserName *string `protobuf:"bytes,3,opt,name=fromUserName" json:"fromUserName,omitempty"`
|
||||
ToUserName *string `protobuf:"bytes,4,opt,name=toUserName" json:"toUserName,omitempty"`
|
||||
MsgId *int32 `protobuf:"varint,5,opt,name=msgId" json:"msgId,omitempty"`
|
||||
ClientMsgId *string `protobuf:"bytes,6,opt,name=clientMsgId" json:"clientMsgId,omitempty"`
|
||||
CreateTime *int32 `protobuf:"varint,7,opt,name=createTime" json:"createTime,omitempty"`
|
||||
Type *int32 `protobuf:"varint,8,opt,name=type" json:"type,omitempty"`
|
||||
NewMsgId *int64 `protobuf:"varint,9,opt,name=newMsgId" json:"newMsgId,omitempty"`
|
||||
Aeskey *string `protobuf:"bytes,10,opt,name=aeskey" json:"aeskey,omitempty"`
|
||||
MsgSource *string `protobuf:"bytes,11,opt,name=msgSource" json:"msgSource,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) Reset() {
|
||||
*x = SendAppMsgResp{}
|
||||
mi := &file_send_app_msg_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SendAppMsgResp) ProtoMessage() {}
|
||||
|
||||
func (x *SendAppMsgResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_send_app_msg_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SendAppMsgResp.ProtoReflect.Descriptor instead.
|
||||
func (*SendAppMsgResp) Descriptor() ([]byte, []int) {
|
||||
return file_send_app_msg_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetBaseResponse() *BaseResponse {
|
||||
if x != nil {
|
||||
return x.BaseResponse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetAppId() string {
|
||||
if x != nil && x.AppId != nil {
|
||||
return *x.AppId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetFromUserName() string {
|
||||
if x != nil && x.FromUserName != nil {
|
||||
return *x.FromUserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetToUserName() string {
|
||||
if x != nil && x.ToUserName != nil {
|
||||
return *x.ToUserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetMsgId() int32 {
|
||||
if x != nil && x.MsgId != nil {
|
||||
return *x.MsgId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetClientMsgId() string {
|
||||
if x != nil && x.ClientMsgId != nil {
|
||||
return *x.ClientMsgId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetCreateTime() int32 {
|
||||
if x != nil && x.CreateTime != nil {
|
||||
return *x.CreateTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetType() int32 {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetNewMsgId() int64 {
|
||||
if x != nil && x.NewMsgId != nil {
|
||||
return *x.NewMsgId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetAeskey() string {
|
||||
if x != nil && x.Aeskey != nil {
|
||||
return *x.Aeskey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SendAppMsgResp) GetMsgSource() string {
|
||||
if x != nil && x.MsgSource != nil {
|
||||
return *x.MsgSource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_send_app_msg_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_send_app_msg_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12send_app_msg.proto\x12\awxproto\x1a\rapp_msg.proto\x1a\x11common_resp.proto\"\xeb\x02\n" +
|
||||
"\rSendAppMsgReq\x129\n" +
|
||||
"\vbaseRequest\x18\x01 \x01(\v2\x17.wxproto.ReplyMsgHeaderR\vbaseRequest\x12%\n" +
|
||||
"\x03msg\x18\x02 \x01(\v2\x13.wxproto.AppMsgBodyR\x03msg\x12\x1e\n" +
|
||||
"\n" +
|
||||
"commentUrl\x18\x03 \x01(\tR\n" +
|
||||
"commentUrl\x12\x18\n" +
|
||||
"\areqTime\x18\x04 \x01(\rR\areqTime\x12\x10\n" +
|
||||
"\x03md5\x18\x05 \x01(\tR\x03md5\x12\x1a\n" +
|
||||
"\bfileType\x18\x06 \x01(\rR\bfileType\x12\x1c\n" +
|
||||
"\tsignature\x18\a \x01(\tR\tsignature\x12\x1c\n" +
|
||||
"\tfromSence\x18\b \x01(\tR\tfromSence\x12\x16\n" +
|
||||
"\x06hitMd5\x18\t \x01(\rR\x06hitMd5\x12\x14\n" +
|
||||
"\x05crc32\x18\n" +
|
||||
" \x01(\rR\x05crc32\x12&\n" +
|
||||
"\x0emsgForwardType\x18\v \x01(\rR\x0emsgForwardType\"\x94\x02\n" +
|
||||
"\n" +
|
||||
"AppMsgBody\x12\"\n" +
|
||||
"\ffromUserName\x18\x01 \x01(\tR\ffromUserName\x12\x14\n" +
|
||||
"\x05appId\x18\x02 \x01(\tR\x05appId\x12\x1e\n" +
|
||||
"\n" +
|
||||
"sdkVersion\x18\x03 \x01(\rR\n" +
|
||||
"sdkVersion\x12\x1e\n" +
|
||||
"\n" +
|
||||
"toUserName\x18\x04 \x01(\tR\n" +
|
||||
"toUserName\x12\x12\n" +
|
||||
"\x04type\x18\x05 \x01(\rR\x04type\x12\x18\n" +
|
||||
"\acontent\x18\x06 \x01(\tR\acontent\x12\x1e\n" +
|
||||
"\n" +
|
||||
"createTime\x18\a \x01(\x03R\n" +
|
||||
"createTime\x12 \n" +
|
||||
"\vclientMsgId\x18\b \x01(\tR\vclientMsgId\x12\x1c\n" +
|
||||
"\tmsgSource\x18\f \x01(\tR\tmsgSource\"\xe3\x02\n" +
|
||||
"\x0eSendAppMsgResp\x129\n" +
|
||||
"\fbaseResponse\x18\x01 \x01(\v2\x15.wxproto.BaseResponseR\fbaseResponse\x12\x14\n" +
|
||||
"\x05appId\x18\x02 \x01(\tR\x05appId\x12\"\n" +
|
||||
"\ffromUserName\x18\x03 \x01(\tR\ffromUserName\x12\x1e\n" +
|
||||
"\n" +
|
||||
"toUserName\x18\x04 \x01(\tR\n" +
|
||||
"toUserName\x12\x14\n" +
|
||||
"\x05msgId\x18\x05 \x01(\x05R\x05msgId\x12 \n" +
|
||||
"\vclientMsgId\x18\x06 \x01(\tR\vclientMsgId\x12\x1e\n" +
|
||||
"\n" +
|
||||
"createTime\x18\a \x01(\x05R\n" +
|
||||
"createTime\x12\x12\n" +
|
||||
"\x04type\x18\b \x01(\x05R\x04type\x12\x1a\n" +
|
||||
"\bnewMsgId\x18\t \x01(\x03R\bnewMsgId\x12\x16\n" +
|
||||
"\x06aeskey\x18\n" +
|
||||
" \x01(\tR\x06aeskey\x12\x1c\n" +
|
||||
"\tmsgSource\x18\v \x01(\tR\tmsgSourceB>Z<github.com/yincongcyincong/weixin-macos/onebot/proto;wxproto"
|
||||
|
||||
var (
|
||||
file_send_app_msg_proto_rawDescOnce sync.Once
|
||||
file_send_app_msg_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_send_app_msg_proto_rawDescGZIP() []byte {
|
||||
file_send_app_msg_proto_rawDescOnce.Do(func() {
|
||||
file_send_app_msg_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_send_app_msg_proto_rawDesc), len(file_send_app_msg_proto_rawDesc)))
|
||||
})
|
||||
return file_send_app_msg_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_send_app_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_send_app_msg_proto_goTypes = []any{
|
||||
(*SendAppMsgReq)(nil), // 0: wxproto.SendAppMsgReq
|
||||
(*AppMsgBody)(nil), // 1: wxproto.AppMsgBody
|
||||
(*SendAppMsgResp)(nil), // 2: wxproto.SendAppMsgResp
|
||||
(*ReplyMsgHeader)(nil), // 3: wxproto.ReplyMsgHeader
|
||||
(*BaseResponse)(nil), // 4: wxproto.BaseResponse
|
||||
}
|
||||
var file_send_app_msg_proto_depIdxs = []int32{
|
||||
3, // 0: wxproto.SendAppMsgReq.baseRequest:type_name -> wxproto.ReplyMsgHeader
|
||||
1, // 1: wxproto.SendAppMsgReq.msg:type_name -> wxproto.AppMsgBody
|
||||
4, // 2: wxproto.SendAppMsgResp.baseResponse:type_name -> wxproto.BaseResponse
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_send_app_msg_proto_init() }
|
||||
func file_send_app_msg_proto_init() {
|
||||
if File_send_app_msg_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_msg_proto_init()
|
||||
file_common_resp_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_send_app_msg_proto_rawDesc), len(file_send_app_msg_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_send_app_msg_proto_goTypes,
|
||||
DependencyIndexes: file_send_app_msg_proto_depIdxs,
|
||||
MessageInfos: file_send_app_msg_proto_msgTypes,
|
||||
}.Build()
|
||||
File_send_app_msg_proto = out.File
|
||||
file_send_app_msg_proto_goTypes = nil
|
||||
file_send_app_msg_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.5.1
|
||||
// source: upload_app_attach.proto
|
||||
|
||||
package wxproto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// 通用二进制缓冲(iPad860 SKBuiltinBufferT 对齐)
|
||||
type SKBuiltinBufferT struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ILen *uint32 `protobuf:"varint,1,opt,name=iLen" json:"iLen,omitempty"`
|
||||
Buffer []byte `protobuf:"bytes,2,opt,name=buffer" json:"buffer,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SKBuiltinBufferT) Reset() {
|
||||
*x = SKBuiltinBufferT{}
|
||||
mi := &file_upload_app_attach_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SKBuiltinBufferT) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SKBuiltinBufferT) ProtoMessage() {}
|
||||
|
||||
func (x *SKBuiltinBufferT) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_upload_app_attach_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SKBuiltinBufferT.ProtoReflect.Descriptor instead.
|
||||
func (*SKBuiltinBufferT) Descriptor() ([]byte, []int) {
|
||||
return file_upload_app_attach_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SKBuiltinBufferT) GetILen() uint32 {
|
||||
if x != nil && x.ILen != nil {
|
||||
return *x.ILen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SKBuiltinBufferT) GetBuffer() []byte {
|
||||
if x != nil {
|
||||
return x.Buffer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// /cgi-bin/micromsg-bin/uploadappattach 请求
|
||||
// 参考 we-chat-ipad860 wechat.proto UploadAppAttachRequest,
|
||||
// 但 baseRequest 使用本项目 macOS 风格的 ReplyMsgHeader。
|
||||
type UploadAppAttachRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseRequest *ReplyMsgHeader `protobuf:"bytes,1,opt,name=baseRequest" json:"baseRequest,omitempty"`
|
||||
AppId *string `protobuf:"bytes,2,opt,name=appId" json:"appId,omitempty"`
|
||||
SdkVersion *uint32 `protobuf:"varint,3,opt,name=sdkVersion" json:"sdkVersion,omitempty"`
|
||||
ClientAppDataId *string `protobuf:"bytes,4,opt,name=clientAppDataId" json:"clientAppDataId,omitempty"` // {receiver}_{ts}_UploadFile
|
||||
UserName *string `protobuf:"bytes,5,opt,name=userName" json:"userName,omitempty"` // 接收方 wxid
|
||||
TotalLen *uint32 `protobuf:"varint,6,opt,name=totalLen" json:"totalLen,omitempty"` // 文件总长度
|
||||
StartPos *uint32 `protobuf:"varint,7,opt,name=startPos" json:"startPos,omitempty"` // 本分片偏移
|
||||
DataLen *uint32 `protobuf:"varint,8,opt,name=dataLen" json:"dataLen,omitempty"` // 本分片长度
|
||||
Data *SKBuiltinBufferT `protobuf:"bytes,9,opt,name=data" json:"data,omitempty"` // 本分片原始字节
|
||||
Type *uint32 `protobuf:"varint,10,opt,name=type" json:"type,omitempty"` // 6 = 文件
|
||||
Md5 *string `protobuf:"bytes,11,opt,name=md5" json:"md5,omitempty"` // 文件 MD5
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) Reset() {
|
||||
*x = UploadAppAttachRequest{}
|
||||
mi := &file_upload_app_attach_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UploadAppAttachRequest) ProtoMessage() {}
|
||||
|
||||
func (x *UploadAppAttachRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_upload_app_attach_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UploadAppAttachRequest.ProtoReflect.Descriptor instead.
|
||||
func (*UploadAppAttachRequest) Descriptor() ([]byte, []int) {
|
||||
return file_upload_app_attach_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetBaseRequest() *ReplyMsgHeader {
|
||||
if x != nil {
|
||||
return x.BaseRequest
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetAppId() string {
|
||||
if x != nil && x.AppId != nil {
|
||||
return *x.AppId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetSdkVersion() uint32 {
|
||||
if x != nil && x.SdkVersion != nil {
|
||||
return *x.SdkVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetClientAppDataId() string {
|
||||
if x != nil && x.ClientAppDataId != nil {
|
||||
return *x.ClientAppDataId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetUserName() string {
|
||||
if x != nil && x.UserName != nil {
|
||||
return *x.UserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetTotalLen() uint32 {
|
||||
if x != nil && x.TotalLen != nil {
|
||||
return *x.TotalLen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetStartPos() uint32 {
|
||||
if x != nil && x.StartPos != nil {
|
||||
return *x.StartPos
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetDataLen() uint32 {
|
||||
if x != nil && x.DataLen != nil {
|
||||
return *x.DataLen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetData() *SKBuiltinBufferT {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetType() uint32 {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachRequest) GetMd5() string {
|
||||
if x != nil && x.Md5 != nil {
|
||||
return *x.Md5
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// /cgi-bin/micromsg-bin/uploadappattach 响应
|
||||
type UploadAppAttachResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BaseResponse *BaseResponse `protobuf:"bytes,1,opt,name=baseResponse" json:"baseResponse,omitempty"`
|
||||
AppId *string `protobuf:"bytes,2,opt,name=appId" json:"appId,omitempty"`
|
||||
MediaId *string `protobuf:"bytes,3,opt,name=mediaId" json:"mediaId,omitempty"` // attachid
|
||||
ClientAppDataId *string `protobuf:"bytes,4,opt,name=clientAppDataId" json:"clientAppDataId,omitempty"`
|
||||
UserName *string `protobuf:"bytes,5,opt,name=userName" json:"userName,omitempty"`
|
||||
TotalLen *uint32 `protobuf:"varint,6,opt,name=totalLen" json:"totalLen,omitempty"`
|
||||
StartPos *uint32 `protobuf:"varint,7,opt,name=startPos" json:"startPos,omitempty"`
|
||||
DataLen *uint32 `protobuf:"varint,8,opt,name=dataLen" json:"dataLen,omitempty"`
|
||||
CreateTime *uint64 `protobuf:"varint,9,opt,name=createTime" json:"createTime,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) Reset() {
|
||||
*x = UploadAppAttachResponse{}
|
||||
mi := &file_upload_app_attach_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UploadAppAttachResponse) ProtoMessage() {}
|
||||
|
||||
func (x *UploadAppAttachResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_upload_app_attach_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UploadAppAttachResponse.ProtoReflect.Descriptor instead.
|
||||
func (*UploadAppAttachResponse) Descriptor() ([]byte, []int) {
|
||||
return file_upload_app_attach_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetBaseResponse() *BaseResponse {
|
||||
if x != nil {
|
||||
return x.BaseResponse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetAppId() string {
|
||||
if x != nil && x.AppId != nil {
|
||||
return *x.AppId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetMediaId() string {
|
||||
if x != nil && x.MediaId != nil {
|
||||
return *x.MediaId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetClientAppDataId() string {
|
||||
if x != nil && x.ClientAppDataId != nil {
|
||||
return *x.ClientAppDataId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetUserName() string {
|
||||
if x != nil && x.UserName != nil {
|
||||
return *x.UserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetTotalLen() uint32 {
|
||||
if x != nil && x.TotalLen != nil {
|
||||
return *x.TotalLen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetStartPos() uint32 {
|
||||
if x != nil && x.StartPos != nil {
|
||||
return *x.StartPos
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetDataLen() uint32 {
|
||||
if x != nil && x.DataLen != nil {
|
||||
return *x.DataLen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UploadAppAttachResponse) GetCreateTime() uint64 {
|
||||
if x != nil && x.CreateTime != nil {
|
||||
return *x.CreateTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_upload_app_attach_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_upload_app_attach_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x17upload_app_attach.proto\x12\awxproto\x1a\rapp_msg.proto\x1a\x11common_resp.proto\">\n" +
|
||||
"\x10SKBuiltinBufferT\x12\x12\n" +
|
||||
"\x04iLen\x18\x01 \x01(\rR\x04iLen\x12\x16\n" +
|
||||
"\x06buffer\x18\x02 \x01(\fR\x06buffer\"\xf6\x02\n" +
|
||||
"\x16UploadAppAttachRequest\x129\n" +
|
||||
"\vbaseRequest\x18\x01 \x01(\v2\x17.wxproto.ReplyMsgHeaderR\vbaseRequest\x12\x14\n" +
|
||||
"\x05appId\x18\x02 \x01(\tR\x05appId\x12\x1e\n" +
|
||||
"\n" +
|
||||
"sdkVersion\x18\x03 \x01(\rR\n" +
|
||||
"sdkVersion\x12(\n" +
|
||||
"\x0fclientAppDataId\x18\x04 \x01(\tR\x0fclientAppDataId\x12\x1a\n" +
|
||||
"\buserName\x18\x05 \x01(\tR\buserName\x12\x1a\n" +
|
||||
"\btotalLen\x18\x06 \x01(\rR\btotalLen\x12\x1a\n" +
|
||||
"\bstartPos\x18\a \x01(\rR\bstartPos\x12\x18\n" +
|
||||
"\adataLen\x18\b \x01(\rR\adataLen\x12-\n" +
|
||||
"\x04data\x18\t \x01(\v2\x19.wxproto.SKBuiltinBufferTR\x04data\x12\x12\n" +
|
||||
"\x04type\x18\n" +
|
||||
" \x01(\rR\x04type\x12\x10\n" +
|
||||
"\x03md5\x18\v \x01(\tR\x03md5\"\xbc\x02\n" +
|
||||
"\x17UploadAppAttachResponse\x129\n" +
|
||||
"\fbaseResponse\x18\x01 \x01(\v2\x15.wxproto.BaseResponseR\fbaseResponse\x12\x14\n" +
|
||||
"\x05appId\x18\x02 \x01(\tR\x05appId\x12\x18\n" +
|
||||
"\amediaId\x18\x03 \x01(\tR\amediaId\x12(\n" +
|
||||
"\x0fclientAppDataId\x18\x04 \x01(\tR\x0fclientAppDataId\x12\x1a\n" +
|
||||
"\buserName\x18\x05 \x01(\tR\buserName\x12\x1a\n" +
|
||||
"\btotalLen\x18\x06 \x01(\rR\btotalLen\x12\x1a\n" +
|
||||
"\bstartPos\x18\a \x01(\rR\bstartPos\x12\x18\n" +
|
||||
"\adataLen\x18\b \x01(\rR\adataLen\x12\x1e\n" +
|
||||
"\n" +
|
||||
"createTime\x18\t \x01(\x04R\n" +
|
||||
"createTimeB>Z<github.com/yincongcyincong/weixin-macos/onebot/proto;wxproto"
|
||||
|
||||
var (
|
||||
file_upload_app_attach_proto_rawDescOnce sync.Once
|
||||
file_upload_app_attach_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_upload_app_attach_proto_rawDescGZIP() []byte {
|
||||
file_upload_app_attach_proto_rawDescOnce.Do(func() {
|
||||
file_upload_app_attach_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_upload_app_attach_proto_rawDesc), len(file_upload_app_attach_proto_rawDesc)))
|
||||
})
|
||||
return file_upload_app_attach_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_upload_app_attach_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_upload_app_attach_proto_goTypes = []any{
|
||||
(*SKBuiltinBufferT)(nil), // 0: wxproto.SKBuiltinBufferT
|
||||
(*UploadAppAttachRequest)(nil), // 1: wxproto.UploadAppAttachRequest
|
||||
(*UploadAppAttachResponse)(nil), // 2: wxproto.UploadAppAttachResponse
|
||||
(*ReplyMsgHeader)(nil), // 3: wxproto.ReplyMsgHeader
|
||||
(*BaseResponse)(nil), // 4: wxproto.BaseResponse
|
||||
}
|
||||
var file_upload_app_attach_proto_depIdxs = []int32{
|
||||
3, // 0: wxproto.UploadAppAttachRequest.baseRequest:type_name -> wxproto.ReplyMsgHeader
|
||||
0, // 1: wxproto.UploadAppAttachRequest.data:type_name -> wxproto.SKBuiltinBufferT
|
||||
4, // 2: wxproto.UploadAppAttachResponse.baseResponse:type_name -> wxproto.BaseResponse
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_upload_app_attach_proto_init() }
|
||||
func file_upload_app_attach_proto_init() {
|
||||
if File_upload_app_attach_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_msg_proto_init()
|
||||
file_common_resp_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_upload_app_attach_proto_rawDesc), len(file_upload_app_attach_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_upload_app_attach_proto_goTypes,
|
||||
DependencyIndexes: file_upload_app_attach_proto_depIdxs,
|
||||
MessageInfos: file_upload_app_attach_proto_msgTypes,
|
||||
}.Build()
|
||||
File_upload_app_attach_proto = out.File
|
||||
file_upload_app_attach_proto_goTypes = nil
|
||||
file_upload_app_attach_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.5.1
|
||||
// source: voice_msg.proto
|
||||
|
||||
package wxproto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// 发送语音消息的protobuf结构
|
||||
type WxSendVoiceMsg struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
FromUser string `protobuf:"bytes,1,opt,name=from_user,json=fromUser,proto3" json:"from_user,omitempty"`
|
||||
ToUser string `protobuf:"bytes,2,opt,name=to_user,json=toUser,proto3" json:"to_user,omitempty"`
|
||||
Unknown3 int32 `protobuf:"varint,3,opt,name=unknown3,proto3" json:"unknown3,omitempty"`
|
||||
Unknown4 int32 `protobuf:"varint,4,opt,name=unknown4,proto3" json:"unknown4,omitempty"`
|
||||
ClientMsgId string `protobuf:"bytes,5,opt,name=client_msg_id,json=clientMsgId,proto3" json:"client_msg_id,omitempty"`
|
||||
// field 6 skipped
|
||||
Duration int32 `protobuf:"varint,7,opt,name=duration,proto3" json:"duration,omitempty"`
|
||||
// field 8 skipped
|
||||
Unknown9 int32 `protobuf:"varint,9,opt,name=unknown9,proto3" json:"unknown9,omitempty"`
|
||||
Header *VoiceMsgHeader `protobuf:"bytes,10,opt,name=header,proto3" json:"header,omitempty"`
|
||||
Unknown11 int32 `protobuf:"varint,11,opt,name=unknown11,proto3" json:"unknown11,omitempty"`
|
||||
// field 12 skipped
|
||||
Unknown13 int32 `protobuf:"varint,13,opt,name=unknown13,proto3" json:"unknown13,omitempty"`
|
||||
// field 14 skipped
|
||||
Unknown15 int32 `protobuf:"varint,15,opt,name=unknown15,proto3" json:"unknown15,omitempty"`
|
||||
Unknown16 int32 `protobuf:"varint,16,opt,name=unknown16,proto3" json:"unknown16,omitempty"`
|
||||
Unknown17 int64 `protobuf:"varint,17,opt,name=unknown17,proto3" json:"unknown17,omitempty"`
|
||||
// fields 18-19 skipped
|
||||
AesKey []byte `protobuf:"bytes,20,opt,name=aes_key,json=aesKey,proto3" json:"aes_key,omitempty"`
|
||||
CdnKey []byte `protobuf:"bytes,21,opt,name=cdn_key,json=cdnKey,proto3" json:"cdn_key,omitempty"`
|
||||
// fields 22-23 skipped
|
||||
Unknown24 []byte `protobuf:"bytes,24,opt,name=unknown24,proto3" json:"unknown24,omitempty"`
|
||||
Unknown25 int32 `protobuf:"varint,25,opt,name=unknown25,proto3" json:"unknown25,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) Reset() {
|
||||
*x = WxSendVoiceMsg{}
|
||||
mi := &file_voice_msg_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*WxSendVoiceMsg) ProtoMessage() {}
|
||||
|
||||
func (x *WxSendVoiceMsg) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_voice_msg_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use WxSendVoiceMsg.ProtoReflect.Descriptor instead.
|
||||
func (*WxSendVoiceMsg) Descriptor() ([]byte, []int) {
|
||||
return file_voice_msg_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetFromUser() string {
|
||||
if x != nil {
|
||||
return x.FromUser
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetToUser() string {
|
||||
if x != nil {
|
||||
return x.ToUser
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown3() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown3
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown4() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown4
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetClientMsgId() string {
|
||||
if x != nil {
|
||||
return x.ClientMsgId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetDuration() int32 {
|
||||
if x != nil {
|
||||
return x.Duration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown9() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown9
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetHeader() *VoiceMsgHeader {
|
||||
if x != nil {
|
||||
return x.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown11() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown11
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown13() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown13
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown15() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown15
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown16() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown16
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown17() int64 {
|
||||
if x != nil {
|
||||
return x.Unknown17
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetAesKey() []byte {
|
||||
if x != nil {
|
||||
return x.AesKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetCdnKey() []byte {
|
||||
if x != nil {
|
||||
return x.CdnKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown24() []byte {
|
||||
if x != nil {
|
||||
return x.Unknown24
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *WxSendVoiceMsg) GetUnknown25() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown25
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type VoiceMsgHeader struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Flag []byte `protobuf:"bytes,1,opt,name=flag,proto3" json:"flag,omitempty"`
|
||||
SessionId int64 `protobuf:"varint,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
|
||||
ClientProof []byte `protobuf:"bytes,3,opt,name=client_proof,json=clientProof,proto3" json:"client_proof,omitempty"`
|
||||
DeviceId int64 `protobuf:"varint,4,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
|
||||
Platform []byte `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"`
|
||||
Version int32 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// field 7 skipped
|
||||
Unknown8 int32 `protobuf:"varint,8,opt,name=unknown8,proto3" json:"unknown8,omitempty"`
|
||||
// fields 9-14 skipped
|
||||
Unknown15 int32 `protobuf:"varint,15,opt,name=unknown15,proto3" json:"unknown15,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) Reset() {
|
||||
*x = VoiceMsgHeader{}
|
||||
mi := &file_voice_msg_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*VoiceMsgHeader) ProtoMessage() {}
|
||||
|
||||
func (x *VoiceMsgHeader) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_voice_msg_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use VoiceMsgHeader.ProtoReflect.Descriptor instead.
|
||||
func (*VoiceMsgHeader) Descriptor() ([]byte, []int) {
|
||||
return file_voice_msg_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetFlag() []byte {
|
||||
if x != nil {
|
||||
return x.Flag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetSessionId() int64 {
|
||||
if x != nil {
|
||||
return x.SessionId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetClientProof() []byte {
|
||||
if x != nil {
|
||||
return x.ClientProof
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetDeviceId() int64 {
|
||||
if x != nil {
|
||||
return x.DeviceId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetPlatform() []byte {
|
||||
if x != nil {
|
||||
return x.Platform
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetVersion() int32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetUnknown8() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown8
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *VoiceMsgHeader) GetUnknown15() int32 {
|
||||
if x != nil {
|
||||
return x.Unknown15
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_voice_msg_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_voice_msg_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x0fvoice_msg.proto\x12\awxproto\"\x8f\x04\n" +
|
||||
"\x0eWxSendVoiceMsg\x12\x1b\n" +
|
||||
"\tfrom_user\x18\x01 \x01(\tR\bfromUser\x12\x17\n" +
|
||||
"\ato_user\x18\x02 \x01(\tR\x06toUser\x12\x1a\n" +
|
||||
"\bunknown3\x18\x03 \x01(\x05R\bunknown3\x12\x1a\n" +
|
||||
"\bunknown4\x18\x04 \x01(\x05R\bunknown4\x12\"\n" +
|
||||
"\rclient_msg_id\x18\x05 \x01(\tR\vclientMsgId\x12\x1a\n" +
|
||||
"\bduration\x18\a \x01(\x05R\bduration\x12\x1a\n" +
|
||||
"\bunknown9\x18\t \x01(\x05R\bunknown9\x12/\n" +
|
||||
"\x06header\x18\n" +
|
||||
" \x01(\v2\x17.wxproto.VoiceMsgHeaderR\x06header\x12\x1c\n" +
|
||||
"\tunknown11\x18\v \x01(\x05R\tunknown11\x12\x1c\n" +
|
||||
"\tunknown13\x18\r \x01(\x05R\tunknown13\x12\x1c\n" +
|
||||
"\tunknown15\x18\x0f \x01(\x05R\tunknown15\x12\x1c\n" +
|
||||
"\tunknown16\x18\x10 \x01(\x05R\tunknown16\x12\x1c\n" +
|
||||
"\tunknown17\x18\x11 \x01(\x03R\tunknown17\x12\x17\n" +
|
||||
"\aaes_key\x18\x14 \x01(\fR\x06aesKey\x12\x17\n" +
|
||||
"\acdn_key\x18\x15 \x01(\fR\x06cdnKey\x12\x1c\n" +
|
||||
"\tunknown24\x18\x18 \x01(\fR\tunknown24\x12\x1c\n" +
|
||||
"\tunknown25\x18\x19 \x01(\x05R\tunknown25\"\xf3\x01\n" +
|
||||
"\x0eVoiceMsgHeader\x12\x12\n" +
|
||||
"\x04flag\x18\x01 \x01(\fR\x04flag\x12\x1d\n" +
|
||||
"\n" +
|
||||
"session_id\x18\x02 \x01(\x03R\tsessionId\x12!\n" +
|
||||
"\fclient_proof\x18\x03 \x01(\fR\vclientProof\x12\x1b\n" +
|
||||
"\tdevice_id\x18\x04 \x01(\x03R\bdeviceId\x12\x1a\n" +
|
||||
"\bplatform\x18\x05 \x01(\fR\bplatform\x12\x18\n" +
|
||||
"\aversion\x18\x06 \x01(\x05R\aversion\x12\x1a\n" +
|
||||
"\bunknown8\x18\b \x01(\x05R\bunknown8\x12\x1c\n" +
|
||||
"\tunknown15\x18\x0f \x01(\x05R\tunknown15B>Z<github.com/yincongcyincong/weixin-macos/onebot/proto;wxprotob\x06proto3"
|
||||
|
||||
var (
|
||||
file_voice_msg_proto_rawDescOnce sync.Once
|
||||
file_voice_msg_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_voice_msg_proto_rawDescGZIP() []byte {
|
||||
file_voice_msg_proto_rawDescOnce.Do(func() {
|
||||
file_voice_msg_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_voice_msg_proto_rawDesc), len(file_voice_msg_proto_rawDesc)))
|
||||
})
|
||||
return file_voice_msg_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_voice_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_voice_msg_proto_goTypes = []any{
|
||||
(*WxSendVoiceMsg)(nil), // 0: wxproto.WxSendVoiceMsg
|
||||
(*VoiceMsgHeader)(nil), // 1: wxproto.VoiceMsgHeader
|
||||
}
|
||||
var file_voice_msg_proto_depIdxs = []int32{
|
||||
1, // 0: wxproto.WxSendVoiceMsg.header:type_name -> wxproto.VoiceMsgHeader
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_voice_msg_proto_init() }
|
||||
func file_voice_msg_proto_init() {
|
||||
if File_voice_msg_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_voice_msg_proto_rawDesc), len(file_voice_msg_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_voice_msg_proto_goTypes,
|
||||
DependencyIndexes: file_voice_msg_proto_depIdxs,
|
||||
MessageInfos: file_voice_msg_proto_msgTypes,
|
||||
}.Build()
|
||||
File_voice_msg_proto = out.File
|
||||
file_voice_msg_proto_goTypes = nil
|
||||
file_voice_msg_proto_depIdxs = nil
|
||||
}
|
||||
+46
-1
@@ -93,6 +93,7 @@ function initAddresses() {
|
||||
setImmediate(setupSendTextMessageDynamic);
|
||||
setImmediate(setupSendFileMessageDynamic);
|
||||
setImmediate(setupSendFileUploadMessageDynamic);
|
||||
setImmediate(setupSendAppAttachMessageDynamic);
|
||||
setImmediate(attachBlrX8Hook);
|
||||
setImmediate(AttachSendFunc);
|
||||
setImmediate(attachReq2buf);
|
||||
@@ -332,6 +333,8 @@ var replyProtoHexGlobal = "";
|
||||
// 文件消息protobuf全局变量 (从Go直接传入hex编码)
|
||||
var fileProtoHexGlobal = "";
|
||||
var fileUploadProtoHexGlobal = "";
|
||||
// uploadappattach protobuf全局变量 (从Go直接传入hex编码)
|
||||
var appAttachProtoHexGlobal = "";
|
||||
|
||||
// 文件消息全局变量
|
||||
var fileCgiAddr = ptr(0);
|
||||
@@ -345,6 +348,11 @@ var fileUploadCgiAddr = ptr(0);
|
||||
var sendFileUploadMessageAddr = ptr(0);
|
||||
var fileUploadMessageAddr = ptr(0);
|
||||
|
||||
// uploadappattach 全局变量
|
||||
var appAttachCgiAddr = ptr(0);
|
||||
var sendAppAttachMessageAddr = ptr(0);
|
||||
var appAttachMessageAddr = ptr(0);
|
||||
|
||||
// 回复消息全局变量
|
||||
var replyMessageCallbackFunc;
|
||||
var replyCgiAddr = ptr(0);
|
||||
@@ -362,7 +370,7 @@ function setupSendTextMessageDynamic() {
|
||||
textCgiAddr = Memory.alloc(128);
|
||||
sendTextMessageAddr = Memory.alloc(256);
|
||||
textMessageAddr = Memory.alloc(256);
|
||||
textProtoDataAddr = Memory.alloc(4096);
|
||||
textProtoDataAddr = Memory.alloc(64 * 1024); // 支持 50KB 分片(uploadappattach)的 protobuf
|
||||
|
||||
// A. 写入字符串内容
|
||||
patchString(textCgiAddr, "/cgi-bin/micromsg-bin/newsendmsg");
|
||||
@@ -452,6 +460,35 @@ function triggerSendFileUploadMessage(taskId, sender, receiver, protoHex, payloa
|
||||
return triggerSendMediaMessage(taskId, sender, receiver, protoHex, payloadHex, "fileupload");
|
||||
}
|
||||
|
||||
// -------------------------uploadappattach分区-------------------------
|
||||
function setupSendAppAttachMessageDynamic() {
|
||||
appAttachCgiAddr = Memory.alloc(128);
|
||||
sendAppAttachMessageAddr = Memory.alloc(256);
|
||||
appAttachMessageAddr = Memory.alloc(256);
|
||||
|
||||
patchString(appAttachCgiAddr, "/cgi-bin/micromsg-bin/uploadappattach");
|
||||
|
||||
sendAppAttachMessageAddr.add(0x00).writeU64(0);
|
||||
sendAppAttachMessageAddr.add(0x08).writeU64(0);
|
||||
sendAppAttachMessageAddr.add(0x10).writeU64(0);
|
||||
sendAppAttachMessageAddr.add(0x18).writeU64(1);
|
||||
sendAppAttachMessageAddr.add(0x20).writeU32(taskIdGlobal);
|
||||
sendAppAttachMessageAddr.add(0x28).writePointer(appAttachMessageAddr);
|
||||
|
||||
appAttachMessageAddr.add(0x00).writePointer(fakeVtable);
|
||||
appAttachMessageAddr.add(0x08).writeU32(taskIdGlobal);
|
||||
appAttachMessageAddr.add(0x0c).writeU32(0x6e);
|
||||
appAttachMessageAddr.add(0x10).writeU64(0x3);
|
||||
appAttachMessageAddr.add(0x18).writePointer(appAttachCgiAddr);
|
||||
appAttachMessageAddr.add(0x20).writeU64(0x25);
|
||||
appAttachMessageAddr.add(0x28).writeU64(uint64("0x8000000000000030"));
|
||||
appAttachMessageAddr.add(0x30).writeU64(uint64("0x0000000001010100"));
|
||||
}
|
||||
|
||||
function triggerUploadAppAttach(taskId, sender, receiver, protoHex, payloadHex) {
|
||||
return triggerSendMediaMessage(taskId, sender, receiver, protoHex, payloadHex, "appattach");
|
||||
}
|
||||
|
||||
// -------------------------发送文件消息分区-------------------------
|
||||
|
||||
|
||||
@@ -502,6 +539,8 @@ function attachBlrX8Hook() {
|
||||
protoHex = fileProtoHexGlobal;
|
||||
} else if (sendMsgType === "fileupload") {
|
||||
protoHex = fileUploadProtoHexGlobal;
|
||||
} else if (sendMsgType === "appattach") {
|
||||
protoHex = appAttachProtoHexGlobal;
|
||||
} else if (sendMsgType === "voice") {
|
||||
protoHex = voiceProtoHexGlobal;
|
||||
}
|
||||
@@ -587,6 +626,10 @@ function attachReq2buf() {
|
||||
insertMsgAddr.writePointer(sendFileUploadMessageAddr);
|
||||
console.log("[+] 发送fileUploadMsg成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendFileUploadMessageAddr +
|
||||
"[+] Req2Buf 写入后内存预览: " + insertMsgAddr);
|
||||
} else if (sendMsgType === "appattach") {
|
||||
insertMsgAddr.writePointer(sendAppAttachMessageAddr);
|
||||
console.log("[+] 发送uploadAppAttach成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendAppAttachMessageAddr +
|
||||
"[+] Req2Buf 写入后内存预览: " + insertMsgAddr);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -723,6 +766,7 @@ function triggerSendMediaMessage(taskId, sender, receiver, protoHex, payloadHex,
|
||||
"voice": { messageAddr: voiceMessageAddr, sendMessageAddr: sendVoiceMessageAddr, cgiAddr: voiceCgiAddr, protoHexSetter: function(h) { voiceProtoHexGlobal = h; } },
|
||||
"file": { messageAddr: fileMessageAddr, sendMessageAddr: sendFileMessageAddr, cgiAddr: fileCgiAddr, protoHexSetter: function(h) { fileProtoHexGlobal = h; } },
|
||||
"fileupload": { messageAddr: fileUploadMessageAddr, sendMessageAddr: sendFileUploadMessageAddr, cgiAddr: fileUploadCgiAddr, protoHexSetter: function(h) { fileUploadProtoHexGlobal = h; } },
|
||||
"appattach": { messageAddr: appAttachMessageAddr, sendMessageAddr: sendAppAttachMessageAddr, cgiAddr: appAttachCgiAddr, protoHexSetter: function(h) { appAttachProtoHexGlobal = h; } },
|
||||
};
|
||||
|
||||
var info = msgAddrInfo[msgType];
|
||||
@@ -998,6 +1042,7 @@ rpc.exports = {
|
||||
triggerSendFileMessage: triggerSendFileMessage,
|
||||
triggerSendFileUploadMessage: triggerSendFileUploadMessage,
|
||||
triggerUploadFile: triggerUploadFile,
|
||||
triggerUploadAppAttach: triggerUploadAppAttach,
|
||||
};
|
||||
|
||||
// -------------------------发送图片消息分区-------------------------
|
||||
|
||||
@@ -141,6 +141,11 @@ func BuildSendPayload(taskId int64, msgType string) string {
|
||||
payloadData[16] = 0x10
|
||||
payloadData[28] = 0x27
|
||||
payloadData[92] = 0x6E
|
||||
case "appattach":
|
||||
payloadData[0] = 0x6E
|
||||
payloadData[16] = 0x10
|
||||
payloadData[28] = 0x25
|
||||
payloadData[92] = 0x6E
|
||||
case "voice":
|
||||
payloadData[0] = 0x6E
|
||||
payloadData[16] = 0x10
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
wxproto "github.com/yincongcyincong/weixin-macos/onebot/proto"
|
||||
)
|
||||
|
||||
// uploadAppAttachChunkSize 每个分片大小 (参考 we-chat-ipad860 的 50000 字节)
|
||||
const uploadAppAttachChunkSize = 50000
|
||||
|
||||
// BuildUploadAppAttachChunks 读取文件并按分片构造 uploadappattach 请求的 protobuf hex 列表。
|
||||
// 返回: 每个分片的 protobuf hex、填充好基础信息的 FileInfo、错误。
|
||||
// 参考 we-chat-ipad860 models/Tools/UploadApp.go 的 SendUploadAppAttach。
|
||||
func BuildUploadAppAttachChunks(receiver, filePath string) ([]string, *FileInfo, error) {
|
||||
fileData, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("读取文件失败: %w", err)
|
||||
}
|
||||
|
||||
total := len(fileData)
|
||||
if total == 0 {
|
||||
return nil, nil, fmt.Errorf("文件为空: %s", filePath)
|
||||
}
|
||||
|
||||
sum := md5.Sum(fileData)
|
||||
fileMd5 := hex.EncodeToString(sum[:])
|
||||
|
||||
// clientAppDataId: {receiver}_{ts}_UploadFile (对齐 iPad860)
|
||||
clientAppDataId := fmt.Sprintf("%s_%d_UploadFile", receiver, time.Now().Unix())
|
||||
|
||||
fileInfo := &FileInfo{
|
||||
FileName: extractFileName(filePath),
|
||||
TotalLen: int64(total),
|
||||
FileExt: getFileExt(filePath),
|
||||
Md5: fileMd5,
|
||||
ClientMsgId: clientAppDataId,
|
||||
}
|
||||
|
||||
var hexChunks []string
|
||||
for startPos := 0; startPos < total; startPos += uploadAppAttachChunkSize {
|
||||
end := startPos + uploadAppAttachChunkSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
chunk := fileData[startPos:end]
|
||||
|
||||
hexStr, err := buildUploadAppAttachChunk(receiver, clientAppDataId, fileMd5,
|
||||
uint32(total), uint32(startPos), chunk)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hexChunks = append(hexChunks, hexStr)
|
||||
}
|
||||
|
||||
return hexChunks, fileInfo, nil
|
||||
}
|
||||
|
||||
// buildUploadAppAttachChunk 构造单个分片的 UploadAppAttachRequest protobuf hex。
|
||||
func buildUploadAppAttachChunk(receiver, clientAppDataId, fileMd5 string,
|
||||
totalLen, startPos uint32, chunk []byte) (string, error) {
|
||||
|
||||
version := NextVersion()
|
||||
appId := ""
|
||||
sdkVersion := uint32(0)
|
||||
dataLen := uint32(len(chunk))
|
||||
fileType := uint32(6) // 6 = 文件
|
||||
|
||||
req := &wxproto.UploadAppAttachRequest{
|
||||
BaseRequest: &wxproto.ReplyMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: &globalSessionId,
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: &globalDeviceId,
|
||||
Platform: proto.String("UnifiedPCMac 26 arm64"),
|
||||
Version: &version,
|
||||
},
|
||||
AppId: &appId,
|
||||
SdkVersion: &sdkVersion,
|
||||
ClientAppDataId: &clientAppDataId,
|
||||
UserName: &receiver,
|
||||
TotalLen: &totalLen,
|
||||
StartPos: &startPos,
|
||||
DataLen: &dataLen,
|
||||
Data: &wxproto.SKBuiltinBufferT{
|
||||
ILen: &dataLen,
|
||||
Buffer: chunk,
|
||||
},
|
||||
Type: &fileType,
|
||||
Md5: &fileMd5,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal uploadappattach chunk failed: %w", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("[uploadappattach-proto] startPos=%d dataLen=%d totalLen=%d len=%d\n%s\n",
|
||||
// startPos, dataLen, totalLen, len(data), HexDump(data, 0))
|
||||
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
// ParseUploadAppAttachResponse 解析 uploadappattach 响应,提取 mediaId(attachid)。
|
||||
func ParseUploadAppAttachResponse(data []byte) (string, error) {
|
||||
// fmt.Printf("[uploadappattach-resp] len=%d\n%s\n", len(data), HexDump(data, 0))
|
||||
|
||||
resp := &wxproto.UploadAppAttachResponse{}
|
||||
if err := proto.Unmarshal(data, resp); err != nil {
|
||||
return "", fmt.Errorf("unmarshal uploadappattach response failed: %w", err)
|
||||
}
|
||||
|
||||
if resp.BaseResponse != nil && resp.BaseResponse.Ret != nil && *resp.BaseResponse.Ret != 0 {
|
||||
errMsg := ""
|
||||
if resp.BaseResponse.ErrMsg != nil {
|
||||
errMsg = resp.BaseResponse.ErrMsg.GetMsg()
|
||||
}
|
||||
return "", fmt.Errorf("uploadappattach failed, ret=%d, errMsg=%s",
|
||||
*resp.BaseResponse.Ret, errMsg)
|
||||
}
|
||||
|
||||
return resp.GetMediaId(), nil
|
||||
}
|
||||
|
||||
// BuildSimpleFileMsgProto 构建发送文件消息的 protobuf(uploadappattach 直传后用)。
|
||||
// 严格按 wechat7016 WXSendMsgFile / SendAppMsgRequest 格式:
|
||||
// - 顶层 SendAppMsgReq{baseRequest, msg=AppMsgBody{...}}
|
||||
// - AppMsgBody 只填 fromUserName/toUserName/type=6/content(XML)/clientMsgId/createTime
|
||||
// - appmsg XML 极简:只有 title/type/appattach(totallen/attachid/fileext),不含 cdnattachurl
|
||||
func BuildSimpleFileMsgProto(sender, receiver string, fileInfo *FileInfo) (string, error) {
|
||||
now := time.Now().Unix()
|
||||
|
||||
// wechat7016 WXSendMsgFile 的极简 XML(attachid 用完整 mediaId,不加 cdnattachurl)
|
||||
xml := `<?xml version="1.0"?>` + "\n"
|
||||
xml += `<appmsg appid='' sdkver=''>`
|
||||
xml += `<title>` + escapeXmlStr(fileInfo.FileName) + `</title>`
|
||||
xml += `<des></des>`
|
||||
xml += `<action></action>`
|
||||
xml += `<type>6</type>`
|
||||
xml += `<content></content>`
|
||||
xml += `<url></url>`
|
||||
xml += `<lowurl></lowurl>`
|
||||
xml += `<appattach>`
|
||||
xml += `<totallen>` + fmt.Sprintf("%d", fileInfo.TotalLen) + `</totallen>`
|
||||
xml += `<attachid>` + escapeXmlStr(fileInfo.AttachId) + `</attachid>`
|
||||
xml += `<fileext>` + escapeXmlStr(fileInfo.FileExt) + `</fileext>`
|
||||
xml += `</appattach>`
|
||||
xml += `<extinfo></extinfo>`
|
||||
xml += `</appmsg>`
|
||||
|
||||
version := NextVersion()
|
||||
appId := ""
|
||||
sdkVersion := uint32(0)
|
||||
msgType := uint32(6) // 6 = 文件
|
||||
clientMsgId := fmt.Sprintf("%d", now)
|
||||
msgSource := "<msgsource><alnode><fr>1</fr><cf>2</cf></alnode></msgsource>"
|
||||
|
||||
req := &wxproto.SendAppMsgReq{
|
||||
BaseRequest: &wxproto.ReplyMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: &globalSessionId,
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: &globalDeviceId,
|
||||
Platform: proto.String("UnifiedPCMac 26 arm64"),
|
||||
Version: &version,
|
||||
},
|
||||
Msg: &wxproto.AppMsgBody{
|
||||
FromUserName: &sender,
|
||||
AppId: &appId,
|
||||
SdkVersion: &sdkVersion,
|
||||
ToUserName: &receiver,
|
||||
Type: &msgType,
|
||||
Content: &xml,
|
||||
CreateTime: proto.Int64(now),
|
||||
ClientMsgId: &clientMsgId,
|
||||
MsgSource: &msgSource,
|
||||
},
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal SendAppMsgReq failed: %w", err)
|
||||
}
|
||||
|
||||
// fmt.Printf("[simple-file-proto] xml=%s\n[simple-file-proto] hex dump:\n%s\n", xml, HexDump(data, 0))
|
||||
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
@@ -119,3 +119,117 @@ func BuildUploadPayload(uploadType string) string {
|
||||
|
||||
return hex.EncodeToString(payload)
|
||||
}
|
||||
|
||||
// BuildVoiceUploadPayload 构建语音上传的payload模板,返回hex编码字符串
|
||||
// 基于WeChat实际内存dump校正
|
||||
// JS侧会在固定偏移写入运行时指针: 0x00, 0x08, 0x48(voiceIdAddr), 0x50(voiceId长度), 0x58(voiceId容量), 0x68(receiver), 0x100(audioData), 0x108(audioLen), 0x110(audioCap)
|
||||
func BuildVoiceUploadPayload() string {
|
||||
payload := []byte{
|
||||
// 0x00 (被uploadFunc1Addr覆盖)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x08 (被uploadFunc2Addr覆盖)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x10
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x18
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x20
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x28
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x30
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x38
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x40
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x48 (被voiceIdAddr覆盖)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x50 voiceId length (JS实时写入)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x58 voiceId capacity (JS实时写入)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
// 0x60
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x68 (被receiver覆盖, inline string区域)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x70
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x78 [0x7F]=receiver length
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
|
||||
// 0x80
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x88
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x90
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x98 [0x9C]=0x0F voice type
|
||||
0x01, 0xAA, 0xAA, 0xAA, 0x0F, 0x00, 0x00, 0x00,
|
||||
// 0xA0
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0xA8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xB0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xB8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xC0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xC8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xD0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xD8 音频参数
|
||||
0x10, 0x0E, 0x00, 0x00, 0x08, 0x07, 0x00, 0x00,
|
||||
// 0xE0 文件大小(-1=未知)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
// 0xE8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xF0
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xF8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x100 (被voiceAudioDataAddr覆盖 - 音频数据指针)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x108 音频数据长度 (JS写入)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x110 音频数据容量 (JS写入, 带high bit)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
// 0x118
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x120
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x128
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x130
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x138
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x140
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x148
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x150
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x158
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x160
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x168
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x170 [0x174]=0x01
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x178
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x180
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x188
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x190
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x198
|
||||
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
return hex.EncodeToString(payload)
|
||||
}
|
||||
|
||||
+193
@@ -61,6 +61,199 @@ func SaveBase64Image(base64Data string) (string, string, error) {
|
||||
return targetPath, md5Str, nil
|
||||
}
|
||||
|
||||
// SaveVoiceFile 解码base64音频数据并保存为文件(不追加salt,保持二进制完整性)
|
||||
// 返回原始字节、文件路径、错误
|
||||
func SaveVoiceFile(base64Data string) ([]byte, string, error) {
|
||||
rawContents := base64Data
|
||||
if strings.HasPrefix(base64Data, "base64://") {
|
||||
rawContents = strings.TrimPrefix(base64Data, "base64://")
|
||||
} else if idx := strings.Index(base64Data, ","); idx != -1 {
|
||||
rawContents = base64Data[idx+1:]
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(rawContents)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("base64 decode failed: %v", err)
|
||||
}
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000)
|
||||
timestamp := time.Now().Unix()
|
||||
ext := DetectFileFormat(data)
|
||||
fileName := fmt.Sprintf("%d_%d.%s", randomNumber, timestamp, ext)
|
||||
targetPath := config.ImagePath + fileName
|
||||
dir := filepath.Dir(targetPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, "", fmt.Errorf("create directory failed: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(targetPath, data, 0666)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("write file failed: %v", err)
|
||||
}
|
||||
os.Chmod(targetPath, 0666)
|
||||
|
||||
return data, targetPath, nil
|
||||
}
|
||||
|
||||
// SaveBase64File 解码 base64 数据并以指定扩展名保存文件,返回文件路径和 MD5
|
||||
func SaveBase64File(base64Data string, ext string) (string, string, error) {
|
||||
rawContents := base64Data
|
||||
if strings.HasPrefix(base64Data, "base64://") {
|
||||
rawContents = strings.TrimPrefix(base64Data, "base64://")
|
||||
} else if idx := strings.Index(base64Data, ","); idx != -1 {
|
||||
rawContents = base64Data[idx+1:]
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(rawContents)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("base64 decode failed: %v", err)
|
||||
}
|
||||
|
||||
// 如果没有传入扩展名,尝试自动检测
|
||||
if ext == "" {
|
||||
ext = DetectFileFormat(data)
|
||||
if ext == "unknown" {
|
||||
// fallback: 用 MIME 类型推断
|
||||
mimeType := http.DetectContentType(data)
|
||||
ext = mimeToExt(mimeType)
|
||||
}
|
||||
}
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000)
|
||||
timestamp := time.Now().Unix()
|
||||
fileName := fmt.Sprintf("%d_%d.%s", randomNumber, timestamp, ext)
|
||||
targetPath := config.ImagePath + fileName
|
||||
dir := filepath.Dir(targetPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", "", fmt.Errorf("create directory failed: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(targetPath, data, 0666)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("write file failed: %v", err)
|
||||
}
|
||||
os.Chmod(targetPath, 0666)
|
||||
|
||||
md5Str, err := GetFileMD5(targetPath)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("get file md5 failed: %v", err)
|
||||
}
|
||||
|
||||
return targetPath, md5Str, nil
|
||||
}
|
||||
|
||||
// mimeToExt 将 MIME 类型转换为文件扩展名
|
||||
func mimeToExt(mimeType string) string {
|
||||
if idx := strings.Index(mimeType, ";"); idx != -1 {
|
||||
mimeType = strings.TrimSpace(mimeType[:idx])
|
||||
}
|
||||
switch mimeType {
|
||||
case "text/plain":
|
||||
return "txt"
|
||||
case "text/html":
|
||||
return "html"
|
||||
case "text/xml", "application/xml":
|
||||
return "xml"
|
||||
case "application/json":
|
||||
return "json"
|
||||
case "application/pdf":
|
||||
return "pdf"
|
||||
case "application/zip":
|
||||
return "zip"
|
||||
case "application/gzip":
|
||||
return "gz"
|
||||
case "image/jpeg":
|
||||
return "jpg"
|
||||
case "image/png":
|
||||
return "png"
|
||||
case "image/gif":
|
||||
return "gif"
|
||||
case "image/webp":
|
||||
return "webp"
|
||||
case "video/mp4":
|
||||
return "mp4"
|
||||
case "audio/mpeg":
|
||||
return "mp3"
|
||||
default:
|
||||
return "bin"
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToSilk 将音频数据(任意格式)通过ffmpeg转为PCM,再编码为SILK格式
|
||||
// 微信要求格式: \x02#!SILK_V3 开头 (tencent silk)
|
||||
// 如果输入已经是该格式,则直接返回
|
||||
// 返回: silkData, 时长(毫秒), error
|
||||
func ConvertToSilk(audioData []byte) ([]byte, int32, error) {
|
||||
// 已经是tencent SILK格式 (\x02#!SILK_V3),直接返回,时长未知设为0
|
||||
if len(audioData) > 10 && audioData[0] == 0x02 && bytes.HasPrefix(audioData[1:], []byte("#!SILK_V3")) {
|
||||
return audioData, 0, nil
|
||||
}
|
||||
|
||||
// 先用ffmpeg将输入音频转为PCM (s16le, 16000Hz, mono)
|
||||
cmd := exec.Command("ffmpeg",
|
||||
"-i", "pipe:0",
|
||||
"-f", "s16le",
|
||||
"-ar", "16000",
|
||||
"-ac", "1",
|
||||
"pipe:1",
|
||||
)
|
||||
cmd.Stdin = bytes.NewReader(audioData)
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, 0, fmt.Errorf("ffmpeg to pcm error: %v, details: %s", err, stderr.String())
|
||||
}
|
||||
|
||||
pcmBytes := out.Bytes()
|
||||
// 时长(ms) = pcm字节数 * 1000 / (采样率 * 通道数 * 每样本字节数)
|
||||
durationMs := int32(int64(len(pcmBytes)) * 1000 / (16000 * 2))
|
||||
|
||||
// 尝试使用外部silk-encoder(和微信兼容性更好)
|
||||
silkData, err := encodeSilkExternal(pcmBytes)
|
||||
if err != nil {
|
||||
// fallback: 使用go-silk库
|
||||
silkData, err = silk.EncodePcmBuffToSilk(pcmBytes, 16000, 16000, true)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("encode silk error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return silkData, durationMs, nil
|
||||
}
|
||||
|
||||
// encodeSilkExternal 使用外部pilk(Python)工具编码pcm->silk(和微信兼容)
|
||||
func encodeSilkExternal(pcmBytes []byte) ([]byte, error) {
|
||||
tmpPcm, err := os.CreateTemp("", "voice_*.pcm")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.Remove(tmpPcm.Name())
|
||||
|
||||
if _, err := tmpPcm.Write(pcmBytes); err != nil {
|
||||
tmpPcm.Close()
|
||||
return nil, err
|
||||
}
|
||||
tmpPcm.Close()
|
||||
|
||||
tmpSilk := tmpPcm.Name() + ".silk"
|
||||
defer os.Remove(tmpSilk)
|
||||
|
||||
pyScript := fmt.Sprintf(`import pilk; pilk.encode("%s", "%s", pcm_rate=16000, tencent=True)`, tmpPcm.Name(), tmpSilk)
|
||||
cmd := exec.Command("python3", "-c", pyScript)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("pilk encode failed: %v, %s", err, stderr.String())
|
||||
}
|
||||
return os.ReadFile(tmpSilk)
|
||||
}
|
||||
|
||||
// GetVideoDuration 使用ffprobe获取视频时长(秒)
|
||||
func GetVideoDuration(filePath string) (int32, error) {
|
||||
cmd := exec.Command("ffprobe",
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
wxproto "github.com/yincongcyincong/weixin-macos/onebot/proto"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// BuildVoiceMsgProto 构建发送语音消息的protobuf并返回hex编码的字符串
|
||||
func BuildVoiceMsgProto(sender, targetId, cdnKey, aesKey string, voiceDuration int32, silkDataLen int32, unknown13 int32) (string, error) {
|
||||
// client_msg_id: UUID格式
|
||||
clientMsgId := uuid.New().String()
|
||||
|
||||
msg := &wxproto.WxSendVoiceMsg{
|
||||
FromUser: sender,
|
||||
ToUser: targetId,
|
||||
Unknown3: 0,
|
||||
Unknown4: silkDataLen,
|
||||
ClientMsgId: clientMsgId,
|
||||
Duration: voiceDuration,
|
||||
Unknown9: 1,
|
||||
Header: &wxproto.VoiceMsgHeader{
|
||||
Flag: []byte{0x00},
|
||||
SessionId: int64(globalSessionId),
|
||||
ClientProof: globalClientProof,
|
||||
DeviceId: int64(globalDeviceId),
|
||||
Platform: []byte("UnifiedPCMac 26 arm64"),
|
||||
Version: int32(NextVersion()),
|
||||
Unknown8: 4,
|
||||
Unknown15: 0,
|
||||
},
|
||||
Unknown11: 0,
|
||||
Unknown13: 4,
|
||||
Unknown15: 0,
|
||||
Unknown16: 0,
|
||||
Unknown17: time.Now().Unix(),
|
||||
CdnKey: []byte(cdnKey),
|
||||
AesKey: []byte(aesKey),
|
||||
Unknown24: []byte{},
|
||||
Unknown25: 0,
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal voice proto failed: %w", err)
|
||||
}
|
||||
|
||||
// 手动追加proto3不会序列化的0值字段
|
||||
// field 3 (unknown3=0)
|
||||
data = appendZeroVarintField(data, 3)
|
||||
// field 11 (unknown11=0)
|
||||
data = appendZeroVarintField(data, 11)
|
||||
// field 15 (unknown15=0)
|
||||
data = appendZeroVarintField(data, 15)
|
||||
// field 16 (unknown16=0)
|
||||
data = appendZeroVarintField(data, 16)
|
||||
// field 25 (unknown25=0)
|
||||
data = appendZeroVarintField(data, 25)
|
||||
|
||||
//fmt.Printf("[voice-proto] final protobuf hex dump:\n%s\n", HexDump(data, 0))
|
||||
|
||||
return hex.EncodeToString(data), nil
|
||||
}
|
||||
@@ -195,6 +195,126 @@ func SendWechatMsg(m *SendMsg) {
|
||||
sendErr = errors.New("send reply failed")
|
||||
return
|
||||
}
|
||||
case "voice":
|
||||
// 直接base64解码,不追加salt(音频二进制不能被修改)
|
||||
rawAudio, targetPath, err := SaveVoiceFile(m.Content)
|
||||
if err != nil {
|
||||
Error("保存语音文件失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为SILK格式
|
||||
silkData, voiceDurationMs, err := ConvertToSilk(rawAudio)
|
||||
if err != nil {
|
||||
Error("转换SILK格式失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
|
||||
audioHex := hex.EncodeToString(silkData)
|
||||
|
||||
uploadPayloadHex := BuildVoiceUploadPayload()
|
||||
result := fridaScript.ExportsCall("triggerUploadVoice", targetId, targetPath, uploadPayloadHex, audioHex, voiceDurationMs)
|
||||
Info("📩 上传语音任务执行结果", "result", result, "target_id", targetId, "path", targetPath, "silk_len", len(silkData), "duration_ms", voiceDurationMs)
|
||||
if result != "0" {
|
||||
Error("上传语音失败", "target_id", targetId, "result", result)
|
||||
sendErr = errors.New("upload voice failed")
|
||||
return
|
||||
}
|
||||
if m.ResultChan != nil {
|
||||
pendingResultMap.Store(targetId, m.ResultChan)
|
||||
m.ResultChan = nil
|
||||
}
|
||||
return
|
||||
case "send_voice":
|
||||
protoHex, err := BuildVoiceMsgProto(myWechatId, targetId, m.CdnKey, m.AesKey, m.VoiceDuration, m.SilkDataLen, m.Unknown13)
|
||||
if err != nil {
|
||||
Error("构建语音protobuf失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
payloadHex := BuildSendPayload(currTaskId, "voice")
|
||||
result := fridaScript.ExportsCall("triggerSendVoiceMessage", currTaskId, myWechatId, targetId, protoHex, payloadHex)
|
||||
Info("📩 发送语音任务执行结果", "result", result, "task_id", currTaskId, "wechat_id", myWechatId, "target_id", targetId, "unknown13", m.Unknown13)
|
||||
if result != "1" {
|
||||
Error("发送语音失败", "task_id", currTaskId, "target_id", targetId, "result", result)
|
||||
sendErr = errors.New("send voice failed")
|
||||
return
|
||||
}
|
||||
case "send_file_simple":
|
||||
// iPad860 风格: uploadappattach 分片直传 → sendappmsg,不走 CDN。
|
||||
// 文件名/扩展名由内容自动识别 + 时间戳随机生成(SaveBase64File 内部完成)。
|
||||
targetPath, _, err := SaveBase64File(m.Content, "")
|
||||
if err != nil {
|
||||
Error("保存文件失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
|
||||
chunks, fileInfo, err := BuildUploadAppAttachChunks(targetId, targetPath)
|
||||
if err != nil {
|
||||
Error("构建uploadappattach分片失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
Info("📩 开始uploadappattach直传", "target_id", targetId, "chunks", len(chunks),
|
||||
"file_name", fileInfo.FileName, "file_ext", fileInfo.FileExt,
|
||||
"total_len", fileInfo.TotalLen, "md5", fileInfo.Md5)
|
||||
|
||||
var attachId string
|
||||
for i, chunkHex := range chunks {
|
||||
chunkTaskId := atomic.AddInt64(&taskId, 1)
|
||||
payloadHex := BuildSendPayload(chunkTaskId, "appattach")
|
||||
result := fridaScript.ExportsCall("triggerUploadAppAttach", chunkTaskId, myWechatId, targetId, chunkHex, payloadHex)
|
||||
if result != "1" {
|
||||
Error("uploadappattach分片发送失败", "chunk", i, "result", result)
|
||||
sendErr = errors.New("upload app attach chunk failed")
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
Error("等待uploadappattach响应超时", "chunk", i)
|
||||
sendErr = errors.New("upload app attach timeout")
|
||||
return
|
||||
case data := <-appAttachRespChan:
|
||||
id, perr := ParseUploadAppAttachResponse(data)
|
||||
if perr != nil {
|
||||
Error("解析uploadappattach响应失败", "chunk", i, "err", perr)
|
||||
sendErr = perr
|
||||
return
|
||||
}
|
||||
if id != "" {
|
||||
attachId = id
|
||||
}
|
||||
Info("📩 uploadappattach分片完成", "chunk", i, "attach_id", id)
|
||||
}
|
||||
}
|
||||
|
||||
if attachId == "" {
|
||||
Error("uploadappattach未返回attachId", "target_id", targetId)
|
||||
sendErr = errors.New("upload app attach no attachId")
|
||||
return
|
||||
}
|
||||
fileInfo.AttachId = attachId
|
||||
|
||||
// sendappmsg (type=6),精简版 appmsg,cdnattachurl 也填 attachId
|
||||
currTaskId = atomic.AddInt64(&taskId, 1)
|
||||
protoHex, err := BuildSimpleFileMsgProto(myWechatId, targetId, fileInfo)
|
||||
if err != nil {
|
||||
Error("构建文件protobuf失败", "err", err)
|
||||
sendErr = err
|
||||
return
|
||||
}
|
||||
payloadHex := BuildSendPayload(currTaskId, "file")
|
||||
result := fridaScript.ExportsCall("triggerSendFileMessage", currTaskId, myWechatId, targetId, protoHex, payloadHex)
|
||||
Info("📩 发送文件消息(simple)执行结果", "result", result, "task_id", currTaskId, "target_id", targetId)
|
||||
if result != "1" {
|
||||
Error("发送文件失败(simple)", "task_id", currTaskId, "target_id", targetId, "result", result)
|
||||
sendErr = errors.New("send file failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -383,6 +503,13 @@ func HandleBuf2Resp(msgType string, data []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
case "appattach":
|
||||
Info("收到uploadappattach响应", "data_len", len(data))
|
||||
appAttachRespChan <- data
|
||||
return
|
||||
}
|
||||
|
||||
ret, errMsg, err := ParseSendMsgResponse(data)
|
||||
if err != nil {
|
||||
Info("buf2resp响应无法提取错误码,视为成功", "msg_type", msgType, "err", err)
|
||||
|
||||
Reference in New Issue
Block a user