diff --git a/bot_test.go b/bot_test.go
index 40d87f0..e26e1e6 100644
--- a/bot_test.go
+++ b/bot_test.go
@@ -196,3 +196,20 @@ func TestSendMessage(t *testing.T) {
return
}
}
+
+func TestAgreeFriendsAdd(t *testing.T) {
+ bot := defaultBot()
+ bot.MessageHandler = func(msg *Message) {
+ if msg.IsFriendAdd() {
+ if err := msg.Agree(); err != nil {
+ t.Error(err)
+ }
+ bot.Logout()
+ }
+ }
+ if err := bot.Login(); err != nil {
+ t.Error(err)
+ return
+ }
+ bot.Block()
+}
diff --git a/caller.go b/caller.go
index ae2d6a1..fd1c183 100644
--- a/caller.go
+++ b/caller.go
@@ -1,262 +1,268 @@
package openwechat
import (
- "errors"
- "os"
+ "errors"
+ "os"
)
// 调用请求和解析请求
// 上层模块可以直接获取封装后的请求结果
type Caller struct {
- Client *Client
+ Client *Client
}
// Constructor for Caller
func NewCaller(client *Client) *Caller {
- return &Caller{Client: client}
+ return &Caller{Client: client}
}
// Default Constructor for Caller
func DefaultCaller(urlManager UrlManager) *Caller {
- return NewCaller(DefaultClient(urlManager))
+ return NewCaller(DefaultClient(urlManager))
}
// 获取登录的uuid
func (c *Caller) GetLoginUUID() (string, error) {
- resp := NewReturnResponse(c.Client.GetLoginUUID())
- if resp.Err() != nil {
- return "", resp.Err()
- }
- defer resp.Body.Close()
- data, err := resp.ReadAll()
- if err != nil {
- return "", err
- }
- // 正则匹配uuid字符串
- results := uuidRegexp.FindSubmatch(data)
- if len(results) != 2 {
- // 如果没有匹配到,可能微信的接口做了修改,或者当前机器的ip被加入了黑名单
- return "", errors.New("uuid does not match")
- }
- return string(results[1]), nil
+ resp := NewReturnResponse(c.Client.GetLoginUUID())
+ if resp.Err() != nil {
+ return "", resp.Err()
+ }
+ defer resp.Body.Close()
+ data, err := resp.ReadAll()
+ if err != nil {
+ return "", err
+ }
+ // 正则匹配uuid字符串
+ results := uuidRegexp.FindSubmatch(data)
+ if len(results) != 2 {
+ // 如果没有匹配到,可能微信的接口做了修改,或者当前机器的ip被加入了黑名单
+ return "", errors.New("uuid does not match")
+ }
+ return string(results[1]), nil
}
// 检查是否登录成功
func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
- resp := NewReturnResponse(c.Client.CheckLogin(uuid))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- data, err := resp.ReadAll()
- if err != nil {
- return nil, err
- }
- // 正则匹配检测的code
- // 具体code参考global.go
- results := statusCodeRegexp.FindSubmatch(data)
- if len(results) != 2 {
- return nil, errors.New("error status code match")
- }
- code := string(results[1])
- return &CheckLoginResponse{Code: code, Raw: data}, nil
+ resp := NewReturnResponse(c.Client.CheckLogin(uuid))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ data, err := resp.ReadAll()
+ if err != nil {
+ return nil, err
+ }
+ // 正则匹配检测的code
+ // 具体code参考global.go
+ results := statusCodeRegexp.FindSubmatch(data)
+ if len(results) != 2 {
+ return nil, errors.New("error status code match")
+ }
+ code := string(results[1])
+ return &CheckLoginResponse{Code: code, Raw: data}, nil
}
// 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
- // 从响应体里面获取需要跳转的url
- results := redirectUriRegexp.FindSubmatch(body)
- if len(results) != 2 {
- return nil, errors.New("redirect url does not match")
- }
- path := string(results[1])
- resp := NewReturnResponse(c.Client.GetLoginInfo(path))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- var loginInfo LoginInfo
- // xml结构体序列化储存
- if err := resp.ScanXML(&loginInfo); err != nil {
- return nil, err
- }
- return &loginInfo, nil
+ // 从响应体里面获取需要跳转的url
+ results := redirectUriRegexp.FindSubmatch(body)
+ if len(results) != 2 {
+ return nil, errors.New("redirect url does not match")
+ }
+ path := string(results[1])
+ resp := NewReturnResponse(c.Client.GetLoginInfo(path))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ var loginInfo LoginInfo
+ // xml结构体序列化储存
+ if err := resp.ScanXML(&loginInfo); err != nil {
+ return nil, err
+ }
+ return &loginInfo, nil
}
// 获取初始化信息
func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) {
- resp := NewReturnResponse(c.Client.WebInit(request))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- var webInitResponse WebInitResponse
- defer resp.Body.Close()
- if err := resp.ScanJSON(&webInitResponse); err != nil {
- return nil, err
- }
- return &webInitResponse, nil
+ resp := NewReturnResponse(c.Client.WebInit(request))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ var webInitResponse WebInitResponse
+ defer resp.Body.Close()
+ if err := resp.ScanJSON(&webInitResponse); err != nil {
+ return nil, err
+ }
+ return &webInitResponse, nil
}
// 通知手机已登录
func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error {
- resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info))
- if resp.Err() != nil {
- return resp.Err()
- }
- var item struct{ BaseResponse BaseResponse }
- defer resp.Body.Close()
- if err := resp.ScanJSON(&item); err != nil {
- return err
- }
- if !item.BaseResponse.Ok() {
- return item.BaseResponse
- }
- return nil
+ resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info))
+ if resp.Err() != nil {
+ return resp.Err()
+ }
+ var item struct{ BaseResponse BaseResponse }
+ defer resp.Body.Close()
+ if err := resp.ScanJSON(&item); err != nil {
+ return err
+ }
+ if !item.BaseResponse.Ok() {
+ return item.BaseResponse
+ }
+ return nil
}
// 异步获取是否有新的消息
func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) {
- resp := NewReturnResponse(c.Client.SyncCheck(info, response))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- data, err := resp.ReadAll()
- if err != nil {
- return nil, err
- }
- results := syncCheckRegexp.FindSubmatch(data)
- if len(results) != 3 {
- return nil, errors.New("parse sync key failed")
- }
- retCode, selector := string(results[1]), string(results[2])
- syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector}
- return syncCheckResponse, nil
+ resp := NewReturnResponse(c.Client.SyncCheck(info, response))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ data, err := resp.ReadAll()
+ if err != nil {
+ return nil, err
+ }
+ results := syncCheckRegexp.FindSubmatch(data)
+ if len(results) != 3 {
+ return nil, errors.New("parse sync key failed")
+ }
+ retCode, selector := string(results[1]), string(results[2])
+ syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector}
+ return syncCheckResponse, nil
}
// 获取所有的联系人
func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) {
- resp := NewReturnResponse(c.Client.WebWxGetContact(info))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- var item WebWxContactResponse
- if err := resp.ScanJSON(&item); err != nil {
- return nil, err
- }
- if !item.BaseResponse.Ok() {
- return nil, item.BaseResponse
- }
- return item.MemberList, nil
+ resp := NewReturnResponse(c.Client.WebWxGetContact(info))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ var item WebWxContactResponse
+ if err := resp.ScanJSON(&item); err != nil {
+ return nil, err
+ }
+ if !item.BaseResponse.Ok() {
+ return nil, item.BaseResponse
+ }
+ return item.MemberList, nil
}
// 获取联系人的详情
// 注: Members参数的长度不要大于50
func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) {
- resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- var item WebWxBatchContactResponse
- if err := resp.ScanJSON(&item); err != nil {
- return nil, err
- }
- if !item.BaseResponse.Ok() {
- return nil, item.BaseResponse
- }
- return item.ContactList, nil
+ resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ var item WebWxBatchContactResponse
+ if err := resp.ScanJSON(&item); err != nil {
+ return nil, err
+ }
+ if !item.BaseResponse.Ok() {
+ return nil, item.BaseResponse
+ }
+ return item.ContactList, nil
}
// 获取新的消息接口
func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*WebWxSyncResponse, error) {
- resp := NewReturnResponse(c.Client.WebWxSync(request, response, info))
- if resp.Err() != nil {
- return nil, resp.Err()
- }
- defer resp.Body.Close()
- var webWxSyncResponse WebWxSyncResponse
- if err := resp.ScanJSON(&webWxSyncResponse); err != nil {
- return nil, err
- }
- return &webWxSyncResponse, nil
+ resp := NewReturnResponse(c.Client.WebWxSync(request, response, info))
+ if resp.Err() != nil {
+ return nil, resp.Err()
+ }
+ defer resp.Body.Close()
+ var webWxSyncResponse WebWxSyncResponse
+ if err := resp.ScanJSON(&webWxSyncResponse); err != nil {
+ return nil, err
+ }
+ return &webWxSyncResponse, nil
}
// 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) error {
- resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
- return parseBaseResponseError(resp)
+ resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
+ return parseBaseResponseError(resp)
}
// 修改用户备注接口
func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error {
- resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName))
- return parseBaseResponseError(resp)
+ resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName))
+ return parseBaseResponseError(resp)
}
// 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) error {
- // 首先尝试上传图片
- resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic"))
- // 无错误上传成功之后获取请求结果,判断结果是否正常
- if resp.Err() != nil {
- return resp.Err()
- }
- defer resp.Body.Close()
- var item struct {
- BaseResponse BaseResponse
- MediaId string
- }
- if err := resp.ScanJSON(&item); err != nil {
- return err
- }
- if !item.BaseResponse.Ok() {
- return item.BaseResponse
- }
- // 构造新的图片类型的信息
- msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
- // 发送图片信息
- resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
- return parseBaseResponseError(resp)
+ // 首先尝试上传图片
+ resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic"))
+ // 无错误上传成功之后获取请求结果,判断结果是否正常
+ if resp.Err() != nil {
+ return resp.Err()
+ }
+ defer resp.Body.Close()
+ var item struct {
+ BaseResponse BaseResponse
+ MediaId string
+ }
+ if err := resp.ScanJSON(&item); err != nil {
+ return err
+ }
+ if !item.BaseResponse.Ok() {
+ return item.BaseResponse
+ }
+ // 构造新的图片类型的信息
+ msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
+ // 发送图片信息
+ resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
+ return parseBaseResponseError(resp)
}
// 用户退出
func (c *Caller) Logout(info *LoginInfo) error {
- resp := NewReturnResponse(c.Client.Logout(info))
- return parseBaseResponseError(resp)
+ resp := NewReturnResponse(c.Client.Logout(info))
+ return parseBaseResponseError(resp)
}
// 拉好友入群
func (c *Caller) AddFriendIntoChatRoom(req *BaseRequest, info *LoginInfo, group *Group, friends ...*Friend) error {
- if len(friends) == 0 {
- return errors.New("no friends found")
- }
- resp := NewReturnResponse(c.Client.AddMemberIntoChatRoom(req, info, group, friends...))
- return parseBaseResponseError(resp)
+ if len(friends) == 0 {
+ return errors.New("no friends found")
+ }
+ resp := NewReturnResponse(c.Client.AddMemberIntoChatRoom(req, info, group, friends...))
+ return parseBaseResponseError(resp)
}
// 从群聊中移除用户
func (c *Caller) RemoveFriendFromChatRoom(req *BaseRequest, info *LoginInfo, group *Group, users ...*User) error {
- if len(users) == 0 {
- return errors.New("no users found")
- }
- resp := NewReturnResponse(c.Client.RemoveMemberFromChatRoom(req, info, group, users...))
- return parseBaseResponseError(resp)
+ if len(users) == 0 {
+ return errors.New("no users found")
+ }
+ resp := NewReturnResponse(c.Client.RemoveMemberFromChatRoom(req, info, group, users...))
+ return parseBaseResponseError(resp)
+}
+
+// 同意加好友请求
+func (c *Caller) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyContent string) error {
+ resp := NewReturnResponse(c.Client.WebWxVerifyUser(storage, info, verifyContent))
+ return parseBaseResponseError(resp)
}
// 处理响应返回的结果是否正常
func parseBaseResponseError(resp *ReturnResponse) error {
- if resp.Err() != nil {
- return resp.Err()
- }
- defer resp.Body.Close()
- var item struct{ BaseResponse BaseResponse }
- if err := resp.ScanJSON(&item); err != nil {
- return err
- }
- if !item.BaseResponse.Ok() {
- return item.BaseResponse
- }
- return nil
+ if resp.Err() != nil {
+ return resp.Err()
+ }
+ defer resp.Body.Close()
+ var item struct{ BaseResponse BaseResponse }
+ if err := resp.ScanJSON(&item); err != nil {
+ return err
+ }
+ if !item.BaseResponse.Ok() {
+ return item.BaseResponse
+ }
+ return nil
}
diff --git a/client.go b/client.go
index 25a55cf..d3085fe 100644
--- a/client.go
+++ b/client.go
@@ -340,7 +340,7 @@ func (c *Client) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyCon
content := map[string]interface{}{
"BaseRequest": storage.Request,
"Opcode": 3,
- "SceneList": []int{33},
+ "SceneList": [1]int{33},
"SceneListCount": 1,
"VerifyContent": verifyContent,
"VerifyUserList": []interface{}{map[string]string{
diff --git a/message.go b/message.go
index 6c61711..b55d5c2 100644
--- a/message.go
+++ b/message.go
@@ -1,353 +1,413 @@
package openwechat
import (
- "context"
- "encoding/xml"
- "errors"
- "net/http"
- "os"
- "strconv"
- "strings"
- "sync"
- "time"
- "unicode"
+ "context"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "net/http"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+ "unicode"
)
type Message struct {
- IsAt bool
- AppInfo struct {
- Type int
- AppID string
- }
- AppMsgType int
- HasProductId int
- ImgHeight int
- ImgStatus int
- ImgWidth int
- ForwardFlag int
- MsgType int
- Status int
- StatusNotifyCode int
- SubMsgType int
- VoiceLength int
- CreateTime int64
- NewMsgId int64
- PlayLength int64
- MediaId string
- MsgId string
- EncryFileName string
- FileName string
- FileSize string
- Content string
- FromUserName string
- OriContent string
- StatusNotifyUserName string
- Ticket string
- ToUserName string
- Url string
- senderInGroupUserName string
- RecommendInfo RecommendInfo
- Bot *Bot
- mu sync.RWMutex
- Context context.Context
- item map[string]interface{}
+ IsAt bool
+ AppInfo struct {
+ Type int
+ AppID string
+ }
+ AppMsgType int
+ HasProductId int
+ ImgHeight int
+ ImgStatus int
+ ImgWidth int
+ ForwardFlag int
+ MsgType int
+ Status int
+ StatusNotifyCode int
+ SubMsgType int
+ VoiceLength int
+ CreateTime int64
+ NewMsgId int64
+ PlayLength int64
+ MediaId string
+ MsgId string
+ EncryFileName string
+ FileName string
+ FileSize string
+ Content string
+ FromUserName string
+ OriContent string
+ StatusNotifyUserName string
+ Ticket string
+ ToUserName string
+ Url string
+ senderInGroupUserName string
+ RecommendInfo RecommendInfo
+ Bot *Bot
+ mu sync.RWMutex
+ Context context.Context
+ item map[string]interface{}
}
// 获取消息的发送者
func (m *Message) Sender() (*User, error) {
- members, err := m.Bot.self.Members(true)
- if err != nil {
- return nil, err
- }
- if m.FromUserName == m.Bot.self.User.UserName {
- return m.Bot.self.User, nil
- }
- user := members.SearchByUserName(1, m.FromUserName)
- if user == nil {
- return nil, noSuchUserFoundError
- }
- return user.First().Detail()
+ members, err := m.Bot.self.Members(true)
+ if err != nil {
+ return nil, err
+ }
+ if m.FromUserName == m.Bot.self.User.UserName {
+ return m.Bot.self.User, nil
+ }
+ user := members.SearchByUserName(1, m.FromUserName)
+ if user == nil {
+ return nil, noSuchUserFoundError
+ }
+ return user.First().Detail()
}
// 获取消息在群里面的发送者
func (m *Message) SenderInGroup() (*User, error) {
- if !m.IsSendByGroup() {
- return nil, errors.New("message is not from group")
- }
- group, err := m.Sender()
- if err != nil {
- return nil, err
- }
- group, err = group.Detail()
- if err != nil {
- return nil, err
- }
- users := group.MemberList.SearchByUserName(1, m.senderInGroupUserName)
- if users == nil {
- return nil, noSuchUserFoundError
- }
- return users.First(), nil
+ if !m.IsSendByGroup() {
+ return nil, errors.New("message is not from group")
+ }
+ group, err := m.Sender()
+ if err != nil {
+ return nil, err
+ }
+ group, err = group.Detail()
+ if err != nil {
+ return nil, err
+ }
+ users := group.MemberList.SearchByUserName(1, m.senderInGroupUserName)
+ if users == nil {
+ return nil, noSuchUserFoundError
+ }
+ return users.First(), nil
}
// 获取消息的接收者
func (m *Message) Receiver() (*User, error) {
- if m.IsSendByGroup() {
- if sender, err := m.Sender(); err != nil {
- return nil, err
- } else {
- users := sender.MemberList.SearchByUserName(1, m.ToUserName)
- if users == nil {
- return nil, noSuchUserFoundError
- }
- return users.First(), nil
- }
- } else {
- users := m.Bot.self.MemberList.SearchByUserName(1, m.ToUserName)
- if users == nil {
- return nil, noSuchUserFoundError
- }
- return users.First(), nil
- }
+ if m.IsSendByGroup() {
+ if sender, err := m.Sender(); err != nil {
+ return nil, err
+ } else {
+ users := sender.MemberList.SearchByUserName(1, m.ToUserName)
+ if users == nil {
+ return nil, noSuchUserFoundError
+ }
+ return users.First(), nil
+ }
+ } else {
+ users := m.Bot.self.MemberList.SearchByUserName(1, m.ToUserName)
+ if users == nil {
+ return nil, noSuchUserFoundError
+ }
+ return users.First(), nil
+ }
}
// 判断消息是否由自己发送
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 {
- return !m.IsSendByGroup() && strings.HasPrefix(m.FromUserName, "@")
+ return !m.IsSendByGroup() && strings.HasPrefix(m.FromUserName, "@")
}
// 判断消息是否由群组发送
func (m *Message) IsSendByGroup() bool {
- return strings.HasPrefix(m.FromUserName, "@@")
+ return strings.HasPrefix(m.FromUserName, "@@")
}
// 回复消息
func (m *Message) Reply(msgType int, content, mediaId string) error {
- msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
- info := m.Bot.storage.LoginInfo
- request := m.Bot.storage.Request
- return m.Bot.Caller.WebWxSendMsg(msg, info, request)
+ msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
+ info := m.Bot.storage.LoginInfo
+ request := m.Bot.storage.Request
+ return m.Bot.Caller.WebWxSendMsg(msg, info, request)
}
// 回复文本消息
func (m *Message) ReplyText(content string) error {
- return m.Reply(TextMessage, content, "")
+ return m.Reply(TextMessage, content, "")
}
// 回复图片消息
func (m *Message) ReplyImage(file *os.File) error {
- info := m.Bot.storage.LoginInfo
- request := m.Bot.storage.Request
- return m.Bot.Caller.WebWxSendImageMsg(file, request, info, m.Bot.self.UserName, m.FromUserName)
+ info := m.Bot.storage.LoginInfo
+ request := m.Bot.storage.Request
+ return m.Bot.Caller.WebWxSendImageMsg(file, request, info, m.Bot.self.UserName, m.FromUserName)
}
func (m *Message) IsText() bool {
- return m.MsgType == 1 && m.Url == ""
+ return m.MsgType == 1 && m.Url == ""
}
func (m *Message) IsMap() bool {
- return m.MsgType == 1 && m.Url != ""
+ return m.MsgType == 1 && m.Url != ""
}
func (m *Message) IsPicture() bool {
- return m.MsgType == 3 || m.MsgType == 47
+ return m.MsgType == 3 || m.MsgType == 47
}
func (m *Message) IsVoice() bool {
- return m.MsgType == 34
+ return m.MsgType == 34
}
func (m *Message) IsFriendAdd() bool {
- return m.MsgType == 37
+ return m.MsgType == 37 && m.FromUserName == "fmessage"
}
func (m *Message) IsCard() bool {
- return m.MsgType == 42
+ return m.MsgType == 42
}
func (m *Message) IsVideo() bool {
- return m.MsgType == 43 || m.MsgType == 62
+ return m.MsgType == 43 || m.MsgType == 62
}
func (m *Message) IsMedia() bool {
- return m.MsgType == 49
+ return m.MsgType == 49
}
func (m *Message) IsRecalled() bool {
- return m.MsgType == 10002
+ return m.MsgType == 10002
}
func (m *Message) IsSystem() bool {
- return m.MsgType == 10000
+ return m.MsgType == 10000
}
func (m *Message) IsNotify() bool {
- return m.MsgType == 51 && m.StatusNotifyCode != 0
+ return m.MsgType == 51 && m.StatusNotifyCode != 0
}
// 判断消息是否为文件类型的消息
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) {
- if !m.HasFile() {
- return nil, errors.New("invalid message type")
- }
- if m.IsPicture() {
- return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.LoginInfo)
- }
- if m.IsVoice() {
- return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.LoginInfo)
- }
- if m.IsVideo() {
- return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.LoginInfo)
- }
- if m.IsMedia() {
- return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.LoginInfo)
- }
- return nil, errors.New("unsupported type")
+ if !m.HasFile() {
+ return nil, errors.New("invalid message type")
+ }
+ if m.IsPicture() {
+ return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.LoginInfo)
+ }
+ if m.IsVoice() {
+ return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.LoginInfo)
+ }
+ if m.IsVideo() {
+ return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.LoginInfo)
+ }
+ if m.IsMedia() {
+ return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.LoginInfo)
+ }
+ return nil, errors.New("unsupported type")
}
// 获取card类型
func (m *Message) Card() (*Card, error) {
- if !m.IsCard() {
- return nil, errors.New("card message required")
- }
- var card Card
- content := XmlFormString(m.Content)
- err := xml.Unmarshal([]byte(content), &card)
- return &card, err
+ if !m.IsCard() {
+ return nil, errors.New("card message required")
+ }
+ var card Card
+ content := XmlFormString(m.Content)
+ err := xml.Unmarshal([]byte(content), &card)
+ return &card, err
+}
+
+// 获取FriendAddMessageContent内容
+func (m *Message) FriendAddMessageContent() (*FriendAddMessageContent, error) {
+ if !m.IsFriendAdd() {
+ return nil, errors.New("friend add message required")
+ }
+ var f FriendAddMessageContent
+ content := XmlFormString(m.Content)
+ err := xml.Unmarshal([]byte(content), &f)
+ return &f, err
+}
+
+// 同意好友的请求
+func (m *Message) Agree(verifyContents ...string) error {
+ if !m.IsFriendAdd() {
+ return fmt.Errorf("friend add message required")
+ }
+ var builder strings.Builder
+ for _, v := range verifyContents {
+ builder.WriteString(v)
+ }
+ return m.Bot.Caller.WebWxVerifyUser(m.Bot.storage, m.RecommendInfo, builder.String())
}
// 往消息上下文中设置值
// goroutine safe
func (m *Message) Set(key string, value interface{}) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.item == nil {
- m.item = make(map[string]interface{})
- }
- m.item[key] = value
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.item == nil {
+ m.item = make(map[string]interface{})
+ }
+ m.item[key] = value
}
// 从消息上下文中获取值
// goroutine safe
func (m *Message) Get(key string) (value interface{}, exist bool) {
- m.mu.RLock()
- defer m.mu.RUnlock()
- value, exist = m.item[key]
- return
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ value, exist = m.item[key]
+ return
}
// 消息初始化,根据不同的消息作出不同的处理
func (m *Message) init(bot *Bot) {
- m.Bot = bot
- if m.IsSendByGroup() {
- data := strings.Split(m.Content, ":
")
- m.Content = strings.Join(data[1:], "")
- m.senderInGroupUserName = data[0]
- receiver, err := m.Receiver()
- if err == nil {
- displayName := receiver.DisplayName
- if displayName == "" {
- displayName = receiver.NickName
- }
- atFlag := "@" + displayName
- index := len(atFlag) + 1 + 1
- if strings.HasPrefix(m.Content, atFlag) && unicode.IsSpace(rune(m.Content[index])) {
- m.IsAt = true
- m.Content = m.Content[index+1:]
- }
- }
- }
+ m.Bot = bot
+ if m.IsSendByGroup() {
+ data := strings.Split(m.Content, ":
")
+ m.Content = strings.Join(data[1:], "")
+ m.senderInGroupUserName = data[0]
+ receiver, err := m.Receiver()
+ if err == nil {
+ displayName := receiver.DisplayName
+ if displayName == "" {
+ displayName = receiver.NickName
+ }
+ atFlag := "@" + displayName
+ index := len(atFlag) + 1 + 1
+ if strings.HasPrefix(m.Content, atFlag) && unicode.IsSpace(rune(m.Content[index])) {
+ m.IsAt = true
+ m.Content = m.Content[index+1:]
+ }
+ }
+ }
}
-//func (m *Message) Agree() error {
-// if !m.IsFriendAdd() {
-// return fmt.Errorf("the excepted message type is 37, but got %d", m.MsgType)
-// }
-// return m.Bot.Caller.Client.WebWxVerifyUser(m.Bot.storage, m.RecommendInfo, "")
-//}
-
// 发送消息的结构体
type SendMessage struct {
- Type int
- Content string
- FromUserName string
- ToUserName string
- LocalID string
- ClientMsgId string
- MediaId string
+ Type int
+ Content string
+ FromUserName string
+ ToUserName string
+ LocalID string
+ ClientMsgId string
+ MediaId string
}
// SendMessage的构造方法
func NewSendMessage(msgType int, content, fromUserName, toUserName, mediaId string) *SendMessage {
- id := strconv.FormatInt(time.Now().Unix()*1e4, 10)
- return &SendMessage{
- Type: msgType,
- Content: content,
- FromUserName: fromUserName,
- ToUserName: toUserName,
- LocalID: id,
- ClientMsgId: id,
- MediaId: mediaId,
- }
+ id := strconv.FormatInt(time.Now().Unix()*1e4, 10)
+ return &SendMessage{
+ Type: msgType,
+ Content: content,
+ FromUserName: fromUserName,
+ ToUserName: toUserName,
+ LocalID: id,
+ ClientMsgId: id,
+ MediaId: mediaId,
+ }
}
// 文本消息的构造方法
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 {
- return NewSendMessage(msgType, "", fromUserName, toUserName, mediaId)
+ return NewSendMessage(msgType, "", fromUserName, toUserName, mediaId)
}
// 一些特殊类型的消息会携带该结构体信息
type RecommendInfo struct {
- OpCode int
- Scene int
- Sex int
- VerifyFlag int
- AttrStatus int64
- QQNum int64
- Alias string
- City string
- Content string
- NickName string
- Province string
- Signature string
- Ticket string
- UserName string
+ OpCode int
+ Scene int
+ Sex int
+ VerifyFlag int
+ AttrStatus int64
+ QQNum int64
+ Alias string
+ City string
+ Content string
+ NickName string
+ Province string
+ Signature string
+ Ticket string
+ UserName string
}
// 名片消息内容
type Card struct {
- XMLName xml.Name `xml:"msg"`
- ImageStatus int `xml:"imagestatus,attr"`
- Scene int `xml:"scene,attr"`
- Sex int `xml:"sex,attr"`
- Certflag int `xml:"certflag,attr"`
- BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
- SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
- UserName string `xml:"username,attr"`
- NickName string `xml:"nickname,attr"`
- ShortPy string `xml:"shortpy,attr"`
- Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号
- Province string `xml:"province,attr"`
- City string `xml:"city,attr"`
- Sign string `xml:"sign,attr"`
- Certinfo string `xml:"certinfo,attr"`
- BrandIconUrl string `xml:"brandIconUrl,attr"`
- BrandHomeUr string `xml:"brandHomeUr,attr"`
- BrandSubscriptConfigUrl string `xml:"brandSubscriptConfigUrl,attr"`
- BrandFlags string `xml:"brandFlags,attr"`
- RegionCode string `xml:"regionCode,attr"`
+ XMLName xml.Name `xml:"msg"`
+ ImageStatus int `xml:"imagestatus,attr"`
+ Scene int `xml:"scene,attr"`
+ Sex int `xml:"sex,attr"`
+ Certflag int `xml:"certflag,attr"`
+ BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
+ SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
+ UserName string `xml:"username,attr"`
+ NickName string `xml:"nickname,attr"`
+ ShortPy string `xml:"shortpy,attr"`
+ Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号
+ Province string `xml:"province,attr"`
+ City string `xml:"city,attr"`
+ Sign string `xml:"sign,attr"`
+ Certinfo string `xml:"certinfo,attr"`
+ BrandIconUrl string `xml:"brandIconUrl,attr"`
+ BrandHomeUr string `xml:"brandHomeUr,attr"`
+ BrandSubscriptConfigUrl string `xml:"brandSubscriptConfigUrl,attr"`
+ BrandFlags string `xml:"brandFlags,attr"`
+ RegionCode string `xml:"regionCode,attr"`
+}
+
+// 好友添加消息信息内容
+type FriendAddMessageContent struct {
+ XMLName xml.Name `xml:"msg"`
+ Shortpy int `xml:"shortpy,attr"`
+ ImageStatus int `xml:"imagestatus,attr"`
+ Scene int `xml:"scene,attr"`
+ PerCard int `xml:"percard,attr"`
+ Sex int `xml:"sex,attr"`
+ AlbumFlag int `xml:"albumflag,attr"`
+ AlbumStyle int `xml:"albumstyle,attr"`
+ SnsFlag int `xml:"snsflag,attr"`
+ Opcode int `xml:"opcode,attr"`
+ FromUserName string `xml:"fromusername,attr"`
+ EncryptUserName string `xml:"encryptusername,attr"`
+ FromNickName string `xml:"fromnickname,attr"`
+ Content string `xml:"content,attr"`
+ Country string `xml:"country,attr"`
+ Province string `xml:"province,attr"`
+ City string `xml:"city,attr"`
+ Sign string `xml:"sign,attr"`
+ Alias string `xml:"alias,attr"`
+ WeiBo string `xml:"weibo,attr"`
+ AlbumBgImgId string `xml:"albumbgimgid,attr"`
+ SnsBgImgId string `xml:"snsbgimgid,attr"`
+ SnsBgObjectId string `xml:"snsbgobjectid,attr"`
+ MHash string `xml:"mhash,attr"`
+ MFullHash string `xml:"mfullhash,attr"`
+ BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
+ SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
+ Ticket string `xml:"ticket,attr"`
+ GoogleContact string `xml:"googlecontact,attr"`
+ QrTicket string `xml:"qrticket,attr"`
+ ChatRoomUserName string `xml:"chatroomusername,attr"`
+ SourceUserName string `xml:"sourceusername,attr"`
+ ShareCardUserName string `xml:"sharecardusername,attr"`
+ ShareCardNickName string `xml:"sharecardnickname,attr"`
+ CardVersion string `xml:"cardversion,attr"`
+ BrandList struct {
+ Count int `xml:"count,attr"`
+ Ver int64 `xml:"ver,attr"`
+ } `xml:"brandlist"`
}