添加文件发送接口
This commit is contained in:
parent
3c7fec07f3
commit
e751e13afd
26
bot_test.go
26
bot_test.go
@ -291,3 +291,29 @@ func TestRevokeMessage(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSendFile(t *testing.T) {
|
||||||
|
bot := defaultBot(Desktop)
|
||||||
|
if err := bot.HotLogin(NewJsonFileHotReloadStorage("2.json"), true); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self, err := bot.GetCurrentUser()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fh, err := self.FileHelper()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, _ := os.Open("Taylor+Swift+-+Red")
|
||||||
|
defer f.Close()
|
||||||
|
msg, err := self.SendFileToFriend(fh, f)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Log(msg.MsgId)
|
||||||
|
}
|
||||||
|
67
caller.go
67
caller.go
@ -200,47 +200,64 @@ func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string)
|
|||||||
return parseBaseResponseError(resp)
|
return parseBaseResponseError(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送图片消息接口
|
func (c *Caller) UploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName, mediaType string) (*UploadResponse, error) {
|
||||||
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
|
|
||||||
// 首先尝试上传图片
|
// 首先尝试上传图片
|
||||||
sate, err := file.Stat()
|
resp := NewReturnResponse(c.Client.WebWxUploadMediaByChunk(file, request, info, fromUserName, toUserName, mediaType))
|
||||||
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()
|
||||||
|
|
||||||
var item struct {
|
var item UploadResponse
|
||||||
BaseResponse BaseResponse
|
|
||||||
MediaId string
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = resp.ScanJSON(&item); err != nil {
|
if err := resp.ScanJSON(&item); err != nil {
|
||||||
return nil, err
|
return &item, err
|
||||||
}
|
}
|
||||||
if !item.BaseResponse.Ok() {
|
if !item.BaseResponse.Ok() {
|
||||||
return nil, item.BaseResponse
|
return &item, item.BaseResponse
|
||||||
}
|
}
|
||||||
if len(item.MediaId) == 0 {
|
if len(item.MediaId) == 0 {
|
||||||
return nil, errors.New("upload failed")
|
return &item, errors.New("upload failed")
|
||||||
|
}
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送图片消息接口
|
||||||
|
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
|
||||||
|
// 首先尝试上传图片
|
||||||
|
resp, err := c.UploadMedia(file, request, info, fromUserName, toUserName, "pic")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
// 构造新的图片类型的信息
|
// 构造新的图片类型的信息
|
||||||
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
|
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, resp.MediaId)
|
||||||
// 发送图片信息
|
// 发送图片信息
|
||||||
resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
|
return c.WebWxSendAppMsg(msg, request, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Caller) WebWxSendFile(file *os.File, req *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
|
||||||
|
resp, err := c.UploadMedia(file, req, info, fromUserName, toUserName, "doc")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 构造新的图片类型的信息
|
||||||
|
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, resp.MediaId)
|
||||||
|
stat, _ := file.Stat()
|
||||||
|
appMsg := NewFileAppMessage(stat, resp.MediaId)
|
||||||
|
content, err := appMsg.XmlByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Content = string(content)
|
||||||
|
return c.WebWxSendAppMsg(msg, req, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送媒体消息
|
||||||
|
func (c *Caller) WebWxSendAppMsg(msg *SendMessage, req *BaseRequest, info *LoginInfo) (*SentMessage, error) {
|
||||||
|
resp := NewReturnResponse(c.Client.WebWxSendMsgImg(msg, req, info))
|
||||||
sendSuccessMsg := &SentMessage{SendMessage: msg}
|
sendSuccessMsg := &SentMessage{SendMessage: msg}
|
||||||
err = parseMessageResponseError(resp, sendSuccessMsg)
|
err := parseMessageResponseError(resp, sendSuccessMsg)
|
||||||
return sendSuccessMsg, err
|
return sendSuccessMsg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +329,7 @@ func parseMessageResponseError(resp *ReturnResponse, msg *SentMessage) error {
|
|||||||
if !messageResp.BaseResponse.Ok() {
|
if !messageResp.BaseResponse.Ok() {
|
||||||
return messageResp.BaseResponse
|
return messageResp.BaseResponse
|
||||||
}
|
}
|
||||||
//// 发送成功之后将msgId赋值给SendMessage
|
// 发送成功之后将msgId赋值给SendMessage
|
||||||
msg.MsgId = messageResp.MsgID
|
msg.MsgId = messageResp.MsgID
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
168
client.go
168
client.go
@ -251,104 +251,12 @@ func (c *Client) WebWxGetHeadImg(headImageUrl string) (*http.Response, error) {
|
|||||||
return c.Do(req)
|
return c.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
|
|
||||||
// 文件复位
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if w, err := writer.CreateFormFile("filename", file.Name()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
if _, err = w.Write(data); 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)
|
|
||||||
return c.Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) {
|
func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) {
|
||||||
// 获取文件上传的类型
|
// 获取文件上传的类型
|
||||||
contentType, err := GetFileContentType(file)
|
contentType, err := GetFileContentType(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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个字节
|
// 上面获取文件的类型的时候已经读取了512个字节
|
||||||
@ -363,6 +271,20 @@ func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, in
|
|||||||
data := buffer.Bytes()
|
data := buffer.Bytes()
|
||||||
fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
|
fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
|
||||||
|
|
||||||
|
sate, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path, _ := url.Parse(c.webWxUpLoadMediaUrl)
|
||||||
|
params := url.Values{}
|
||||||
|
params.Add("f", "json")
|
||||||
|
|
||||||
|
path.RawQuery = params.Encode()
|
||||||
|
|
||||||
|
cookies := c.Jar.Cookies(path)
|
||||||
|
webWxDataTicket := getWebWxDataTicket(cookies)
|
||||||
|
|
||||||
uploadMediaRequest := map[string]interface{}{
|
uploadMediaRequest := map[string]interface{}{
|
||||||
"UploadType": 2,
|
"UploadType": 2,
|
||||||
"BaseRequest": request,
|
"BaseRequest": request,
|
||||||
@ -375,18 +297,40 @@ func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, in
|
|||||||
"ToUserName": toUserName,
|
"ToUserName": toUserName,
|
||||||
"FileMd5": fileMd5,
|
"FileMd5": fileMd5,
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadMediaRequestByte, err := json.Marshal(uploadMediaRequest)
|
uploadMediaRequestByte, err := json.Marshal(uploadMediaRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks := sate.Size() / chunkSize
|
var chunks int64
|
||||||
if chunks*chunkSize < sate.Size() {
|
|
||||||
chunks++
|
if sate.Size() > chunkSize {
|
||||||
|
chunks = sate.Size() / chunkSize
|
||||||
|
if chunks*chunkSize < sate.Size() {
|
||||||
|
chunks++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunks = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
|
||||||
|
content := map[string]string{
|
||||||
|
"id": "WU_FILE_0",
|
||||||
|
"name": file.Name(),
|
||||||
|
"type": contentType,
|
||||||
|
"lastModifiedDate": sate.ModTime().Format(TimeFormat),
|
||||||
|
"size": strconv.FormatInt(sate.Size(), 10),
|
||||||
|
"mediatype": mediaType,
|
||||||
|
"webwx_data_ticket": webWxDataTicket,
|
||||||
|
"pass_ticket": info.PassTicket,
|
||||||
|
}
|
||||||
|
|
||||||
|
if chunks > 1 {
|
||||||
|
content["chunks"] = strconv.FormatInt(chunks, 10)
|
||||||
|
}
|
||||||
|
// 分块上传
|
||||||
for chunk := 0; int64(chunk) < chunks; chunk++ {
|
for chunk := 0; int64(chunk) < chunks; chunk++ {
|
||||||
|
|
||||||
var isLastTime bool
|
var isLastTime bool
|
||||||
@ -394,18 +338,10 @@ func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, in
|
|||||||
isLastTime = true
|
isLastTime = true
|
||||||
}
|
}
|
||||||
|
|
||||||
content := map[string]string{
|
if chunks > 1 {
|
||||||
"id": "WU_FILE_0",
|
content["chunk"] = strconv.Itoa(chunk)
|
||||||
"name": file.Name(),
|
|
||||||
"type": contentType,
|
|
||||||
"lastModifiedDate": sate.ModTime().Format(TimeFormat),
|
|
||||||
"size": strconv.FormatInt(sate.Size(), 10),
|
|
||||||
"mediatype": mediaType,
|
|
||||||
"webwx_data_ticket": webWxDataTicket,
|
|
||||||
"pass_ticket": info.PassTicket,
|
|
||||||
"chunks": strconv.FormatInt(chunks, 10),
|
|
||||||
"chunk": strconv.Itoa(chunk),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var formBuffer bytes.Buffer
|
var formBuffer bytes.Buffer
|
||||||
|
|
||||||
writer := multipart.NewWriter(&formBuffer)
|
writer := multipart.NewWriter(&formBuffer)
|
||||||
@ -450,7 +386,7 @@ func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 将最后一次携带文件信息的response返回
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,3 +581,21 @@ func (c *Client) WebWxRevokeMsg(msg *SentMessage, request *BaseRequest) (*http.R
|
|||||||
req.Header.Set("Content-Type", jsonContentType)
|
req.Header.Set("Content-Type", jsonContentType)
|
||||||
return c.Do(req)
|
return c.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验上传文件
|
||||||
|
func (c *Client) webWxCheckUpload(stat os.FileInfo, request *BaseRequest, fileMd5, fromUserName, toUserName string) (*http.Response, error) {
|
||||||
|
path, _ := url.Parse(c.webWxCheckUploadUrl)
|
||||||
|
content := map[string]interface{}{
|
||||||
|
"BaseRequest": request,
|
||||||
|
"FileMd5": fileMd5,
|
||||||
|
"FileName": stat.Name(),
|
||||||
|
"FileSize": stat.Size(),
|
||||||
|
"FileType": 7,
|
||||||
|
"FromUserName": fromUserName,
|
||||||
|
"ToUserName": toUserName,
|
||||||
|
}
|
||||||
|
body, _ := ToBuffer(content)
|
||||||
|
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
|
||||||
|
req.Header.Add("Content-Type", jsonContentType)
|
||||||
|
return c.Do(req)
|
||||||
|
}
|
||||||
|
147
global.go
147
global.go
@ -1,94 +1,96 @@
|
|||||||
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"
|
||||||
|
webWxCheckUploadNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxcheckupload"
|
||||||
|
|
||||||
// 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"
|
||||||
|
webWxCheckUploadDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxcheckupload"
|
||||||
|
|
||||||
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,10 +99,19 @@ const ALL = 0
|
|||||||
|
|
||||||
// 性别
|
// 性别
|
||||||
const (
|
const (
|
||||||
MALE = 1
|
MALE = 1
|
||||||
FEMALE = 2
|
FEMALE = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
const chunkSize int64 = 512 * 1024
|
const (
|
||||||
|
// 分块上传时每次上传的文件的大小
|
||||||
|
chunkSize int64 = (1 << 20) / 2 // 0.5m
|
||||||
|
// 需要检测的文件大小
|
||||||
|
needCheckSize int64 = 25 << 20 // 20m
|
||||||
|
// 最大文件上传大小
|
||||||
|
maxFileUploadSize int64 = 50 << 20 // 50m
|
||||||
|
// 最大图片上传大小
|
||||||
|
maxImageUploadSize int64 = 20 << 20 // 20m
|
||||||
|
)
|
||||||
|
|
||||||
const TimeFormat = "Mon Jan 02 2006 15:04:05 GMT+0800 (中国标准时间)"
|
const TimeFormat = "Mon Jan 02 2006 15:04:05 GMT+0800 (中国标准时间)"
|
||||||
|
247
items.go
247
items.go
@ -1,8 +1,8 @@
|
|||||||
package openwechat
|
package openwechat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -11,194 +11,199 @@ import (
|
|||||||
|
|
||||||
// 登录信息
|
// 登录信息
|
||||||
type LoginInfo struct {
|
type LoginInfo struct {
|
||||||
Ret int `xml:"ret"`
|
Ret int `xml:"ret"`
|
||||||
WxUin int `xml:"wxuin"`
|
WxUin int `xml:"wxuin"`
|
||||||
IsGrayScale int `xml:"isgrayscale"`
|
IsGrayScale int `xml:"isgrayscale"`
|
||||||
Message string `xml:"message"`
|
Message string `xml:"message"`
|
||||||
SKey string `xml:"skey"`
|
SKey string `xml:"skey"`
|
||||||
WxSid string `xml:"wxsid"`
|
WxSid string `xml:"wxsid"`
|
||||||
PassTicket string `xml:"pass_ticket"`
|
PassTicket string `xml:"pass_ticket"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LoginInfo) Ok() bool {
|
func (l LoginInfo) Ok() bool {
|
||||||
return l.Ret == 0
|
return l.Ret == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LoginInfo) Error() string {
|
func (l LoginInfo) Error() string {
|
||||||
return l.Message
|
return l.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始的请求信息
|
// 初始的请求信息
|
||||||
// 几乎所有的请求都要携带该参数
|
// 几乎所有的请求都要携带该参数
|
||||||
type BaseRequest struct {
|
type BaseRequest struct {
|
||||||
Uin int
|
Uin int
|
||||||
Sid, Skey, DeviceID string
|
Sid, Skey, DeviceID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大部分返回对象都携带该信息
|
// 大部分返回对象都携带该信息
|
||||||
type BaseResponse struct {
|
type BaseResponse struct {
|
||||||
Ret int
|
Ret int
|
||||||
ErrMsg string
|
ErrMsg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseResponse) Ok() bool {
|
func (b BaseResponse) Ok() bool {
|
||||||
return b.Ret == 0
|
return b.Ret == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseResponse) Error() string {
|
func (b BaseResponse) Error() string {
|
||||||
if err := getResponseErrorWithRetCode(b.Ret); err != nil {
|
if err := getResponseErrorWithRetCode(b.Ret); err != nil {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getResponseErrorWithRetCode(code int) error {
|
func getResponseErrorWithRetCode(code int) error {
|
||||||
switch code {
|
switch code {
|
||||||
case 0:
|
case 0:
|
||||||
return nil
|
return nil
|
||||||
case 1:
|
case 1:
|
||||||
return errors.New("param error")
|
return errors.New("param error")
|
||||||
case -14:
|
case -14:
|
||||||
return errors.New("ticket error")
|
return errors.New("ticket error")
|
||||||
case 1100:
|
case 1100:
|
||||||
return errors.New("not login warn")
|
return errors.New("not login warn")
|
||||||
case 1101:
|
case 1101:
|
||||||
return errors.New("not login check")
|
return errors.New("not login check")
|
||||||
case 1102:
|
case 1102:
|
||||||
return errors.New("cookie invalid error")
|
return errors.New("cookie invalid error")
|
||||||
case 1203:
|
case 1203:
|
||||||
return errors.New("login env error")
|
return errors.New("login env error")
|
||||||
case 1205:
|
case 1205:
|
||||||
return errors.New("opt too often")
|
return errors.New("opt too often")
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("base response ret code %d", code)
|
return fmt.Errorf("base response ret code %d", code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncKey struct {
|
type SyncKey struct {
|
||||||
Count int
|
Count int
|
||||||
List []struct{ Key, Val int64 }
|
List []struct{ Key, Val int64 }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化的相应信息
|
// 初始化的相应信息
|
||||||
type WebInitResponse struct {
|
type WebInitResponse struct {
|
||||||
Count int
|
Count int
|
||||||
ClientVersion int
|
ClientVersion int
|
||||||
GrayScale int
|
GrayScale int
|
||||||
InviteStartCount int
|
InviteStartCount int
|
||||||
MPSubscribeMsgCount int
|
MPSubscribeMsgCount int
|
||||||
ClickReportInterval int
|
ClickReportInterval int
|
||||||
SystemTime int64
|
SystemTime int64
|
||||||
ChatSet string
|
ChatSet string
|
||||||
SKey string
|
SKey string
|
||||||
BaseResponse BaseResponse
|
BaseResponse BaseResponse
|
||||||
SyncKey SyncKey
|
SyncKey SyncKey
|
||||||
User User
|
User User
|
||||||
MPSubscribeMsgList []MPSubscribeMsg
|
MPSubscribeMsgList []MPSubscribeMsg
|
||||||
ContactList []User
|
ContactList []User
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公众号的订阅信息
|
// 公众号的订阅信息
|
||||||
type MPSubscribeMsg struct {
|
type MPSubscribeMsg struct {
|
||||||
MPArticleCount int
|
MPArticleCount int
|
||||||
Time int64
|
Time int64
|
||||||
UserName string
|
UserName string
|
||||||
NickName string
|
NickName string
|
||||||
MPArticleList []struct {
|
MPArticleList []struct {
|
||||||
Title string
|
Title string
|
||||||
Cover string
|
Cover string
|
||||||
Digest string
|
Digest string
|
||||||
Url string
|
Url string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserDetailItem struct {
|
type UserDetailItem struct {
|
||||||
UserName string
|
UserName string
|
||||||
EncryChatRoomId string
|
EncryChatRoomId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserDetailItemList []UserDetailItem
|
type UserDetailItemList []UserDetailItem
|
||||||
|
|
||||||
func NewUserDetailItemList(members Members) UserDetailItemList {
|
func NewUserDetailItemList(members Members) UserDetailItemList {
|
||||||
var list UserDetailItemList
|
var list UserDetailItemList
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
|
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
|
||||||
list = append(list, item)
|
list = append(list, item)
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncCheckResponse struct {
|
type SyncCheckResponse struct {
|
||||||
RetCode string
|
RetCode string
|
||||||
Selector string
|
Selector string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncCheckResponse) Success() bool {
|
func (s *SyncCheckResponse) Success() bool {
|
||||||
return s.RetCode == "0"
|
return s.RetCode == "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncCheckResponse) NorMal() bool {
|
func (s *SyncCheckResponse) NorMal() bool {
|
||||||
return s.Success() && s.Selector == "0"
|
return s.Success() && s.Selector == "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实现error接口
|
// 实现error接口
|
||||||
func (s *SyncCheckResponse) Error() string {
|
func (s *SyncCheckResponse) Error() string {
|
||||||
switch s.RetCode {
|
switch s.RetCode {
|
||||||
case "0":
|
case "0":
|
||||||
return ""
|
return ""
|
||||||
case "1":
|
case "1":
|
||||||
return "param error"
|
return "param error"
|
||||||
case "-14":
|
case "-14":
|
||||||
return "ticker error"
|
return "ticker error"
|
||||||
case "1100":
|
case "1100":
|
||||||
return "not login warn"
|
return "not login warn"
|
||||||
case "1101":
|
case "1101":
|
||||||
return "not login check"
|
return "not login check"
|
||||||
case "1102":
|
case "1102":
|
||||||
return "cookie invalid error"
|
return "cookie invalid error"
|
||||||
case "1203":
|
case "1203":
|
||||||
return "login env error"
|
return "login env error"
|
||||||
case "1205":
|
case "1205":
|
||||||
return "opt too often"
|
return "opt too often"
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("sync check response error code %s", s.RetCode)
|
return fmt.Sprintf("sync check response error code %s", s.RetCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebWxSyncResponse struct {
|
type WebWxSyncResponse struct {
|
||||||
AddMsgCount int
|
AddMsgCount int
|
||||||
ContinueFlag int
|
ContinueFlag int
|
||||||
DelContactCount int
|
DelContactCount int
|
||||||
ModChatRoomMemberCount int
|
ModChatRoomMemberCount int
|
||||||
ModContactCount int
|
ModContactCount int
|
||||||
Skey string
|
Skey string
|
||||||
SyncCheckKey SyncKey
|
SyncCheckKey SyncKey
|
||||||
SyncKey SyncKey
|
SyncKey SyncKey
|
||||||
BaseResponse BaseResponse
|
BaseResponse BaseResponse
|
||||||
ModChatRoomMemberList Members
|
ModChatRoomMemberList Members
|
||||||
AddMsgList []*Message
|
AddMsgList []*Message
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebWxContactResponse struct {
|
type WebWxContactResponse struct {
|
||||||
MemberCount int
|
MemberCount int
|
||||||
Seq int
|
Seq int
|
||||||
BaseResponse BaseResponse
|
BaseResponse BaseResponse
|
||||||
MemberList []*User
|
MemberList []*User
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebWxBatchContactResponse struct {
|
type WebWxBatchContactResponse struct {
|
||||||
Count int
|
Count int
|
||||||
BaseResponse BaseResponse
|
BaseResponse BaseResponse
|
||||||
ContactList []*User
|
ContactList []*User
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckLoginResponse struct {
|
type CheckLoginResponse struct {
|
||||||
Code string
|
Code string
|
||||||
Raw []byte
|
Raw []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageResponse struct {
|
type MessageResponse struct {
|
||||||
BaseResponse BaseResponse
|
BaseResponse BaseResponse
|
||||||
LocalID string
|
LocalID string
|
||||||
MsgID string
|
MsgID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadResponse struct {
|
||||||
|
BaseResponse BaseResponse
|
||||||
|
MediaId string
|
||||||
}
|
}
|
||||||
|
629
message.go
629
message.go
@ -1,473 +1,504 @@
|
|||||||
package openwechat
|
package openwechat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
IsAt bool
|
IsAt bool
|
||||||
AppInfo struct {
|
AppInfo struct {
|
||||||
Type int
|
Type int
|
||||||
AppID string
|
AppID string
|
||||||
}
|
}
|
||||||
AppMsgType int
|
AppMsgType int
|
||||||
HasProductId int
|
HasProductId int
|
||||||
ImgHeight int
|
ImgHeight int
|
||||||
ImgStatus int
|
ImgStatus int
|
||||||
ImgWidth int
|
ImgWidth int
|
||||||
ForwardFlag int
|
ForwardFlag int
|
||||||
MsgType int
|
MsgType int
|
||||||
Status int
|
Status int
|
||||||
StatusNotifyCode int
|
StatusNotifyCode int
|
||||||
SubMsgType int
|
SubMsgType int
|
||||||
VoiceLength int
|
VoiceLength int
|
||||||
CreateTime int64
|
CreateTime int64
|
||||||
NewMsgId int64
|
NewMsgId int64
|
||||||
PlayLength int64
|
PlayLength int64
|
||||||
MediaId string
|
MediaId string
|
||||||
MsgId string
|
MsgId string
|
||||||
EncryFileName string
|
EncryFileName string
|
||||||
FileName string
|
FileName string
|
||||||
FileSize string
|
FileSize string
|
||||||
Content string
|
Content string
|
||||||
FromUserName string
|
FromUserName string
|
||||||
OriContent string
|
OriContent string
|
||||||
StatusNotifyUserName string
|
StatusNotifyUserName string
|
||||||
Ticket string
|
Ticket string
|
||||||
ToUserName string
|
ToUserName string
|
||||||
Url string
|
Url string
|
||||||
senderInGroupUserName string
|
senderInGroupUserName string
|
||||||
RecommendInfo RecommendInfo
|
RecommendInfo RecommendInfo
|
||||||
Bot *Bot
|
Bot *Bot
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
Context context.Context
|
Context context.Context
|
||||||
item map[string]interface{}
|
item map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取消息的发送者
|
// 获取消息的发送者
|
||||||
func (m *Message) Sender() (*User, error) {
|
func (m *Message) Sender() (*User, error) {
|
||||||
members, err := m.Bot.self.Members(true)
|
members, err := m.Bot.self.Members(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if m.FromUserName == m.Bot.self.User.UserName {
|
if m.FromUserName == m.Bot.self.User.UserName {
|
||||||
return m.Bot.self.User, nil
|
return m.Bot.self.User, nil
|
||||||
}
|
}
|
||||||
user := members.SearchByUserName(1, m.FromUserName)
|
user := members.SearchByUserName(1, m.FromUserName)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, noSuchUserFoundError
|
return nil, noSuchUserFoundError
|
||||||
}
|
}
|
||||||
return user.First().Detail()
|
return user.First().Detail()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取消息在群里面的发送者
|
// 获取消息在群里面的发送者
|
||||||
func (m *Message) SenderInGroup() (*User, error) {
|
func (m *Message) SenderInGroup() (*User, error) {
|
||||||
if !m.IsSendByGroup() {
|
if !m.IsSendByGroup() {
|
||||||
return nil, errors.New("message is not from group")
|
return nil, errors.New("message is not from group")
|
||||||
}
|
}
|
||||||
group, err := m.Sender()
|
group, err := m.Sender()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
group, err = group.Detail()
|
group, err = group.Detail()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
users := group.MemberList.SearchByUserName(1, m.senderInGroupUserName)
|
users := group.MemberList.SearchByUserName(1, m.senderInGroupUserName)
|
||||||
if users == nil {
|
if users == nil {
|
||||||
return nil, noSuchUserFoundError
|
return nil, noSuchUserFoundError
|
||||||
}
|
}
|
||||||
return users.First(), nil
|
return users.First(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取消息的接收者
|
// 获取消息的接收者
|
||||||
func (m *Message) Receiver() (*User, error) {
|
func (m *Message) Receiver() (*User, error) {
|
||||||
if m.IsSendByGroup() {
|
if m.IsSendByGroup() {
|
||||||
if sender, err := m.Sender(); err != nil {
|
if sender, err := m.Sender(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
users := sender.MemberList.SearchByUserName(1, m.ToUserName)
|
users := sender.MemberList.SearchByUserName(1, m.ToUserName)
|
||||||
if users == nil {
|
if users == nil {
|
||||||
return nil, noSuchUserFoundError
|
return nil, noSuchUserFoundError
|
||||||
}
|
}
|
||||||
return users.First(), nil
|
return users.First(), nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
users := m.Bot.self.MemberList.SearchByUserName(1, m.ToUserName)
|
users := m.Bot.self.MemberList.SearchByUserName(1, m.ToUserName)
|
||||||
if users == nil {
|
if users == nil {
|
||||||
return nil, noSuchUserFoundError
|
return nil, noSuchUserFoundError
|
||||||
}
|
}
|
||||||
return users.First(), nil
|
return users.First(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断消息是否由自己发送
|
// 判断消息是否由自己发送
|
||||||
func (m *Message) IsSendBySelf() bool {
|
func (m *Message) IsSendBySelf() bool {
|
||||||
return m.FromUserName == m.Bot.self.User.UserName
|
return m.FromUserName == m.Bot.self.User.UserName
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断消息是否由好友发送
|
// 判断消息是否由好友发送
|
||||||
func (m *Message) IsSendByFriend() bool {
|
func (m *Message) IsSendByFriend() bool {
|
||||||
return !m.IsSendByGroup() && strings.HasPrefix(m.FromUserName, "@")
|
return !m.IsSendByGroup() && strings.HasPrefix(m.FromUserName, "@")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断消息是否由群组发送
|
// 判断消息是否由群组发送
|
||||||
func (m *Message) IsSendByGroup() bool {
|
func (m *Message) IsSendByGroup() bool {
|
||||||
return strings.HasPrefix(m.FromUserName, "@@")
|
return strings.HasPrefix(m.FromUserName, "@@")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回复消息
|
// 回复消息
|
||||||
func (m *Message) Reply(msgType int, content, mediaId string) (*SentMessage, error) {
|
func (m *Message) Reply(msgType int, content, mediaId string) (*SentMessage, error) {
|
||||||
msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
|
msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
|
||||||
info := m.Bot.storage.LoginInfo
|
info := m.Bot.storage.LoginInfo
|
||||||
request := m.Bot.storage.Request
|
request := m.Bot.storage.Request
|
||||||
return m.Bot.Caller.WebWxSendMsg(msg, info, request)
|
return m.Bot.Caller.WebWxSendMsg(msg, info, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回复文本消息
|
// 回复文本消息
|
||||||
func (m *Message) ReplyText(content string) (*SentMessage, error) {
|
func (m *Message) ReplyText(content string) (*SentMessage, error) {
|
||||||
return m.Reply(TextMessage, content, "")
|
return m.Reply(TextMessage, content, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回复图片消息
|
// 回复图片消息
|
||||||
func (m *Message) ReplyImage(file *os.File) (*SentMessage, error) {
|
func (m *Message) ReplyImage(file *os.File) (*SentMessage, error) {
|
||||||
info := m.Bot.storage.LoginInfo
|
info := m.Bot.storage.LoginInfo
|
||||||
request := m.Bot.storage.Request
|
request := m.Bot.storage.Request
|
||||||
return m.Bot.Caller.WebWxSendImageMsg(file, request, info, m.Bot.self.UserName, m.FromUserName)
|
return m.Bot.Caller.WebWxSendImageMsg(file, request, info, m.Bot.self.UserName, m.FromUserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsText() bool {
|
func (m *Message) IsText() bool {
|
||||||
return m.MsgType == 1 && m.Url == ""
|
return m.MsgType == 1 && m.Url == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsMap() bool {
|
func (m *Message) IsMap() bool {
|
||||||
return m.MsgType == 1 && m.Url != ""
|
return m.MsgType == 1 && m.Url != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsPicture() bool {
|
func (m *Message) IsPicture() bool {
|
||||||
return m.MsgType == 3 || m.MsgType == 47
|
return m.MsgType == 3 || m.MsgType == 47
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsVoice() bool {
|
func (m *Message) IsVoice() bool {
|
||||||
return m.MsgType == 34
|
return m.MsgType == 34
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsFriendAdd() bool {
|
func (m *Message) IsFriendAdd() bool {
|
||||||
return m.MsgType == 37 && m.FromUserName == "fmessage"
|
return m.MsgType == 37 && m.FromUserName == "fmessage"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsCard() bool {
|
func (m *Message) IsCard() bool {
|
||||||
return m.MsgType == 42
|
return m.MsgType == 42
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsVideo() bool {
|
func (m *Message) IsVideo() bool {
|
||||||
return m.MsgType == 43 || m.MsgType == 62
|
return m.MsgType == 43 || m.MsgType == 62
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsMedia() bool {
|
func (m *Message) IsMedia() bool {
|
||||||
return m.MsgType == 49
|
return m.MsgType == 49
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否撤回
|
// 判断是否撤回
|
||||||
func (m *Message) IsRecalled() bool {
|
func (m *Message) IsRecalled() bool {
|
||||||
return m.MsgType == 10002
|
return m.MsgType == 10002
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsSystem() bool {
|
func (m *Message) IsSystem() bool {
|
||||||
return m.MsgType == 10000
|
return m.MsgType == 10000
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsNotify() bool {
|
func (m *Message) IsNotify() bool {
|
||||||
return m.MsgType == 51 && m.StatusNotifyCode != 0
|
return m.MsgType == 51 && m.StatusNotifyCode != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断当前的消息是不是微信转账
|
// 判断当前的消息是不是微信转账
|
||||||
func (m *Message) IsTransferAccounts() bool {
|
func (m *Message) IsTransferAccounts() bool {
|
||||||
return m.IsMedia() && m.FileName == "微信转账"
|
return m.IsMedia() && m.FileName == "微信转账"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断当前是否发出红包
|
// 判断当前是否发出红包
|
||||||
func (m *Message) IsSendRedPacket() bool {
|
func (m *Message) IsSendRedPacket() bool {
|
||||||
return m.IsSystem() && m.Content == "发出红包,请在手机上查看"
|
return m.IsSystem() && m.Content == "发出红包,请在手机上查看"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断当前是否收到红包
|
// 判断当前是否收到红包
|
||||||
func (m *Message) IsReceiveRedPacket() bool {
|
func (m *Message) IsReceiveRedPacket() bool {
|
||||||
return m.IsSystem() && m.Content == "收到红包,请在手机上查看"
|
return m.IsSystem() && m.Content == "收到红包,请在手机上查看"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) IsSysNotice() bool {
|
func (m *Message) IsSysNotice() bool {
|
||||||
return m.MsgType == 9999
|
return m.MsgType == 9999
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为操作通知消息
|
// 判断是否为操作通知消息
|
||||||
func (m *Message) StatusNotify() bool {
|
func (m *Message) StatusNotify() bool {
|
||||||
return m.MsgType == 51
|
return m.MsgType == 51
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断消息是否为文件类型的消息
|
// 判断消息是否为文件类型的消息
|
||||||
func (m *Message) HasFile() bool {
|
func (m *Message) HasFile() bool {
|
||||||
return m.IsPicture() || m.IsVoice() || m.IsVideo() || m.IsMedia()
|
return m.IsPicture() || m.IsVoice() || m.IsVideo() || m.IsMedia()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件消息的文件
|
// 获取文件消息的文件
|
||||||
func (m *Message) GetFile() (*http.Response, error) {
|
func (m *Message) GetFile() (*http.Response, error) {
|
||||||
if !m.HasFile() {
|
if !m.HasFile() {
|
||||||
return nil, errors.New("invalid message type")
|
return nil, errors.New("invalid message type")
|
||||||
}
|
}
|
||||||
if m.IsPicture() {
|
if m.IsPicture() {
|
||||||
return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.LoginInfo)
|
return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.LoginInfo)
|
||||||
}
|
}
|
||||||
if m.IsVoice() {
|
if m.IsVoice() {
|
||||||
return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.LoginInfo)
|
return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.LoginInfo)
|
||||||
}
|
}
|
||||||
if m.IsVideo() {
|
if m.IsVideo() {
|
||||||
return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.LoginInfo)
|
return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.LoginInfo)
|
||||||
}
|
}
|
||||||
if m.IsMedia() {
|
if m.IsMedia() {
|
||||||
return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.LoginInfo)
|
return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.LoginInfo)
|
||||||
}
|
}
|
||||||
return nil, errors.New("unsupported type")
|
return nil, errors.New("unsupported type")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取card类型
|
// 获取card类型
|
||||||
func (m *Message) Card() (*Card, error) {
|
func (m *Message) Card() (*Card, error) {
|
||||||
if !m.IsCard() {
|
if !m.IsCard() {
|
||||||
return nil, errors.New("card message required")
|
return nil, errors.New("card message required")
|
||||||
}
|
}
|
||||||
var card Card
|
var card Card
|
||||||
content := XmlFormString(m.Content)
|
content := XmlFormString(m.Content)
|
||||||
err := xml.Unmarshal([]byte(content), &card)
|
err := xml.Unmarshal([]byte(content), &card)
|
||||||
return &card, err
|
return &card, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取FriendAddMessageContent内容
|
// 获取FriendAddMessageContent内容
|
||||||
func (m *Message) FriendAddMessageContent() (*FriendAddMessage, error) {
|
func (m *Message) FriendAddMessageContent() (*FriendAddMessage, error) {
|
||||||
if !m.IsFriendAdd() {
|
if !m.IsFriendAdd() {
|
||||||
return nil, errors.New("friend add message required")
|
return nil, errors.New("friend add message required")
|
||||||
}
|
}
|
||||||
var f FriendAddMessage
|
var f FriendAddMessage
|
||||||
content := XmlFormString(m.Content)
|
content := XmlFormString(m.Content)
|
||||||
err := xml.Unmarshal([]byte(content), &f)
|
err := xml.Unmarshal([]byte(content), &f)
|
||||||
return &f, err
|
return &f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取撤回消息的内容
|
// 获取撤回消息的内容
|
||||||
func (m *Message) RevokeMsg() (*RevokeMsg, error) {
|
func (m *Message) RevokeMsg() (*RevokeMsg, error) {
|
||||||
if !m.IsRecalled() {
|
if !m.IsRecalled() {
|
||||||
return nil, errors.New("recalled message required")
|
return nil, errors.New("recalled message required")
|
||||||
}
|
}
|
||||||
var r RevokeMsg
|
var r RevokeMsg
|
||||||
content := XmlFormString(m.Content)
|
content := XmlFormString(m.Content)
|
||||||
err := xml.Unmarshal([]byte(content), &r)
|
err := xml.Unmarshal([]byte(content), &r)
|
||||||
return &r, err
|
return &r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同意好友的请求
|
// 同意好友的请求
|
||||||
func (m *Message) Agree(verifyContents ...string) error {
|
func (m *Message) Agree(verifyContents ...string) error {
|
||||||
if !m.IsFriendAdd() {
|
if !m.IsFriendAdd() {
|
||||||
return fmt.Errorf("friend add message required")
|
return fmt.Errorf("friend add message required")
|
||||||
}
|
}
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
for _, v := range verifyContents {
|
for _, v := range verifyContents {
|
||||||
builder.WriteString(v)
|
builder.WriteString(v)
|
||||||
}
|
}
|
||||||
return m.Bot.Caller.WebWxVerifyUser(m.Bot.storage, m.RecommendInfo, builder.String())
|
return m.Bot.Caller.WebWxVerifyUser(m.Bot.storage, m.RecommendInfo, builder.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 往消息上下文中设置值
|
// 往消息上下文中设置值
|
||||||
// goroutine safe
|
// goroutine safe
|
||||||
func (m *Message) Set(key string, value interface{}) {
|
func (m *Message) Set(key string, value interface{}) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
if m.item == nil {
|
if m.item == nil {
|
||||||
m.item = make(map[string]interface{})
|
m.item = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
m.item[key] = value
|
m.item[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从消息上下文中获取值
|
// 从消息上下文中获取值
|
||||||
// goroutine safe
|
// goroutine safe
|
||||||
func (m *Message) Get(key string) (value interface{}, exist bool) {
|
func (m *Message) Get(key string) (value interface{}, exist bool) {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
value, exist = m.item[key]
|
value, exist = m.item[key]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 消息初始化,根据不同的消息作出不同的处理
|
// 消息初始化,根据不同的消息作出不同的处理
|
||||||
func (m *Message) init(bot *Bot) {
|
func (m *Message) init(bot *Bot) {
|
||||||
m.Bot = bot
|
m.Bot = bot
|
||||||
if m.IsSendByGroup() {
|
if m.IsSendByGroup() {
|
||||||
data := strings.Split(m.Content, ":<br/>")
|
data := strings.Split(m.Content, ":<br/>")
|
||||||
m.Content = strings.Join(data[1:], "")
|
m.Content = strings.Join(data[1:], "")
|
||||||
m.senderInGroupUserName = data[0]
|
m.senderInGroupUserName = data[0]
|
||||||
receiver, err := m.Receiver()
|
receiver, err := m.Receiver()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
displayName := receiver.DisplayName
|
displayName := receiver.DisplayName
|
||||||
if displayName == "" {
|
if displayName == "" {
|
||||||
displayName = receiver.NickName
|
displayName = receiver.NickName
|
||||||
}
|
}
|
||||||
atFlag := "@" + displayName
|
atFlag := "@" + displayName
|
||||||
index := len(atFlag) + 1 + 1
|
index := len(atFlag) + 1 + 1
|
||||||
if strings.HasPrefix(m.Content, atFlag) && unicode.IsSpace(rune(m.Content[index])) {
|
if strings.HasPrefix(m.Content, atFlag) && unicode.IsSpace(rune(m.Content[index])) {
|
||||||
m.IsAt = true
|
m.IsAt = true
|
||||||
m.Content = m.Content[index+1:]
|
m.Content = m.Content[index+1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息的结构体
|
// 发送消息的结构体
|
||||||
type SendMessage struct {
|
type SendMessage struct {
|
||||||
Type int
|
Type int
|
||||||
Content string
|
Content string
|
||||||
FromUserName string
|
FromUserName string
|
||||||
ToUserName string
|
ToUserName string
|
||||||
LocalID string
|
LocalID string
|
||||||
ClientMsgId string
|
ClientMsgId string
|
||||||
MediaId string
|
MediaId string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage的构造方法
|
// SendMessage的构造方法
|
||||||
func NewSendMessage(msgType int, content, fromUserName, toUserName, mediaId string) *SendMessage {
|
func NewSendMessage(msgType int, content, fromUserName, toUserName, mediaId string) *SendMessage {
|
||||||
id := strconv.FormatInt(time.Now().UnixNano()/1e2, 10)
|
id := strconv.FormatInt(time.Now().UnixNano()/1e2, 10)
|
||||||
return &SendMessage{
|
return &SendMessage{
|
||||||
Type: msgType,
|
Type: msgType,
|
||||||
Content: content,
|
Content: content,
|
||||||
FromUserName: fromUserName,
|
FromUserName: fromUserName,
|
||||||
ToUserName: toUserName,
|
ToUserName: toUserName,
|
||||||
LocalID: id,
|
LocalID: id,
|
||||||
ClientMsgId: id,
|
ClientMsgId: id,
|
||||||
MediaId: mediaId,
|
MediaId: mediaId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文本消息的构造方法
|
// 文本消息的构造方法
|
||||||
func NewTextSendMessage(content, fromUserName, toUserName string) *SendMessage {
|
func NewTextSendMessage(content, fromUserName, toUserName string) *SendMessage {
|
||||||
return NewSendMessage(TextMessage, content, fromUserName, toUserName, "")
|
return NewSendMessage(TextMessage, content, fromUserName, toUserName, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 媒体消息的构造方法
|
// 媒体消息的构造方法
|
||||||
func NewMediaSendMessage(msgType int, fromUserName, toUserName, mediaId string) *SendMessage {
|
func NewMediaSendMessage(msgType int, fromUserName, toUserName, mediaId string) *SendMessage {
|
||||||
return NewSendMessage(msgType, "", fromUserName, toUserName, mediaId)
|
return NewSendMessage(msgType, "", fromUserName, toUserName, mediaId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 一些特殊类型的消息会携带该结构体信息
|
// 一些特殊类型的消息会携带该结构体信息
|
||||||
type RecommendInfo struct {
|
type RecommendInfo struct {
|
||||||
OpCode int
|
OpCode int
|
||||||
Scene int
|
Scene int
|
||||||
Sex int
|
Sex int
|
||||||
VerifyFlag int
|
VerifyFlag int
|
||||||
AttrStatus int64
|
AttrStatus int64
|
||||||
QQNum int64
|
QQNum int64
|
||||||
Alias string
|
Alias string
|
||||||
City string
|
City string
|
||||||
Content string
|
Content string
|
||||||
NickName string
|
NickName string
|
||||||
Province string
|
Province string
|
||||||
Signature string
|
Signature string
|
||||||
Ticket string
|
Ticket string
|
||||||
UserName string
|
UserName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名片消息内容
|
// 名片消息内容
|
||||||
type Card struct {
|
type Card struct {
|
||||||
XMLName xml.Name `xml:"msg"`
|
XMLName xml.Name `xml:"msg"`
|
||||||
ImageStatus int `xml:"imagestatus,attr"`
|
ImageStatus int `xml:"imagestatus,attr"`
|
||||||
Scene int `xml:"scene,attr"`
|
Scene int `xml:"scene,attr"`
|
||||||
Sex int `xml:"sex,attr"`
|
Sex int `xml:"sex,attr"`
|
||||||
Certflag int `xml:"certflag,attr"`
|
Certflag int `xml:"certflag,attr"`
|
||||||
BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
|
BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
|
||||||
SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
|
SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
|
||||||
UserName string `xml:"username,attr"`
|
UserName string `xml:"username,attr"`
|
||||||
NickName string `xml:"nickname,attr"`
|
NickName string `xml:"nickname,attr"`
|
||||||
ShortPy string `xml:"shortpy,attr"`
|
ShortPy string `xml:"shortpy,attr"`
|
||||||
Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号
|
Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号
|
||||||
Province string `xml:"province,attr"`
|
Province string `xml:"province,attr"`
|
||||||
City string `xml:"city,attr"`
|
City string `xml:"city,attr"`
|
||||||
Sign string `xml:"sign,attr"`
|
Sign string `xml:"sign,attr"`
|
||||||
Certinfo string `xml:"certinfo,attr"`
|
Certinfo string `xml:"certinfo,attr"`
|
||||||
BrandIconUrl string `xml:"brandIconUrl,attr"`
|
BrandIconUrl string `xml:"brandIconUrl,attr"`
|
||||||
BrandHomeUr string `xml:"brandHomeUr,attr"`
|
BrandHomeUr string `xml:"brandHomeUr,attr"`
|
||||||
BrandSubscriptConfigUrl string `xml:"brandSubscriptConfigUrl,attr"`
|
BrandSubscriptConfigUrl string `xml:"brandSubscriptConfigUrl,attr"`
|
||||||
BrandFlags string `xml:"brandFlags,attr"`
|
BrandFlags string `xml:"brandFlags,attr"`
|
||||||
RegionCode string `xml:"regionCode,attr"`
|
RegionCode string `xml:"regionCode,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 好友添加消息信息内容
|
// 好友添加消息信息内容
|
||||||
type FriendAddMessage struct {
|
type FriendAddMessage struct {
|
||||||
XMLName xml.Name `xml:"msg"`
|
XMLName xml.Name `xml:"msg"`
|
||||||
Shortpy int `xml:"shortpy,attr"`
|
Shortpy int `xml:"shortpy,attr"`
|
||||||
ImageStatus int `xml:"imagestatus,attr"`
|
ImageStatus int `xml:"imagestatus,attr"`
|
||||||
Scene int `xml:"scene,attr"`
|
Scene int `xml:"scene,attr"`
|
||||||
PerCard int `xml:"percard,attr"`
|
PerCard int `xml:"percard,attr"`
|
||||||
Sex int `xml:"sex,attr"`
|
Sex int `xml:"sex,attr"`
|
||||||
AlbumFlag int `xml:"albumflag,attr"`
|
AlbumFlag int `xml:"albumflag,attr"`
|
||||||
AlbumStyle int `xml:"albumstyle,attr"`
|
AlbumStyle int `xml:"albumstyle,attr"`
|
||||||
SnsFlag int `xml:"snsflag,attr"`
|
SnsFlag int `xml:"snsflag,attr"`
|
||||||
Opcode int `xml:"opcode,attr"`
|
Opcode int `xml:"opcode,attr"`
|
||||||
FromUserName string `xml:"fromusername,attr"`
|
FromUserName string `xml:"fromusername,attr"`
|
||||||
EncryptUserName string `xml:"encryptusername,attr"`
|
EncryptUserName string `xml:"encryptusername,attr"`
|
||||||
FromNickName string `xml:"fromnickname,attr"`
|
FromNickName string `xml:"fromnickname,attr"`
|
||||||
Content string `xml:"content,attr"`
|
Content string `xml:"content,attr"`
|
||||||
Country string `xml:"country,attr"`
|
Country string `xml:"country,attr"`
|
||||||
Province string `xml:"province,attr"`
|
Province string `xml:"province,attr"`
|
||||||
City string `xml:"city,attr"`
|
City string `xml:"city,attr"`
|
||||||
Sign string `xml:"sign,attr"`
|
Sign string `xml:"sign,attr"`
|
||||||
Alias string `xml:"alias,attr"`
|
Alias string `xml:"alias,attr"`
|
||||||
WeiBo string `xml:"weibo,attr"`
|
WeiBo string `xml:"weibo,attr"`
|
||||||
AlbumBgImgId string `xml:"albumbgimgid,attr"`
|
AlbumBgImgId string `xml:"albumbgimgid,attr"`
|
||||||
SnsBgImgId string `xml:"snsbgimgid,attr"`
|
SnsBgImgId string `xml:"snsbgimgid,attr"`
|
||||||
SnsBgObjectId string `xml:"snsbgobjectid,attr"`
|
SnsBgObjectId string `xml:"snsbgobjectid,attr"`
|
||||||
MHash string `xml:"mhash,attr"`
|
MHash string `xml:"mhash,attr"`
|
||||||
MFullHash string `xml:"mfullhash,attr"`
|
MFullHash string `xml:"mfullhash,attr"`
|
||||||
BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
|
BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
|
||||||
SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
|
SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
|
||||||
Ticket string `xml:"ticket,attr"`
|
Ticket string `xml:"ticket,attr"`
|
||||||
GoogleContact string `xml:"googlecontact,attr"`
|
GoogleContact string `xml:"googlecontact,attr"`
|
||||||
QrTicket string `xml:"qrticket,attr"`
|
QrTicket string `xml:"qrticket,attr"`
|
||||||
ChatRoomUserName string `xml:"chatroomusername,attr"`
|
ChatRoomUserName string `xml:"chatroomusername,attr"`
|
||||||
SourceUserName string `xml:"sourceusername,attr"`
|
SourceUserName string `xml:"sourceusername,attr"`
|
||||||
ShareCardUserName string `xml:"sharecardusername,attr"`
|
ShareCardUserName string `xml:"sharecardusername,attr"`
|
||||||
ShareCardNickName string `xml:"sharecardnickname,attr"`
|
ShareCardNickName string `xml:"sharecardnickname,attr"`
|
||||||
CardVersion string `xml:"cardversion,attr"`
|
CardVersion string `xml:"cardversion,attr"`
|
||||||
BrandList struct {
|
BrandList struct {
|
||||||
Count int `xml:"count,attr"`
|
Count int `xml:"count,attr"`
|
||||||
Ver int64 `xml:"ver,attr"`
|
Ver int64 `xml:"ver,attr"`
|
||||||
} `xml:"brandlist"`
|
} `xml:"brandlist"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 撤回消息Content
|
// 撤回消息Content
|
||||||
type RevokeMsg struct {
|
type RevokeMsg struct {
|
||||||
SysMsg xml.Name `xml:"sysmsg"`
|
SysMsg xml.Name `xml:"sysmsg"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
RevokeMsg struct {
|
RevokeMsg struct {
|
||||||
OldMsgId int64 `xml:"oldmsgid"`
|
OldMsgId int64 `xml:"oldmsgid"`
|
||||||
MsgId int64 `xml:"msgid"`
|
MsgId int64 `xml:"msgid"`
|
||||||
Session string `xml:"session"`
|
Session string `xml:"session"`
|
||||||
ReplaceMsg string `xml:"replacemsg"`
|
ReplaceMsg string `xml:"replacemsg"`
|
||||||
} `xml:"revokemsg"`
|
} `xml:"revokemsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已发送的信息
|
// 已发送的信息
|
||||||
type SentMessage struct {
|
type SentMessage struct {
|
||||||
*SendMessage
|
*SendMessage
|
||||||
Self *Self
|
Self *Self
|
||||||
MsgId string
|
MsgId string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 撤回该消息
|
// 撤回该消息
|
||||||
func (s *SentMessage) Revoke() error {
|
func (s *SentMessage) Revoke() error {
|
||||||
return s.Self.RevokeMessage(s)
|
return s.Self.RevokeMessage(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileAppMessage struct {
|
||||||
|
AppMsg xml.Name `xml:"appmsg"`
|
||||||
|
Type int `xml:"type"`
|
||||||
|
AppId string `xml:"appid,attr"` // wxeb7ec651dd0aefa9
|
||||||
|
SdkVer string `xml:"sdkver,attr"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Des string `xml:"des"`
|
||||||
|
Action string `xml:"action"`
|
||||||
|
Content string `xml:"content"`
|
||||||
|
Url string `xml:"url"`
|
||||||
|
LowUrl string `xml:"lowurl"`
|
||||||
|
ExtInfo string `xml:"extinfo"`
|
||||||
|
AppAttach struct {
|
||||||
|
TotalLen int64 `xml:"totallen"`
|
||||||
|
AttachId string `xml:"attachid"`
|
||||||
|
FileExt string `xml:"fileext"`
|
||||||
|
} `xml:"appattach"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileAppMessage) XmlByte() ([]byte, error) {
|
||||||
|
return xml.Marshal(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileAppMessage(stat os.FileInfo, attachId string) *FileAppMessage {
|
||||||
|
m := &FileAppMessage{AppId: "wxeb7ec651dd0aefa9", Title: stat.Name()}
|
||||||
|
m.AppAttach.AttachId = attachId
|
||||||
|
m.AppAttach.TotalLen = stat.Size()
|
||||||
|
m.AppAttach.FileExt = getFileExt(stat.Name())
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
90
parser.go
90
parser.go
@ -1,66 +1,74 @@
|
|||||||
package openwechat
|
package openwechat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToBuffer(v interface{}) (*bytes.Buffer, error) {
|
func ToBuffer(v interface{}) (*bytes.Buffer, error) {
|
||||||
buf, err := json.Marshal(v)
|
buf, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return bytes.NewBuffer(buf), nil
|
return bytes.NewBuffer(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取随机设备id
|
// 获取随机设备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
|
||||||
builder.WriteString("e")
|
builder.WriteString("e")
|
||||||
for i := 0; i < 15; i++ {
|
for i := 0; i < 15; i++ {
|
||||||
r := rand.Intn(9)
|
r := rand.Intn(9)
|
||||||
builder.WriteString(strconv.Itoa(r))
|
builder.WriteString(strconv.Itoa(r))
|
||||||
}
|
}
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWebWxDataTicket(cookies []*http.Cookie) string {
|
func getWebWxDataTicket(cookies []*http.Cookie) string {
|
||||||
for _, cookie := range cookies {
|
for _, cookie := range cookies {
|
||||||
if cookie.Name == "webwx_data_ticket" {
|
if cookie.Name == "webwx_data_ticket" {
|
||||||
return cookie.Value
|
return cookie.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Form Xml 格式化
|
// Form Xml 格式化
|
||||||
func XmlFormString(text string) string {
|
func XmlFormString(text string) string {
|
||||||
lt := strings.ReplaceAll(text, "<", "<")
|
lt := strings.ReplaceAll(text, "<", "<")
|
||||||
gt := strings.ReplaceAll(lt, ">", ">")
|
gt := strings.ReplaceAll(lt, ">", ">")
|
||||||
br := strings.ReplaceAll(gt, "<br/>", "\n")
|
br := strings.ReplaceAll(gt, "<br/>", "\n")
|
||||||
return strings.ReplaceAll(br, "&amp;", "&")
|
return strings.ReplaceAll(br, "&amp;", "&")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTotalDuration(delay ...time.Duration) time.Duration {
|
func getTotalDuration(delay ...time.Duration) time.Duration {
|
||||||
var total time.Duration
|
var total time.Duration
|
||||||
for _, d := range delay {
|
for _, d := range delay {
|
||||||
total += d
|
total += d
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件上传的类型
|
// 获取文件上传的类型
|
||||||
func GetFileContentType(file multipart.File) (string, error) {
|
func GetFileContentType(file multipart.File) (string, error) {
|
||||||
data := make([]byte, 512)
|
data := make([]byte, 512)
|
||||||
if _, err := file.Read(data); err != nil {
|
if _, err := file.Read(data); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return http.DetectContentType(data), nil
|
return http.DetectContentType(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileExt(name string) string {
|
||||||
|
results := strings.Split(name, ".")
|
||||||
|
if len(results) == 0 {
|
||||||
|
return "undefined"
|
||||||
|
}
|
||||||
|
return results[len(results)-1]
|
||||||
}
|
}
|
||||||
|
3
url.go
3
url.go
@ -23,6 +23,7 @@ type UrlManager struct {
|
|||||||
webWxGetMediaUrl string
|
webWxGetMediaUrl string
|
||||||
webWxUpdateChatRoomUrl string
|
webWxUpdateChatRoomUrl string
|
||||||
webWxRevokeMsg string
|
webWxRevokeMsg string
|
||||||
|
webWxCheckUploadUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -49,6 +50,7 @@ var (
|
|||||||
webWxGetMediaUrl: webWxGetMediaDesktopUrl,
|
webWxGetMediaUrl: webWxGetMediaDesktopUrl,
|
||||||
webWxUpdateChatRoomUrl: webWxUpdateChatRoomDesktopUrl,
|
webWxUpdateChatRoomUrl: webWxUpdateChatRoomDesktopUrl,
|
||||||
webWxRevokeMsg: webWxRevokeMsgDesktopUrl,
|
webWxRevokeMsg: webWxRevokeMsgDesktopUrl,
|
||||||
|
webWxCheckUploadUrl: webWxCheckUploadDesktopUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网页版
|
// 网页版
|
||||||
@ -74,6 +76,7 @@ var (
|
|||||||
webWxGetMediaUrl: webWxGetMediaNormalUrl,
|
webWxGetMediaUrl: webWxGetMediaNormalUrl,
|
||||||
webWxUpdateChatRoomUrl: webWxUpdateChatRoomNormalUrl,
|
webWxUpdateChatRoomUrl: webWxUpdateChatRoomNormalUrl,
|
||||||
webWxRevokeMsg: webWxRevokeMsgNormalUrl,
|
webWxRevokeMsg: webWxRevokeMsgNormalUrl,
|
||||||
|
webWxCheckUploadUrl: webWxCheckUploadNormalUrl,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
654
user.go
654
user.go
@ -1,336 +1,342 @@
|
|||||||
package openwechat
|
package openwechat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 抽象的用户结构: 好友 群组 公众号
|
// 抽象的用户结构: 好友 群组 公众号
|
||||||
type User struct {
|
type User struct {
|
||||||
Uin int
|
Uin int
|
||||||
HideInputBarFlag int
|
HideInputBarFlag int
|
||||||
StarFriend int
|
StarFriend int
|
||||||
Sex int
|
Sex int
|
||||||
AppAccountFlag int
|
AppAccountFlag int
|
||||||
VerifyFlag int
|
VerifyFlag int
|
||||||
ContactFlag int
|
ContactFlag int
|
||||||
WebWxPluginSwitch int
|
WebWxPluginSwitch int
|
||||||
HeadImgFlag int
|
HeadImgFlag int
|
||||||
SnsFlag int
|
SnsFlag int
|
||||||
IsOwner int
|
IsOwner int
|
||||||
MemberCount int
|
MemberCount int
|
||||||
ChatRoomId int
|
ChatRoomId int
|
||||||
UniFriend int
|
UniFriend int
|
||||||
OwnerUin int
|
OwnerUin int
|
||||||
Statues int
|
Statues int
|
||||||
AttrStatus int
|
AttrStatus int
|
||||||
Province string
|
Province string
|
||||||
City string
|
City string
|
||||||
Alias string
|
Alias string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
KeyWord string
|
KeyWord string
|
||||||
EncryChatRoomId string
|
EncryChatRoomId string
|
||||||
UserName string
|
UserName string
|
||||||
NickName string
|
NickName string
|
||||||
HeadImgUrl string
|
HeadImgUrl string
|
||||||
RemarkName string
|
RemarkName string
|
||||||
PYInitial string
|
PYInitial string
|
||||||
PYQuanPin string
|
PYQuanPin string
|
||||||
RemarkPYInitial string
|
RemarkPYInitial string
|
||||||
RemarkPYQuanPin string
|
RemarkPYQuanPin string
|
||||||
Signature string
|
Signature string
|
||||||
|
|
||||||
MemberList Members
|
MemberList Members
|
||||||
|
|
||||||
Self *Self
|
Self *Self
|
||||||
}
|
}
|
||||||
|
|
||||||
// implement fmt.Stringer
|
// implement fmt.Stringer
|
||||||
func (u *User) String() string {
|
func (u *User) String() string {
|
||||||
return fmt.Sprintf("<User:%s>", u.NickName)
|
return fmt.Sprintf("<User:%s>", u.NickName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户头像
|
// 获取用户头像
|
||||||
func (u *User) GetAvatarResponse() (*http.Response, error) {
|
func (u *User) GetAvatarResponse() (*http.Response, error) {
|
||||||
return u.Self.Bot.Caller.Client.WebWxGetHeadImg(u.HeadImgUrl)
|
return u.Self.Bot.Caller.Client.WebWxGetHeadImg(u.HeadImgUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载用户头像
|
// 下载用户头像
|
||||||
func (u *User) SaveAvatar(filename string) error {
|
func (u *User) SaveAvatar(filename string) error {
|
||||||
resp, err := u.GetAvatarResponse()
|
resp, err := u.GetAvatarResponse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
if _, err := buffer.ReadFrom(resp.Body); err != nil {
|
if _, err := buffer.ReadFrom(resp.Body); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
file, err := os.Create(filename)
|
file, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
_, err = file.Write(buffer.Bytes())
|
_, err = file.Write(buffer.Bytes())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户的详情
|
// 获取用户的详情
|
||||||
func (u *User) Detail() (*User, error) {
|
func (u *User) Detail() (*User, error) {
|
||||||
members := Members{u}
|
members := Members{u}
|
||||||
request := u.Self.Bot.storage.Request
|
request := u.Self.Bot.storage.Request
|
||||||
newMembers, err := u.Self.Bot.Caller.WebWxBatchGetContact(members, request)
|
newMembers, err := u.Self.Bot.Caller.WebWxBatchGetContact(members, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
user := newMembers.First()
|
user := newMembers.First()
|
||||||
user.Self = u.Self
|
user.Self = u.Self
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为好友
|
// 判断是否为好友
|
||||||
func (u *User) IsFriend() bool {
|
func (u *User) IsFriend() bool {
|
||||||
return !u.IsGroup() && strings.HasPrefix(u.UserName, "@") && u.VerifyFlag == 0
|
return !u.IsGroup() && strings.HasPrefix(u.UserName, "@") && u.VerifyFlag == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为群组
|
// 判断是否为群组
|
||||||
func (u *User) IsGroup() bool {
|
func (u *User) IsGroup() bool {
|
||||||
return strings.HasPrefix(u.UserName, "@@") && u.VerifyFlag == 0
|
return strings.HasPrefix(u.UserName, "@@") && u.VerifyFlag == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为公众号
|
// 判断是否为公众号
|
||||||
func (u *User) IsMP() bool {
|
func (u *User) IsMP() bool {
|
||||||
return u.VerifyFlag == 8 || u.VerifyFlag == 24 || u.VerifyFlag == 136
|
return u.VerifyFlag == 8 || u.VerifyFlag == 24 || u.VerifyFlag == 136
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自己,当前登录用户对象
|
// 自己,当前登录用户对象
|
||||||
type Self struct {
|
type Self struct {
|
||||||
*User
|
*User
|
||||||
Bot *Bot
|
Bot *Bot
|
||||||
fileHelper *Friend
|
fileHelper *Friend
|
||||||
members Members
|
members Members
|
||||||
friends Friends
|
friends Friends
|
||||||
groups Groups
|
groups Groups
|
||||||
mps Mps
|
mps Mps
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有的好友、群组、公众号信息
|
// 获取所有的好友、群组、公众号信息
|
||||||
func (s *Self) Members(update ...bool) (Members, error) {
|
func (s *Self) Members(update ...bool) (Members, error) {
|
||||||
// 首先判断缓存里有没有,如果没有则去更新缓存
|
// 首先判断缓存里有没有,如果没有则去更新缓存
|
||||||
if s.members == nil {
|
if s.members == nil {
|
||||||
if err := s.updateMembers(); err != nil {
|
if err := s.updateMembers(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return s.members, nil
|
return s.members, nil
|
||||||
}
|
}
|
||||||
// 判断是否需要更新,如果传入的参数不为nil,则取最后一个
|
// 判断是否需要更新,如果传入的参数不为nil,则取最后一个
|
||||||
if len(update) > 0 && update[0] {
|
if len(update) > 0 && update[0] {
|
||||||
if err := s.updateMembers(); err != nil {
|
if err := s.updateMembers(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s.members, nil
|
return s.members, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新联系人处理
|
// 更新联系人处理
|
||||||
func (s *Self) updateMembers() error {
|
func (s *Self) updateMembers() error {
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
members, err := s.Bot.Caller.WebWxGetContact(info)
|
members, err := s.Bot.Caller.WebWxGetContact(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
members.SetOwner(s)
|
members.SetOwner(s)
|
||||||
s.members = members
|
s.members = members
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件传输助手对象,封装成Friend返回
|
// 获取文件传输助手对象,封装成Friend返回
|
||||||
// fh, err := self.FileHelper() // or fh := openwechat.NewFriendHelper(self)
|
// fh, err := self.FileHelper() // or fh := openwechat.NewFriendHelper(self)
|
||||||
func (s *Self) FileHelper() (*Friend, error) {
|
func (s *Self) FileHelper() (*Friend, error) {
|
||||||
// 如果缓存里有,直接返回,否则去联系人里面找
|
// 如果缓存里有,直接返回,否则去联系人里面找
|
||||||
if s.fileHelper != nil {
|
if s.fileHelper != nil {
|
||||||
return s.fileHelper, nil
|
return s.fileHelper, nil
|
||||||
}
|
}
|
||||||
members, err := s.Members()
|
members, err := s.Members()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
users := members.SearchByUserName(1, "filehelper")
|
users := members.SearchByUserName(1, "filehelper")
|
||||||
if users == nil {
|
if users == nil {
|
||||||
return NewFriendHelper(s), nil
|
return NewFriendHelper(s), nil
|
||||||
}
|
}
|
||||||
s.fileHelper = &Friend{users.First()}
|
s.fileHelper = &Friend{users.First()}
|
||||||
return s.fileHelper, nil
|
return s.fileHelper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有的好友
|
// 获取所有的好友
|
||||||
func (s *Self) Friends(update ...bool) (Friends, error) {
|
func (s *Self) Friends(update ...bool) (Friends, error) {
|
||||||
if s.friends == nil || (len(update) > 0 && update[0]) {
|
if s.friends == nil || (len(update) > 0 && update[0]) {
|
||||||
if _, err := s.Members(true); err != nil {
|
if _, err := s.Members(true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.friends = s.members.Friends()
|
s.friends = s.members.Friends()
|
||||||
}
|
}
|
||||||
return s.friends, nil
|
return s.friends, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有的群组
|
// 获取所有的群组
|
||||||
func (s *Self) Groups(update ...bool) (Groups, error) {
|
func (s *Self) Groups(update ...bool) (Groups, error) {
|
||||||
if s.groups == nil || (len(update) > 0 && update[0]) {
|
if s.groups == nil || (len(update) > 0 && update[0]) {
|
||||||
if _, err := s.Members(true); err != nil {
|
if _, err := s.Members(true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.groups = s.members.Groups()
|
s.groups = s.members.Groups()
|
||||||
}
|
}
|
||||||
return s.groups, nil
|
return s.groups, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有的公众号
|
// 获取所有的公众号
|
||||||
func (s *Self) Mps(update ...bool) (Mps, error) {
|
func (s *Self) Mps(update ...bool) (Mps, error) {
|
||||||
if s.mps == nil || (len(update) > 0 && update[0]) {
|
if s.mps == nil || (len(update) > 0 && update[0]) {
|
||||||
if _, err := s.Members(true); err != nil {
|
if _, err := s.Members(true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.mps = s.members.MPs()
|
s.mps = s.members.MPs()
|
||||||
}
|
}
|
||||||
return s.mps, nil
|
return s.mps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新所有的联系人信息
|
// 更新所有的联系人信息
|
||||||
func (s *Self) UpdateMembersDetail() error {
|
func (s *Self) UpdateMembersDetail() error {
|
||||||
// 先获取所有的联系人
|
// 先获取所有的联系人
|
||||||
members, err := s.Members()
|
members, err := s.Members()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return members.detail(s)
|
return members.detail(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 抽象发送消息接口
|
// 抽象发送消息接口
|
||||||
func (s *Self) sendMessageToUser(user *User, msg *SendMessage) (*SentMessage, error) {
|
func (s *Self) sendMessageToUser(user *User, msg *SendMessage) (*SentMessage, error) {
|
||||||
msg.FromUserName = s.UserName
|
msg.FromUserName = s.UserName
|
||||||
msg.ToUserName = user.UserName
|
msg.ToUserName = user.UserName
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
request := s.Bot.storage.Request
|
request := s.Bot.storage.Request
|
||||||
successSendMessage, err := s.Bot.Caller.WebWxSendMsg(msg, info, request)
|
successSendMessage, err := s.Bot.Caller.WebWxSendMsg(msg, info, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
successSendMessage.Self = s
|
successSendMessage.Self = s
|
||||||
return successSendMessage, nil
|
return successSendMessage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息给好友
|
// 发送消息给好友
|
||||||
func (s *Self) SendMessageToFriend(friend *Friend, msg *SendMessage) (*SentMessage, error) {
|
func (s *Self) SendMessageToFriend(friend *Friend, msg *SendMessage) (*SentMessage, error) {
|
||||||
return s.sendMessageToUser(friend.User, msg)
|
return s.sendMessageToUser(friend.User, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送文本消息给好友
|
// 发送文本消息给好友
|
||||||
func (s *Self) SendTextToFriend(friend *Friend, text string) (*SentMessage, error) {
|
func (s *Self) SendTextToFriend(friend *Friend, text string) (*SentMessage, error) {
|
||||||
msg := NewTextSendMessage(text, s.UserName, friend.UserName)
|
msg := NewTextSendMessage(text, s.UserName, friend.UserName)
|
||||||
return s.SendMessageToFriend(friend, msg)
|
return s.SendMessageToFriend(friend, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送图片消息给好友
|
// 发送图片消息给好友
|
||||||
func (s *Self) SendImageToFriend(friend *Friend, file *os.File) (*SentMessage, error) {
|
func (s *Self) SendImageToFriend(friend *Friend, file *os.File) (*SentMessage, error) {
|
||||||
req := s.Bot.storage.Request
|
req := s.Bot.storage.Request
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
return s.Bot.Caller.WebWxSendImageMsg(file, req, info, s.UserName, friend.UserName)
|
return s.Bot.Caller.WebWxSendImageMsg(file, req, info, s.UserName, friend.UserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Self) SendFileToFriend(friend *Friend, file *os.File) (*SentMessage, error) {
|
||||||
|
req := s.Bot.storage.Request
|
||||||
|
info := s.Bot.storage.LoginInfo
|
||||||
|
return s.Bot.Caller.WebWxSendFile(file, req, info, s.UserName, friend.UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置好友备注
|
// 设置好友备注
|
||||||
// self.SetRemarkNameToFriend(friend, "remark") // or friend.SetRemarkName("remark")
|
// self.SetRemarkNameToFriend(friend, "remark") // or friend.SetRemarkName("remark")
|
||||||
func (s *Self) SetRemarkNameToFriend(friend *Friend, remarkName string) error {
|
func (s *Self) SetRemarkNameToFriend(friend *Friend, remarkName string) error {
|
||||||
req := s.Bot.storage.Request
|
req := s.Bot.storage.Request
|
||||||
return s.Bot.Caller.WebWxOplog(req, remarkName, friend.UserName)
|
return s.Bot.Caller.WebWxOplog(req, remarkName, friend.UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拉多名好友进群
|
// 拉多名好友进群
|
||||||
// 最好自己是群主,成功率高一点,因为有的群允许非群组拉人,而有的群不允许
|
// 最好自己是群主,成功率高一点,因为有的群允许非群组拉人,而有的群不允许
|
||||||
func (s *Self) AddFriendsIntoGroup(group *Group, friends ...*Friend) error {
|
func (s *Self) AddFriendsIntoGroup(group *Group, friends ...*Friend) error {
|
||||||
if len(friends) == 0 {
|
if len(friends) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// 获取群的所有的群员
|
// 获取群的所有的群员
|
||||||
groupMembers, err := group.Members()
|
groupMembers, err := group.Members()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 判断当前的成员在不在群里面
|
// 判断当前的成员在不在群里面
|
||||||
for _, friend := range friends {
|
for _, friend := range friends {
|
||||||
for _, member := range groupMembers {
|
for _, member := range groupMembers {
|
||||||
if member.UserName == friend.UserName {
|
if member.UserName == friend.UserName {
|
||||||
return fmt.Errorf("user %s has alreay in this group", friend.String())
|
return fmt.Errorf("user %s has alreay in this group", friend.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req := s.Bot.storage.Request
|
req := s.Bot.storage.Request
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
return s.Bot.Caller.AddFriendIntoChatRoom(req, info, group, friends...)
|
return s.Bot.Caller.AddFriendIntoChatRoom(req, info, group, friends...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从群聊中移除用户
|
// 从群聊中移除用户
|
||||||
// Deprecated
|
// Deprecated
|
||||||
// 无论是网页版,还是程序上都不起作用
|
// 无论是网页版,还是程序上都不起作用
|
||||||
func (s *Self) RemoveMemberFromGroup(group *Group, members Members) error {
|
func (s *Self) RemoveMemberFromGroup(group *Group, members Members) error {
|
||||||
if len(members) == 0 {
|
if len(members) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if group.IsOwner == 0 {
|
if group.IsOwner == 0 {
|
||||||
return errors.New("group owner required")
|
return errors.New("group owner required")
|
||||||
}
|
}
|
||||||
groupMembers, err := group.Members()
|
groupMembers, err := group.Members()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 判断用户是否在群聊中
|
// 判断用户是否在群聊中
|
||||||
var count int
|
var count int
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
for _, gm := range groupMembers {
|
for _, gm := range groupMembers {
|
||||||
if gm.UserName == member.UserName {
|
if gm.UserName == member.UserName {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count != len(members) {
|
if count != len(members) {
|
||||||
return errors.New("invalid members")
|
return errors.New("invalid members")
|
||||||
}
|
}
|
||||||
req := s.Bot.storage.Request
|
req := s.Bot.storage.Request
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
return s.Bot.Caller.RemoveFriendFromChatRoom(req, info, group, members...)
|
return s.Bot.Caller.RemoveFriendFromChatRoom(req, info, group, members...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拉好友进多个群聊
|
// 拉好友进多个群聊
|
||||||
// AddFriendIntoGroups, 名字和上面的有点像
|
// AddFriendIntoGroups, 名字和上面的有点像
|
||||||
func (s *Self) AddFriendIntoManyGroups(friend *Friend, groups ...*Group) error {
|
func (s *Self) AddFriendIntoManyGroups(friend *Friend, groups ...*Group) error {
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if err := s.AddFriendsIntoGroup(group, friend); err != nil {
|
if err := s.AddFriendsIntoGroup(group, friend); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息给群组
|
// 发送消息给群组
|
||||||
func (s *Self) SendMessageToGroup(group *Group, msg *SendMessage) (*SentMessage, error) {
|
func (s *Self) SendMessageToGroup(group *Group, msg *SendMessage) (*SentMessage, error) {
|
||||||
return s.sendMessageToUser(group.User, msg)
|
return s.sendMessageToUser(group.User, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送文本消息给群组
|
// 发送文本消息给群组
|
||||||
func (s *Self) SendTextToGroup(group *Group, text string) (*SentMessage, error) {
|
func (s *Self) SendTextToGroup(group *Group, text string) (*SentMessage, error) {
|
||||||
msg := NewTextSendMessage(text, s.UserName, group.UserName)
|
msg := NewTextSendMessage(text, s.UserName, group.UserName)
|
||||||
return s.SendMessageToGroup(group, msg)
|
return s.SendMessageToGroup(group, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送图片消息给群组
|
// 发送图片消息给群组
|
||||||
func (s *Self) SendImageToGroup(group *Group, file *os.File) (*SentMessage, error) {
|
func (s *Self) SendImageToGroup(group *Group, file *os.File) (*SentMessage, error) {
|
||||||
req := s.Bot.storage.Request
|
req := s.Bot.storage.Request
|
||||||
info := s.Bot.storage.LoginInfo
|
info := s.Bot.storage.LoginInfo
|
||||||
return s.Bot.Caller.WebWxSendImageMsg(file, req, info, s.UserName, group.UserName)
|
return s.Bot.Caller.WebWxSendImageMsg(file, req, info, s.UserName, group.UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 撤回消息
|
// 撤回消息
|
||||||
@ -339,7 +345,7 @@ func (s *Self) SendImageToGroup(group *Group, file *os.File) (*SentMessage, erro
|
|||||||
// self.RevokeMessage(sentMessage) // or sentMessage.Revoke()
|
// self.RevokeMessage(sentMessage) // or sentMessage.Revoke()
|
||||||
// }
|
// }
|
||||||
func (s *Self) RevokeMessage(msg *SentMessage) error {
|
func (s *Self) RevokeMessage(msg *SentMessage) error {
|
||||||
return s.Bot.Caller.WebWxRevokeMsg(msg, s.Bot.storage.Request)
|
return s.Bot.Caller.WebWxRevokeMsg(msg, s.Bot.storage.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 抽象的用户组
|
// 抽象的用户组
|
||||||
@ -347,158 +353,158 @@ type Members []*User
|
|||||||
|
|
||||||
// 统计数量
|
// 统计数量
|
||||||
func (m Members) Count() int {
|
func (m Members) Count() int {
|
||||||
return len(m)
|
return len(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取第一个
|
// 获取第一个
|
||||||
func (m Members) First() *User {
|
func (m Members) First() *User {
|
||||||
if m.Count() > 0 {
|
if m.Count() > 0 {
|
||||||
u := m[0]
|
u := m[0]
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取最后一个
|
// 获取最后一个
|
||||||
func (m Members) Last() *User {
|
func (m Members) Last() *User {
|
||||||
if m.Count() > 0 {
|
if m.Count() > 0 {
|
||||||
u := m[m.Count()-1]
|
u := m[m.Count()-1]
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置owner
|
// 设置owner
|
||||||
// 请不要随意设置
|
// 请不要随意设置
|
||||||
func (m Members) SetOwner(s *Self) {
|
func (m Members) SetOwner(s *Self) {
|
||||||
for _, member := range m {
|
for _, member := range m {
|
||||||
member.Self = s
|
member.Self = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据用户名查找
|
// 根据用户名查找
|
||||||
func (m Members) SearchByUserName(limit int, username string) (results Members) {
|
func (m Members) SearchByUserName(limit int, username string) (results Members) {
|
||||||
return m.Search(limit, func(user *User) bool { return user.UserName == username })
|
return m.Search(limit, func(user *User) bool { return user.UserName == username })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据昵称查找
|
// 根据昵称查找
|
||||||
func (m Members) SearchByNickName(limit int, nickName string) (results Members) {
|
func (m Members) SearchByNickName(limit int, nickName string) (results Members) {
|
||||||
return m.Search(limit, func(user *User) bool { return user.NickName == nickName })
|
return m.Search(limit, func(user *User) bool { return user.NickName == nickName })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据备注查找
|
// 根据备注查找
|
||||||
func (m Members) SearchByRemarkName(limit int, remarkName string) (results Members) {
|
func (m Members) SearchByRemarkName(limit int, remarkName string) (results Members) {
|
||||||
return m.Search(limit, func(user *User) bool { return user.RemarkName == remarkName })
|
return m.Search(limit, func(user *User) bool { return user.RemarkName == remarkName })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据自定义条件查找
|
// 根据自定义条件查找
|
||||||
func (m Members) Search(limit int, condFuncList ...func(user *User) bool) (results Members) {
|
func (m Members) Search(limit int, condFuncList ...func(user *User) bool) (results Members) {
|
||||||
if condFuncList == nil {
|
if condFuncList == nil {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
if limit <= 0 {
|
if limit <= 0 {
|
||||||
limit = m.Count()
|
limit = m.Count()
|
||||||
}
|
}
|
||||||
for _, member := range m {
|
for _, member := range m {
|
||||||
if count := len(results); count == limit {
|
if count := len(results); count == limit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var passCount int
|
var passCount int
|
||||||
for _, condFunc := range condFuncList {
|
for _, condFunc := range condFuncList {
|
||||||
if condFunc(member) {
|
if condFunc(member) {
|
||||||
passCount++
|
passCount++
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if passCount == len(condFuncList) {
|
if passCount == len(condFuncList) {
|
||||||
results = append(results, member)
|
results = append(results, member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Members) Friends() Friends {
|
func (m Members) Friends() Friends {
|
||||||
friends := make(Friends, 0)
|
friends := make(Friends, 0)
|
||||||
for _, mb := range m {
|
for _, mb := range m {
|
||||||
if mb.IsFriend() {
|
if mb.IsFriend() {
|
||||||
friend := &Friend{mb}
|
friend := &Friend{mb}
|
||||||
friends = append(friends, friend)
|
friends = append(friends, friend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return friends
|
return friends
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Members) Groups() Groups {
|
func (m Members) Groups() Groups {
|
||||||
groups := make(Groups, 0)
|
groups := make(Groups, 0)
|
||||||
for _, mb := range m {
|
for _, mb := range m {
|
||||||
if mb.IsGroup() {
|
if mb.IsGroup() {
|
||||||
group := &Group{mb}
|
group := &Group{mb}
|
||||||
groups = append(groups, group)
|
groups = append(groups, group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Members) MPs() Mps {
|
func (m Members) MPs() Mps {
|
||||||
mps := make(Mps, 0)
|
mps := make(Mps, 0)
|
||||||
for _, mb := range m {
|
for _, mb := range m {
|
||||||
if mb.IsMP() {
|
if mb.IsMP() {
|
||||||
mp := &Mp{mb}
|
mp := &Mp{mb}
|
||||||
mps = append(mps, mp)
|
mps = append(mps, mp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mps
|
return mps
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前Members的详情
|
// 获取当前Members的详情
|
||||||
func (m Members) detail(self *Self) error {
|
func (m Members) detail(self *Self) error {
|
||||||
// 获取他们的数量
|
// 获取他们的数量
|
||||||
members := m
|
members := m
|
||||||
|
|
||||||
count := members.Count()
|
count := members.Count()
|
||||||
// 一次更新50个,分情况讨论
|
// 一次更新50个,分情况讨论
|
||||||
|
|
||||||
// 获取总的需要更新的次数
|
// 获取总的需要更新的次数
|
||||||
var times int
|
var times int
|
||||||
if count < 50 {
|
if count < 50 {
|
||||||
times = 1
|
times = 1
|
||||||
} else {
|
} else {
|
||||||
times = count / 50
|
times = count / 50
|
||||||
}
|
}
|
||||||
var newMembers Members
|
var newMembers Members
|
||||||
request := self.Bot.storage.Request
|
request := self.Bot.storage.Request
|
||||||
var pMembers Members
|
var pMembers Members
|
||||||
// 分情况依次更新
|
// 分情况依次更新
|
||||||
for i := 1; i <= times; i++ {
|
for i := 1; i <= times; i++ {
|
||||||
if times == 1 {
|
if times == 1 {
|
||||||
pMembers = members
|
pMembers = members
|
||||||
} else {
|
} else {
|
||||||
pMembers = members[(i-1)*50 : i*50]
|
pMembers = members[(i-1)*50 : i*50]
|
||||||
}
|
}
|
||||||
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
|
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newMembers = append(newMembers, nMembers...)
|
newMembers = append(newMembers, nMembers...)
|
||||||
}
|
}
|
||||||
// 最后判断是否全部更新完毕
|
// 最后判断是否全部更新完毕
|
||||||
total := times * 50
|
total := times * 50
|
||||||
if total < count {
|
if total < count {
|
||||||
// 将全部剩余的更新完毕
|
// 将全部剩余的更新完毕
|
||||||
left := count - total
|
left := count - total
|
||||||
pMembers = members[total : total+left]
|
pMembers = members[total : total+left]
|
||||||
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
|
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newMembers = append(newMembers, nMembers...)
|
newMembers = append(newMembers, nMembers...)
|
||||||
}
|
}
|
||||||
if len(newMembers) > 0 {
|
if len(newMembers) > 0 {
|
||||||
newMembers.SetOwner(self)
|
newMembers.SetOwner(self)
|
||||||
self.members = newMembers
|
self.members = newMembers
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 这里为了兼容Desktop版本找不到文件传输助手的问题
|
// 这里为了兼容Desktop版本找不到文件传输助手的问题
|
||||||
@ -506,5 +512,5 @@ func (m Members) detail(self *Self) error {
|
|||||||
// 这种形式的对象可能缺少一些其他属性
|
// 这种形式的对象可能缺少一些其他属性
|
||||||
// 但是不影响发送信息的功能
|
// 但是不影响发送信息的功能
|
||||||
func NewFriendHelper(self *Self) *Friend {
|
func NewFriendHelper(self *Self) *Friend {
|
||||||
return &Friend{&User{UserName: "filehelper", Self: self}}
|
return &Friend{&User{UserName: "filehelper", Self: self}}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user