增加大文件上传支持

This commit is contained in:
eatMoreApple 2021-04-29 18:44:06 +08:00
parent 5ff43c4cea
commit fac0ede488
6 changed files with 254 additions and 79 deletions

View File

@ -1,6 +1,7 @@
package openwechat package openwechat
import ( import (
"os"
"testing" "testing"
"time" "time"
) )
@ -238,8 +239,8 @@ func TestHotLogin(t *testing.T) {
} }
func TestFriendHelper(t *testing.T) { func TestFriendHelper(t *testing.T) {
bot := defaultBot(Desktop) bot := defaultBot()
if err := bot.Login(); err != nil { if err := bot.HotLogin(NewJsonFileHotReloadStorage("2.json"), true); err != nil {
t.Error(err) t.Error(err)
return return
} }
@ -253,7 +254,11 @@ func TestFriendHelper(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
msg, err := fh.SendText("test message") f, _ := os.Open("webwxgetmsgimg.jpeg")
//f, _ := os.Open("2.jpeg")
defer f.Close()
msg, err := fh.SendImage(f)
//msg, err := fh.SendText("hh")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -263,7 +268,7 @@ func TestFriendHelper(t *testing.T) {
func TestRevokeMessage(t *testing.T) { func TestRevokeMessage(t *testing.T) {
bot := defaultBot(Desktop) bot := defaultBot(Desktop)
if err := bot.Login(); err != nil { if err := bot.HotLogin(NewJsonFileHotReloadStorage("2.json")); err != nil {
t.Error(err) t.Error(err)
return return
} }

View File

@ -2,6 +2,7 @@ package openwechat
import ( import (
"errors" "errors"
"fmt"
"os" "os"
) )
@ -203,29 +204,45 @@ func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string)
// 发送图片消息接口 // 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) { func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
// 首先尝试上传图片 // 首先尝试上传图片
resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic")) sate, err := file.Stat()
if err != nil {
return nil, err
}
var resp *ReturnResponse
if sate.Size() <= chunkSize {
resp = NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "pic"))
} else {
resp = NewReturnResponse(c.Client.WebWxUploadMediaByChunk(file, request, info, fromUserName, toUserName, "pic"))
}
// 无错误上传成功之后获取请求结果,判断结果是否正常 // 无错误上传成功之后获取请求结果,判断结果是否正常
if resp.Err() != nil { if resp.Err() != nil {
return nil, resp.Err() return nil, resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
data, _ := resp.ReadAll()
fmt.Println(string(data))
var item struct { var item struct {
BaseResponse BaseResponse BaseResponse BaseResponse
MediaId string MediaId string
} }
if err := resp.ScanJSON(&item); err != nil {
if err = resp.ScanJSON(&item); err != nil {
return nil, err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return nil, item.BaseResponse return nil, item.BaseResponse
} }
if len(item.MediaId) == 0 {
return nil, errors.New("upload failed")
}
// 构造新的图片类型的信息 // 构造新的图片类型的信息
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId) msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
// 发送图片信息 // 发送图片信息
resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info)) resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
sendSuccessMsg := &SentMessage{SendMessage: msg} sendSuccessMsg := &SentMessage{SendMessage: msg}
err := parseMessageResponseError(resp, sendSuccessMsg) err = parseMessageResponseError(resp, sendSuccessMsg)
return sendSuccessMsg, err return sendSuccessMsg, err
} }

144
client.go
View File

@ -46,6 +46,7 @@ func DefaultClient(urlManager UrlManager) *Client {
// 抽象Do方法,将所有的有效的cookie存入Client.cookies // 抽象Do方法,将所有的有效的cookie存入Client.cookies
// 方便热登陆时获取 // 方便热登陆时获取
func (c *Client) Do(req *http.Request) (*http.Response, error) { func (c *Client) Do(req *http.Request) (*http.Response, error) {
req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36")
resp, err := c.Client.Do(req) resp, err := c.Client.Do(req)
if err != nil { if err != nil {
return resp, err return resp, err
@ -251,7 +252,12 @@ func (c *Client) WebWxGetHeadImg(headImageUrl string) (*http.Response, error) {
} }
// 上传文件 // 上传文件
func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, contentType, mediaType string) (*http.Response, error) { func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) {
// 获取文件上传的类型
contentType, err := GetFileContentType(file)
if err != nil {
return nil, err
}
path, _ := url.Parse(c.webWxUpLoadMediaUrl) path, _ := url.Parse(c.webWxUpLoadMediaUrl)
params := url.Values{} params := url.Values{}
params.Add("f", "json") params.Add("f", "json")
@ -260,13 +266,21 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log
if err != nil { if err != nil {
return nil, err return nil, err
} }
cookies := c.Jar.Cookies(path)
webWxDataTicket := getWebWxDataTicket(cookies)
// 文件复位
if _, err = file.Seek(0, 0); err != nil {
return nil, err
}
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(file); err != nil { if _, err := buffer.ReadFrom(file); err != nil {
return nil, err return nil, err
} }
data := buffer.Bytes() data := buffer.Bytes()
fileMd5 := fmt.Sprintf("%x", md5.Sum(data)) fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
cookies := c.Jar.Cookies(path)
uploadMediaRequest := map[string]interface{}{ uploadMediaRequest := map[string]interface{}{
"UploadType": 2, "UploadType": 2,
"BaseRequest": request, "BaseRequest": request,
@ -283,14 +297,15 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log
if err != nil { if err != nil {
return nil, err return nil, err
} }
content := map[string]interface{}{ content := map[string]interface{}{
"id": "WU_FILE_0", "id": "WU_FILE_0",
"name": file.Name(), "name": file.Name(),
"type": contentType, "type": contentType,
"lastModifiedDate": time.Now().Format(http.TimeFormat), "lastModifiedDate": sate.ModTime().Format(TimeFormat),
"size": sate.Size(), "size": sate.Size(),
"mediatype": mediaType, "mediatype": mediaType,
"webwx_data_ticket": getWebWxDataTicket(cookies), "webwx_data_ticket": webWxDataTicket,
"pass_ticket": info.PassTicket, "pass_ticket": info.PassTicket,
} }
body, err := ToBuffer(content) body, err := ToBuffer(content)
@ -313,10 +328,131 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log
return nil, err return nil, err
} }
req, _ := http.NewRequest(http.MethodPost, path.String(), body) req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Set("Content-Type", ct) req.Header.Set("Content-Type", ct)
return c.Do(req) return c.Do(req)
} }
func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) {
// 获取文件上传的类型
contentType, err := GetFileContentType(file)
if err != nil {
return nil, err
}
path, _ := url.Parse(c.webWxUpLoadMediaUrl)
params := url.Values{}
params.Add("f", "json")
path.RawQuery = params.Encode()
sate, err := file.Stat()
if err != nil {
return nil, err
}
cookies := c.Jar.Cookies(path)
webWxDataTicket := getWebWxDataTicket(cookies)
// 将文件的游标复原到原点
// 上面获取文件的类型的时候已经读取了512个字节
if _, err = file.Seek(0, 0); err != nil {
return nil, err
}
buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(file); err != nil {
return nil, err
}
data := buffer.Bytes()
fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
uploadMediaRequest := map[string]interface{}{
"UploadType": 2,
"BaseRequest": request,
"ClientMediaId": time.Now().Unix() * 1e4,
"TotalLen": sate.Size(),
"StartPos": 0,
"DataLen": sate.Size(),
"MediaType": 4,
"FromUserName": forUserName,
"ToUserName": toUserName,
"FileMd5": fileMd5,
}
uploadMediaRequestByte, err := json.Marshal(uploadMediaRequest)
if err != nil {
return nil, err
}
chunks := sate.Size() / chunkSize
if chunks*chunkSize < sate.Size() {
chunks++
}
var resp *http.Response
for chunk := 0; int64(chunk) < chunks; chunk++ {
content := map[string]interface{}{
"id": "WU_FILE_0",
"name": file.Name(),
"type": contentType,
"lastModifiedDate": sate.ModTime().Format(TimeFormat),
"size": sate.Size(),
"mediatype": mediaType,
"webwx_data_ticket": webWxDataTicket,
"pass_ticket": info.PassTicket,
"chunks": chunks,
"chunk": chunk,
}
body, err := ToBuffer(content)
if err != nil {
return nil, err
}
writer := multipart.NewWriter(body)
if err = writer.WriteField("uploadmediarequest", string(uploadMediaRequestByte)); err != nil {
return nil, err
}
var isLastTime bool
if int64(chunk)+1 == chunks {
isLastTime = true
}
if w, err := writer.CreateFormFile("filename", file.Name()); err != nil {
return nil, err
} else {
var chunkData []byte
// 判断是不是最后一次
if !isLastTime {
chunkData = data[int64(chunk)*chunkSize : (int64(chunk)+1)*chunkSize]
} else {
chunkData = data[int64(chunk)*chunkSize:]
}
if _, err = w.Write(chunkData); err != nil {
return nil, err
}
}
ct := writer.FormDataContentType()
if err = writer.Close(); err != nil {
return nil, err
}
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Set("Content-Type", ct)
//req.Header.Add("Referer", c.baseUrl)
//req.Header.Add("Origin", c.baseUrl)
//// Host: file.wx2.qq.com
//req.Header.Add("Host", "file.wx2.qq.com")
// 发送数据
resp, err = c.Do(req)
// 如果不是最后一次, 解析有没有错误
if !isLastTime {
returnResp := NewReturnResponse(resp, err)
if err := parseBaseResponseError(returnResp); err != nil {
return nil, err
}
}
}
return resp, err
}
// 发送图片 // 发送图片
// 这个接口依赖上传文件的接口 // 这个接口依赖上传文件的接口
// 发送的图片必须是已经成功上传的图片 // 发送的图片必须是已经成功上传的图片

138
global.go
View File

@ -1,94 +1,94 @@
package openwechat package openwechat
import ( import (
"errors" "errors"
"regexp" "regexp"
) )
var ( var (
uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`) uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`)
statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`) statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`)
syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`) syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`)
redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`) redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`)
) )
const ( const (
appId = "wx782c26e4c19acffb" appId = "wx782c26e4c19acffb"
jsLoginUrl = "https://login.wx.qq.com/jslogin" jsLoginUrl = "https://login.wx.qq.com/jslogin"
qrcodeUrl = "https://login.weixin.qq.com/qrcode/" qrcodeUrl = "https://login.weixin.qq.com/qrcode/"
loginUrl = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login" loginUrl = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login"
// Normal urls // Normal urls
baseNormalUrl = "https://wx2.qq.com" baseNormalUrl = "https://wx2.qq.com"
webWxNewLoginPageNormalUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage" webWxNewLoginPageNormalUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage"
webWxInitNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit" webWxInitNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit"
webWxStatusNotifyNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" webWxStatusNotifyNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify"
webWxSyncNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync" webWxSyncNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync"
webWxSendMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" webWxSendMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg"
webWxGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" webWxGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact"
webWxSendMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" webWxSendMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg"
webWxSendAppMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" webWxSendAppMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg"
webWxBatchGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" webWxBatchGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact"
webWxOplogNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" webWxOplogNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxoplog"
webWxVerifyUserNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" webWxVerifyUserNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser"
syncCheckNormalUrl = "https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck" syncCheckNormalUrl = "https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck"
webWxUpLoadMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" webWxUpLoadMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia"
webWxGetMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" webWxGetMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg"
webWxGetVoiceNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" webWxGetVoiceNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice"
webWxGetVideoNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" webWxGetVideoNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo"
webWxLogoutNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" webWxLogoutNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout"
webWxGetMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" webWxGetMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia"
webWxUpdateChatRoomNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" webWxUpdateChatRoomNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom"
webWxRevokeMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" webWxRevokeMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg"
// Desktop urls // Desktop urls
baseDesktopUrl = "https://wx.qq.com" baseDesktopUrl = "https://wx.qq.com"
webWxNewLoginPageDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop" webWxNewLoginPageDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop"
webWxInitDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit" webWxInitDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit"
webWxStatusNotifyDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" webWxStatusNotifyDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify"
webWxSyncDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync" webWxSyncDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync"
webWxSendMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" webWxSendMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg"
webWxGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" webWxGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact"
webWxSendMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" webWxSendMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg"
webWxSendAppMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" webWxSendAppMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg"
webWxBatchGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" webWxBatchGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact"
webWxOplogDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" webWxOplogDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog"
webWxVerifyUserDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" webWxVerifyUserDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser"
syncCheckDesktopUrl = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck" syncCheckDesktopUrl = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck"
webWxUpLoadMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" webWxUpLoadMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia"
webWxGetMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" webWxGetMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg"
webWxGetVoiceDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" webWxGetVoiceDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice"
webWxGetVideoDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" webWxGetVideoDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo"
webWxLogoutDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" webWxLogoutDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout"
webWxGetMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" webWxGetMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia"
webWxUpdateChatRoomDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" webWxUpdateChatRoomDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom"
webWxRevokeMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" webWxRevokeMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg"
jsonContentType = "application/json; charset=utf-8" jsonContentType = "application/json; charset=utf-8"
uosPatchClientVersion = "2.0.0" uosPatchClientVersion = "2.0.0"
uosPatchExtspam = "Gp8ICJkIEpkICggwMDAwMDAwMRAGGoAI1GiJSIpeO1RZTq9QBKsRbPJdi84ropi16EYI10WB6g74sGmRwSNXjPQnYUKYotKkvLGpshucCaeWZMOylnc6o2AgDX9grhQQx7fm2DJRTyuNhUlwmEoWhjoG3F0ySAWUsEbH3bJMsEBwoB//0qmFJob74ffdaslqL+IrSy7LJ76/G5TkvNC+J0VQkpH1u3iJJs0uUYyLDzdBIQ6Ogd8LDQ3VKnJLm4g/uDLe+G7zzzkOPzCjXL+70naaQ9medzqmh+/SmaQ6uFWLDQLcRln++wBwoEibNpG4uOJvqXy+ql50DjlNchSuqLmeadFoo9/mDT0q3G7o/80P15ostktjb7h9bfNc+nZVSnUEJXbCjTeqS5UYuxn+HTS5nZsPVxJA2O5GdKCYK4x8lTTKShRstqPfbQpplfllx2fwXcSljuYi3YipPyS3GCAqf5A7aYYwJ7AvGqUiR2SsVQ9Nbp8MGHET1GxhifC692APj6SJxZD3i1drSYZPMMsS9rKAJTGz2FEupohtpf2tgXm6c16nDk/cw+C7K7me5j5PLHv55DFCS84b06AytZPdkFZLj7FHOkcFGJXitHkX5cgww7vuf6F3p0yM/W73SoXTx6GX4G6Hg2rYx3O/9VU2Uq8lvURB4qIbD9XQpzmyiFMaytMnqxcZJcoXCtfkTJ6pI7a92JpRUvdSitg967VUDUAQnCXCM/m0snRkR9LtoXAO1FUGpwlp1EfIdCZFPKNnXMeqev0j9W9ZrkEs9ZWcUEexSj5z+dKYQBhIICviYUQHVqBTZSNy22PlUIeDeIs11j7q4t8rD8LPvzAKWVqXE+5lS1JPZkjg4y5hfX1Dod3t96clFfwsvDP6xBSe1NBcoKbkyGxYK0UvPGtKQEE0Se2zAymYDv41klYE9s+rxp8e94/H8XhrL9oGm8KWb2RmYnAE7ry9gd6e8ZuBRIsISlJAE/e8y8xFmP031S6Lnaet6YXPsFpuFsdQs535IjcFd75hh6DNMBYhSfjv456cvhsb99+fRw/KVZLC3yzNSCbLSyo9d9BI45Plma6V8akURQA/qsaAzU0VyTIqZJkPDTzhuCl92vD2AD/QOhx6iwRSVPAxcRFZcWjgc2wCKh+uCYkTVbNQpB9B90YlNmI3fWTuUOUjwOzQRxJZj11NsimjOJ50qQwTTFj6qQvQ1a/I+MkTx5UO+yNHl718JWcR3AXGmv/aa9rD1eNP8ioTGlOZwPgmr2sor2iBpKTOrB83QgZXP+xRYkb4zVC+LoAXEoIa1+zArywlgREer7DLePukkU6wHTkuSaF+ge5Of1bXuU4i938WJHj0t3D8uQxkJvoFi/EYN/7u2P1zGRLV4dHVUsZMGCCtnO6BBigFMAA=" uosPatchExtspam = "Gp8ICJkIEpkICggwMDAwMDAwMRAGGoAI1GiJSIpeO1RZTq9QBKsRbPJdi84ropi16EYI10WB6g74sGmRwSNXjPQnYUKYotKkvLGpshucCaeWZMOylnc6o2AgDX9grhQQx7fm2DJRTyuNhUlwmEoWhjoG3F0ySAWUsEbH3bJMsEBwoB//0qmFJob74ffdaslqL+IrSy7LJ76/G5TkvNC+J0VQkpH1u3iJJs0uUYyLDzdBIQ6Ogd8LDQ3VKnJLm4g/uDLe+G7zzzkOPzCjXL+70naaQ9medzqmh+/SmaQ6uFWLDQLcRln++wBwoEibNpG4uOJvqXy+ql50DjlNchSuqLmeadFoo9/mDT0q3G7o/80P15ostktjb7h9bfNc+nZVSnUEJXbCjTeqS5UYuxn+HTS5nZsPVxJA2O5GdKCYK4x8lTTKShRstqPfbQpplfllx2fwXcSljuYi3YipPyS3GCAqf5A7aYYwJ7AvGqUiR2SsVQ9Nbp8MGHET1GxhifC692APj6SJxZD3i1drSYZPMMsS9rKAJTGz2FEupohtpf2tgXm6c16nDk/cw+C7K7me5j5PLHv55DFCS84b06AytZPdkFZLj7FHOkcFGJXitHkX5cgww7vuf6F3p0yM/W73SoXTx6GX4G6Hg2rYx3O/9VU2Uq8lvURB4qIbD9XQpzmyiFMaytMnqxcZJcoXCtfkTJ6pI7a92JpRUvdSitg967VUDUAQnCXCM/m0snRkR9LtoXAO1FUGpwlp1EfIdCZFPKNnXMeqev0j9W9ZrkEs9ZWcUEexSj5z+dKYQBhIICviYUQHVqBTZSNy22PlUIeDeIs11j7q4t8rD8LPvzAKWVqXE+5lS1JPZkjg4y5hfX1Dod3t96clFfwsvDP6xBSe1NBcoKbkyGxYK0UvPGtKQEE0Se2zAymYDv41klYE9s+rxp8e94/H8XhrL9oGm8KWb2RmYnAE7ry9gd6e8ZuBRIsISlJAE/e8y8xFmP031S6Lnaet6YXPsFpuFsdQs535IjcFd75hh6DNMBYhSfjv456cvhsb99+fRw/KVZLC3yzNSCbLSyo9d9BI45Plma6V8akURQA/qsaAzU0VyTIqZJkPDTzhuCl92vD2AD/QOhx6iwRSVPAxcRFZcWjgc2wCKh+uCYkTVbNQpB9B90YlNmI3fWTuUOUjwOzQRxJZj11NsimjOJ50qQwTTFj6qQvQ1a/I+MkTx5UO+yNHl718JWcR3AXGmv/aa9rD1eNP8ioTGlOZwPgmr2sor2iBpKTOrB83QgZXP+xRYkb4zVC+LoAXEoIa1+zArywlgREer7DLePukkU6wHTkuSaF+ge5Of1bXuU4i938WJHj0t3D8uQxkJvoFi/EYN/7u2P1zGRLV4dHVUsZMGCCtnO6BBigFMAA="
) )
// 消息类型 // 消息类型
const ( const (
TextMessage = 1 TextMessage = 1
ImageMessage = 3 ImageMessage = 3
AppMessage = 6 AppMessage = 6
) )
// 登录状态 // 登录状态
const ( const (
statusSuccess = "200" statusSuccess = "200"
statusScanned = "201" statusScanned = "201"
statusTimeout = "400" statusTimeout = "400"
statusWait = "408" statusWait = "408"
) )
// errors // errors
var ( var (
noSuchUserFoundError = errors.New("no such user found") noSuchUserFoundError = errors.New("no such user found")
) )
// ALL跟search函数搭配 // ALL跟search函数搭配
@ -97,6 +97,10 @@ const ALL = 0
// 性别 // 性别
const ( const (
MALE = 1 MALE = 1
FEMALE = 2 FEMALE = 2
) )
const chunkSize int64 = 512 * 1024
const TimeFormat = "Mon Jan 02 2006 15:04:05 GMT+0800 (中国标准时间)"

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"math/rand" "math/rand"
"mime/multipart"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -18,6 +19,7 @@ func ToBuffer(v interface{}) (*bytes.Buffer, error) {
return bytes.NewBuffer(buf), nil return bytes.NewBuffer(buf), nil
} }
// 获取随机设备id
func GetRandomDeviceId() string { func GetRandomDeviceId() string {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
var builder strings.Builder var builder strings.Builder
@ -38,6 +40,7 @@ func getWebWxDataTicket(cookies []*http.Cookie) string {
return "" return ""
} }
// Form Xml 格式化
func XmlFormString(text string) string { func XmlFormString(text string) string {
lt := strings.ReplaceAll(text, "&lt;", "<") lt := strings.ReplaceAll(text, "&lt;", "<")
gt := strings.ReplaceAll(lt, "&gt;", ">") gt := strings.ReplaceAll(lt, "&gt;", ">")
@ -52,3 +55,12 @@ func getTotalDuration(delay ...time.Duration) time.Duration {
} }
return total return total
} }
// 获取文件上传的类型
func GetFileContentType(file multipart.File) (string, error) {
data := make([]byte, 512)
if _, err := file.Read(data); err != nil {
return "", err
}
return http.DetectContentType(data), nil
}

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
) )
// 抽象的用户结构,包含 好友 群组 公众号 // 抽象的用户结构: 好友 群组 公众号
type User struct { type User struct {
Uin int Uin int
HideInputBarFlag int HideInputBarFlag int
@ -451,6 +451,7 @@ func (m Members) MPs() Mps {
return mps return mps
} }
// 获取当前Members的详情
func (m Members) detail(self *Self) error { func (m Members) detail(self *Self) error {
// 获取他们的数量 // 获取他们的数量
members := m members := m