支持消息撤回

This commit is contained in:
eatMoreApple 2021-04-28 17:26:10 +08:00
parent 314e811961
commit b967eda0d0
10 changed files with 338 additions and 223 deletions

View File

@ -193,12 +193,12 @@ func TestSendMessage(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
if err = helper.SendText("test message! received ?"); err != nil { if _, err = helper.SendText("test message! received ?"); err != nil {
t.Error(err) t.Error(err)
return return
} }
time.Sleep(time.Second) time.Sleep(time.Second)
if err = self.SendTextToFriend(helper, "send test message twice ! received?"); err != nil { if _, err = self.SendTextToFriend(helper, "send test message twice ! received?"); err != nil {
t.Error(err) t.Error(err)
return return
} }
@ -253,5 +253,38 @@ func TestFriendHelper(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
fh.SendText("test message") msg, err := fh.SendText("test message")
if err != nil {
t.Error(err)
return
}
t.Log(msg.MsgId)
}
func TestRevokeMessage(t *testing.T) {
bot := defaultBot(Desktop)
if err := bot.Login(); err != nil {
t.Error(err)
return
}
self, err := bot.GetCurrentUser()
if err != nil {
t.Error(err)
return
}
friends, err := self.Friends()
if err != nil {
t.Error(err)
return
}
fs := friends.SearchByRemarkName(1, "1")
ms, err := fs.First().SendText("test")
if err != nil {
t.Error(err)
return
}
time.Sleep(time.Second)
if err := ms.Revoke(); err != nil {
t.Error(err)
}
} }

View File

@ -187,9 +187,11 @@ func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info
} }
// 发送消息接口 // 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) error { func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) (*SentMessage, error) {
resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request)) resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
return parseBaseResponseError(resp) sendSuccessMsg := &SentMessage{SendMessage: msg}
err := parseMessageResponseError(resp, sendSuccessMsg)
return sendSuccessMsg, err
} }
// 修改用户备注接口 // 修改用户备注接口
@ -199,12 +201,12 @@ func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string)
} }
// 发送图片消息接口 // 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) error { func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
// 首先尝试上传图片 // 首先尝试上传图片
resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic")) resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic"))
// 无错误上传成功之后获取请求结果,判断结果是否正常 // 无错误上传成功之后获取请求结果,判断结果是否正常
if resp.Err() != nil { if resp.Err() != nil {
return resp.Err() return nil, resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
var item struct { var item struct {
@ -212,16 +214,19 @@ func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *Lo
MediaId string MediaId string
} }
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return nil, item.BaseResponse
} }
// 构造新的图片类型的信息 // 构造新的图片类型的信息
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId) msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
// 发送图片信息 // 发送图片信息
resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info)) resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
return parseBaseResponseError(resp)
sendSuccessMsg := &SentMessage{SendMessage: msg}
err := parseMessageResponseError(resp, sendSuccessMsg)
return sendSuccessMsg, err
} }
// 用户退出 // 用户退出
@ -254,6 +259,12 @@ func (c *Caller) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyCon
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// 撤回消息操作
func (c *Caller) WebWxRevokeMsg(msg *SentMessage, request *BaseRequest) error {
resp := NewReturnResponse(c.Client.WebWxRevokeMsg(msg, request))
return parseBaseResponseError(resp)
}
// 处理响应返回的结果是否正常 // 处理响应返回的结果是否正常
func parseBaseResponseError(resp *ReturnResponse) error { func parseBaseResponseError(resp *ReturnResponse) error {
if resp.Err() != nil { if resp.Err() != nil {
@ -269,3 +280,24 @@ func parseBaseResponseError(resp *ReturnResponse) error {
} }
return nil return nil
} }
func parseMessageResponseError(resp *ReturnResponse, msg *SentMessage) error {
if resp.Err() != nil {
return resp.Err()
}
defer resp.Body.Close()
var messageResp MessageResponse
if err := resp.ScanJSON(&messageResp); err != nil {
return err
}
if !messageResp.BaseResponse.Ok() {
return messageResp.BaseResponse
}
//// 发送成功之后将msgId赋值给SendMessage
msg.MsgId = messageResp.MsgID
return nil
}

View File

@ -494,3 +494,17 @@ func (c *Client) RemoveMemberFromChatRoom(req *BaseRequest, info *LoginInfo, gro
requ.Header.Set("Content-Type", jsonContentType) requ.Header.Set("Content-Type", jsonContentType)
return c.Do(requ) return c.Do(requ)
} }
// 撤回消息
func (c *Client) WebWxRevokeMsg(msg *SentMessage, request *BaseRequest) (*http.Response, error) {
content := map[string]interface{}{
"BaseRequest": request,
"ClientMsgId": msg.ClientMsgId,
"SvrMsgId": msg.MsgId,
"ToUserName": msg.ToUserName,
}
buffer, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, c.webWxRevokeMsg, buffer)
req.Header.Set("Content-Type", jsonContentType)
return c.Do(req)
}

View File

@ -17,5 +17,5 @@ func TestSendEmoji(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
_ = f.SendText(Emoji.Dagger) _ , _= f.SendText(Emoji.Dagger)
} }

133
global.go
View File

@ -1,99 +1,100 @@
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"
// 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"
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")
) )
const ALL = 0 const ALL = 0
// sex // sex
const ( const (
MALE = 1 MALE = 1
FEMALE = 2 FEMALE = 2
) )

View File

@ -196,3 +196,9 @@ type CheckLoginResponse struct {
Code string Code string
Raw []byte Raw []byte
} }
type MessageResponse struct {
BaseResponse BaseResponse
LocalID string
MsgID string
}

View File

@ -127,7 +127,7 @@ func (m *Message) IsSendByGroup() bool {
} }
// 回复消息 // 回复消息
func (m *Message) Reply(msgType int, content, mediaId string) 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
@ -135,12 +135,12 @@ func (m *Message) Reply(msgType int, content, mediaId string) error {
} }
// 回复文本消息 // 回复文本消息
func (m *Message) ReplyText(content string) 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) 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)
@ -341,7 +341,7 @@ type SendMessage struct {
// 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().Unix()*1e4, 10) id := strconv.FormatInt(time.Now().UnixNano()/1e2, 10)
return &SendMessage{ return &SendMessage{
Type: msgType, Type: msgType,
Content: content, Content: content,
@ -459,3 +459,15 @@ type RevokeMsg struct {
ReplaceMsg string `xml:"replacemsg"` ReplaceMsg string `xml:"replacemsg"`
} `xml:"revokemsg"` } `xml:"revokemsg"`
} }
// 已发送的信息
type SentMessage struct {
*SendMessage
Self *Self
MsgId string
}
// 撤回该消息
func (s *SentMessage) Revoke() error {
return s.Self.RevokeMessage(s)
}

View File

@ -19,17 +19,17 @@ func (f *Friend) SetRemarkName(name string) error {
} }
// 发送自定义消息 // 发送自定义消息
func (f *Friend) SendMsg(msg *SendMessage) error { func (f *Friend) SendMsg(msg *SendMessage) (*SentMessage, error) {
return f.Self.SendMessageToFriend(f, msg) return f.Self.SendMessageToFriend(f, msg)
} }
// 发送文本消息 // 发送文本消息
func (f *Friend) SendText(content string) error { func (f *Friend) SendText(content string) (*SentMessage, error) {
return f.Self.SendTextToFriend(f, content) return f.Self.SendTextToFriend(f, content)
} }
// 发送图片消息 // 发送图片消息
func (f *Friend) SendImage(file *os.File) error { func (f *Friend) SendImage(file *os.File) (*SentMessage, error) {
return f.Self.SendImageToFriend(f, file) return f.Self.SendImageToFriend(f, file)
} }
@ -106,7 +106,7 @@ func (f Friends) SendMsg(msg *SendMessage, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, friend := range f { for _, friend := range f {
time.Sleep(total) time.Sleep(total)
if err := friend.SendMsg(msg); err != nil { if _, err := friend.SendMsg(msg); err != nil {
return err return err
} }
} }
@ -118,7 +118,7 @@ func (f Friends) SendText(text string, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, friend := range f { for _, friend := range f {
time.Sleep(total) time.Sleep(total)
if err := friend.SendText(text); err != nil { if _, err := friend.SendText(text); err != nil {
return err return err
} }
} }
@ -130,7 +130,7 @@ func (f Friends) SendImage(file *os.File, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, friend := range f { for _, friend := range f {
time.Sleep(total) time.Sleep(total)
if err := friend.SendImage(file); err != nil { if _, err := friend.SendImage(file); err != nil {
return err return err
} }
} }
@ -145,17 +145,17 @@ func (g Group) String() string {
} }
// 发行消息给当前的群组 // 发行消息给当前的群组
func (g *Group) SendMsg(msg *SendMessage) error { func (g *Group) SendMsg(msg *SendMessage) (*SentMessage, error) {
return g.Self.SendMessageToGroup(g, msg) return g.Self.SendMessageToGroup(g, msg)
} }
// 发行文本消息给当前的群组 // 发行文本消息给当前的群组
func (g *Group) SendText(content string) error { func (g *Group) SendText(content string) (*SentMessage, error) {
return g.Self.SendTextToGroup(g, content) return g.Self.SendTextToGroup(g, content)
} }
// 发行图片消息给当前的群组 // 发行图片消息给当前的群组
func (g *Group) SendImage(file *os.File) error { func (g *Group) SendImage(file *os.File) (*SentMessage, error) {
return g.Self.SendImageToGroup(g, file) return g.Self.SendImageToGroup(g, file)
} }
@ -208,7 +208,7 @@ func (g Groups) SendMsg(msg *SendMessage, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, group := range g { for _, group := range g {
time.Sleep(total) time.Sleep(total)
if err := group.SendMsg(msg); err != nil { if _, err := group.SendMsg(msg); err != nil {
return err return err
} }
} }
@ -220,7 +220,7 @@ func (g Groups) SendText(text string, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, group := range g { for _, group := range g {
time.Sleep(total) time.Sleep(total)
if err := group.SendText(text); err != nil { if _, err := group.SendText(text); err != nil {
return err return err
} }
} }
@ -232,7 +232,7 @@ func (g Groups) SendImage(file *os.File, delay ...time.Duration) error {
total := getTotalDuration(delay...) total := getTotalDuration(delay...)
for _, group := range g { for _, group := range g {
time.Sleep(total) time.Sleep(total)
if err := group.SendImage(file); err != nil { if _, err := group.SendImage(file); err != nil {
return err return err
} }
} }
@ -334,5 +334,3 @@ func (m Mps) Search(limit int, condFuncList ...func(group *Mp) bool) (results Mp
} }
return return
} }

155
url.go
View File

@ -2,76 +2,79 @@ package openwechat
// url信息存储 // url信息存储
type UrlManager struct { type UrlManager struct {
baseUrl string baseUrl string
webWxNewLoginPageUrl string webWxNewLoginPageUrl string
webWxInitUrl string webWxInitUrl string
webWxStatusNotifyUrl string webWxStatusNotifyUrl string
webWxSyncUrl string webWxSyncUrl string
webWxSendMsgUrl string webWxSendMsgUrl string
webWxGetContactUrl string webWxGetContactUrl string
webWxSendMsgImgUrl string webWxSendMsgImgUrl string
webWxSendAppMsgUrl string webWxSendAppMsgUrl string
webWxBatchGetContactUrl string webWxBatchGetContactUrl string
webWxOplogUrl string webWxOplogUrl string
webWxVerifyUserUrl string webWxVerifyUserUrl string
syncCheckUrl string syncCheckUrl string
webWxUpLoadMediaUrl string webWxUpLoadMediaUrl string
webWxGetMsgImgUrl string webWxGetMsgImgUrl string
webWxGetVoiceUrl string webWxGetVoiceUrl string
webWxGetVideoUrl string webWxGetVideoUrl string
webWxLogoutUrl string webWxLogoutUrl string
webWxGetMediaUrl string webWxGetMediaUrl string
webWxUpdateChatRoomUrl string webWxUpdateChatRoomUrl string
webWxRevokeMsg string
} }
var ( var (
// uos版 // uos版
desktop = UrlManager{ desktop = UrlManager{
baseUrl: baseDesktopUrl, baseUrl: baseDesktopUrl,
webWxNewLoginPageUrl: webWxNewLoginPageDesktopUrl, webWxNewLoginPageUrl: webWxNewLoginPageDesktopUrl,
webWxInitUrl: webWxInitDesktopUrl, webWxInitUrl: webWxInitDesktopUrl,
webWxStatusNotifyUrl: webWxStatusNotifyDesktopUrl, webWxStatusNotifyUrl: webWxStatusNotifyDesktopUrl,
webWxSyncUrl: webWxSyncDesktopUrl, webWxSyncUrl: webWxSyncDesktopUrl,
webWxSendMsgUrl: webWxSendMsgDesktopUrl, webWxSendMsgUrl: webWxSendMsgDesktopUrl,
webWxGetContactUrl: webWxGetContactDesktopUrl, webWxGetContactUrl: webWxGetContactDesktopUrl,
webWxSendMsgImgUrl: webWxSendMsgImgDesktopUrl, webWxSendMsgImgUrl: webWxSendMsgImgDesktopUrl,
webWxSendAppMsgUrl: webWxSendAppMsgDesktopUrl, webWxSendAppMsgUrl: webWxSendAppMsgDesktopUrl,
webWxBatchGetContactUrl: webWxBatchGetContactDesktopUrl, webWxBatchGetContactUrl: webWxBatchGetContactDesktopUrl,
webWxOplogUrl: webWxOplogDesktopUrl, webWxOplogUrl: webWxOplogDesktopUrl,
webWxVerifyUserUrl: webWxVerifyUserDesktopUrl, webWxVerifyUserUrl: webWxVerifyUserDesktopUrl,
syncCheckUrl: syncCheckDesktopUrl, syncCheckUrl: syncCheckDesktopUrl,
webWxUpLoadMediaUrl: webWxUpLoadMediaDesktopUrl, webWxUpLoadMediaUrl: webWxUpLoadMediaDesktopUrl,
webWxGetMsgImgUrl: webWxGetMsgImgDesktopUrl, webWxGetMsgImgUrl: webWxGetMsgImgDesktopUrl,
webWxGetVoiceUrl: webWxGetVoiceDesktopUrl, webWxGetVoiceUrl: webWxGetVoiceDesktopUrl,
webWxGetVideoUrl: webWxGetVideoDesktopUrl, webWxGetVideoUrl: webWxGetVideoDesktopUrl,
webWxLogoutUrl: webWxLogoutDesktopUrl, webWxLogoutUrl: webWxLogoutDesktopUrl,
webWxGetMediaUrl: webWxGetMediaDesktopUrl, webWxGetMediaUrl: webWxGetMediaDesktopUrl,
webWxUpdateChatRoomUrl: webWxUpdateChatRoomDesktopUrl, webWxUpdateChatRoomUrl: webWxUpdateChatRoomDesktopUrl,
} webWxRevokeMsg: webWxRevokeMsgDesktopUrl,
}
// 网页版 // 网页版
normal = UrlManager{ normal = UrlManager{
baseUrl: baseNormalUrl, baseUrl: baseNormalUrl,
webWxNewLoginPageUrl: webWxNewLoginPageNormalUrl, webWxNewLoginPageUrl: webWxNewLoginPageNormalUrl,
webWxInitUrl: webWxInitNormalUrl, webWxInitUrl: webWxInitNormalUrl,
webWxStatusNotifyUrl: webWxStatusNotifyNormalUrl, webWxStatusNotifyUrl: webWxStatusNotifyNormalUrl,
webWxSyncUrl: webWxSyncNormalUrl, webWxSyncUrl: webWxSyncNormalUrl,
webWxSendMsgUrl: webWxSendMsgNormalUrl, webWxSendMsgUrl: webWxSendMsgNormalUrl,
webWxGetContactUrl: webWxGetContactNormalUrl, webWxGetContactUrl: webWxGetContactNormalUrl,
webWxSendMsgImgUrl: webWxSendMsgImgNormalUrl, webWxSendMsgImgUrl: webWxSendMsgImgNormalUrl,
webWxSendAppMsgUrl: webWxSendAppMsgNormalUrl, webWxSendAppMsgUrl: webWxSendAppMsgNormalUrl,
webWxBatchGetContactUrl: webWxBatchGetContactNormalUrl, webWxBatchGetContactUrl: webWxBatchGetContactNormalUrl,
webWxOplogUrl: webWxOplogNormalUrl, webWxOplogUrl: webWxOplogNormalUrl,
webWxVerifyUserUrl: webWxVerifyUserNormalUrl, webWxVerifyUserUrl: webWxVerifyUserNormalUrl,
syncCheckUrl: syncCheckNormalUrl, syncCheckUrl: syncCheckNormalUrl,
webWxUpLoadMediaUrl: webWxUpLoadMediaNormalUrl, webWxUpLoadMediaUrl: webWxUpLoadMediaNormalUrl,
webWxGetMsgImgUrl: webWxGetMsgImgNormalUrl, webWxGetMsgImgUrl: webWxGetMsgImgNormalUrl,
webWxGetVoiceUrl: webWxGetVoiceNormalUrl, webWxGetVoiceUrl: webWxGetVoiceNormalUrl,
webWxGetVideoUrl: webWxGetVideoNormalUrl, webWxGetVideoUrl: webWxGetVideoNormalUrl,
webWxLogoutUrl: webWxLogoutNormalUrl, webWxLogoutUrl: webWxLogoutNormalUrl,
webWxGetMediaUrl: webWxGetMediaNormalUrl, webWxGetMediaUrl: webWxGetMediaNormalUrl,
webWxUpdateChatRoomUrl: webWxUpdateChatRoomNormalUrl, webWxUpdateChatRoomUrl: webWxUpdateChatRoomNormalUrl,
} webWxRevokeMsg: webWxRevokeMsgNormalUrl,
}
) )
// mode 类型限制 // mode 类型限制
@ -79,17 +82,17 @@ type mode string
// 向外暴露2种模式 // 向外暴露2种模式
const ( const (
Normal mode = "normal" Normal mode = "normal"
Desktop mode = "desktop" Desktop mode = "desktop"
) )
func GetUrlManagerByMode(m mode) UrlManager { func GetUrlManagerByMode(m mode) UrlManager {
switch m { switch m {
case Desktop: case Desktop:
return desktop return desktop
case Normal: case Normal:
return normal return normal
default: default:
panic("unsupport mode got") panic("unsupport mode got")
} }
} }

120
user.go
View File

@ -205,74 +205,36 @@ func (s *Self) UpdateMembersDetail() error {
if err != nil { if err != nil {
return err return err
} }
// 获取他们的数量 return members.detail(s)
count := members.Count()
// 一次更新50个,分情况讨论
// 获取总的需要更新的次数
var times int
if count < 50 {
times = 1
} else {
times = count / 50
}
var newMembers Members
request := s.Bot.storage.Request
var pMembers Members
// 分情况依次更新
for i := 1; i <= times; i++ {
if times == 1 {
pMembers = members
} else {
pMembers = members[(i-1)*50 : i*50]
}
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
// 最后判断是否全部更新完毕
total := times * 50
if total < count {
// 将全部剩余的更新完毕
left := count - total
pMembers = members[total : total+left]
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
if len(newMembers) > 0 {
newMembers.SetOwner(s)
s.members = newMembers
}
return nil
} }
// 抽象发送消息接口 // 抽象发送消息接口
func (s *Self) sendMessageToUser(user *User, msg *SendMessage) 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
return s.Bot.Caller.WebWxSendMsg(msg, info, request) successSendMessage, err := s.Bot.Caller.WebWxSendMsg(msg, info, request)
if err != nil {
return nil, err
}
successSendMessage.Self = s
return successSendMessage, nil
} }
// 发送消息给好友 // 发送消息给好友
func (s *Self) SendMessageToFriend(friend *Friend, msg *SendMessage) 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) 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) 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)
@ -351,23 +313,28 @@ func (s *Self) AddFriendIntoManyGroups(friend *Friend, groups ...*Group) error {
} }
// 发送消息给群组 // 发送消息给群组
func (s *Self) SendMessageToGroup(group *Group, msg *SendMessage) 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) 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) 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)
} }
// 撤回消息
func (s *Self) RevokeMessage(msg *SentMessage) error {
return s.Bot.Caller.WebWxRevokeMsg(msg, s.Bot.storage.Request)
}
// 抽象的用户组 // 抽象的用户组
type Members []*User type Members []*User
@ -477,6 +444,55 @@ func (m Members) MPs() Mps {
return mps return mps
} }
func (m Members) detail(self *Self) error {
// 获取他们的数量
members := m
count := members.Count()
// 一次更新50个,分情况讨论
// 获取总的需要更新的次数
var times int
if count < 50 {
times = 1
} else {
times = count / 50
}
var newMembers Members
request := self.Bot.storage.Request
var pMembers Members
// 分情况依次更新
for i := 1; i <= times; i++ {
if times == 1 {
pMembers = members
} else {
pMembers = members[(i-1)*50 : i*50]
}
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
// 最后判断是否全部更新完毕
total := times * 50
if total < count {
// 将全部剩余的更新完毕
left := count - total
pMembers = members[total : total+left]
nMembers, err := self.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
if len(newMembers) > 0 {
newMembers.SetOwner(self)
self.members = newMembers
}
return nil
}
// 这里为了兼容Desktop版本找不到文件传输助手的问题 // 这里为了兼容Desktop版本找不到文件传输助手的问题
// 文件传输助手的微信身份标识符永远是filehelper // 文件传输助手的微信身份标识符永远是filehelper
// 这种形式的对象可能缺少一些其他属性 // 这种形式的对象可能缺少一些其他属性