添加同意好友请求支持

This commit is contained in:
eatMoreApple 2021-04-27 15:17:18 +08:00
parent 72338d99bb
commit 2d4d7e7d78
4 changed files with 491 additions and 408 deletions

View File

@ -196,3 +196,20 @@ func TestSendMessage(t *testing.T) {
return 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()
}

366
caller.go
View File

@ -1,262 +1,268 @@
package openwechat package openwechat
import ( import (
"errors" "errors"
"os" "os"
) )
// 调用请求和解析请求 // 调用请求和解析请求
// 上层模块可以直接获取封装后的请求结果 // 上层模块可以直接获取封装后的请求结果
type Caller struct { type Caller struct {
Client *Client Client *Client
} }
// Constructor for Caller // Constructor for Caller
func NewCaller(client *Client) *Caller { func NewCaller(client *Client) *Caller {
return &Caller{Client: client} return &Caller{Client: client}
} }
// Default Constructor for Caller // Default Constructor for Caller
func DefaultCaller(urlManager UrlManager) *Caller { func DefaultCaller(urlManager UrlManager) *Caller {
return NewCaller(DefaultClient(urlManager)) return NewCaller(DefaultClient(urlManager))
} }
// 获取登录的uuid // 获取登录的uuid
func (c *Caller) GetLoginUUID() (string, error) { func (c *Caller) GetLoginUUID() (string, error) {
resp := NewReturnResponse(c.Client.GetLoginUUID()) resp := NewReturnResponse(c.Client.GetLoginUUID())
if resp.Err() != nil { if resp.Err() != nil {
return "", resp.Err() return "", resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := resp.ReadAll() data, err := resp.ReadAll()
if err != nil { if err != nil {
return "", err return "", err
} }
// 正则匹配uuid字符串 // 正则匹配uuid字符串
results := uuidRegexp.FindSubmatch(data) results := uuidRegexp.FindSubmatch(data)
if len(results) != 2 { if len(results) != 2 {
// 如果没有匹配到,可能微信的接口做了修改或者当前机器的ip被加入了黑名单 // 如果没有匹配到,可能微信的接口做了修改或者当前机器的ip被加入了黑名单
return "", errors.New("uuid does not match") return "", errors.New("uuid does not match")
} }
return string(results[1]), nil return string(results[1]), nil
} }
// 检查是否登录成功 // 检查是否登录成功
func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) { func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
resp := NewReturnResponse(c.Client.CheckLogin(uuid)) resp := NewReturnResponse(c.Client.CheckLogin(uuid))
if resp.Err() != nil { if resp.Err() != nil {
return nil, resp.Err() return nil, resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := resp.ReadAll() data, err := resp.ReadAll()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 正则匹配检测的code // 正则匹配检测的code
// 具体code参考global.go // 具体code参考global.go
results := statusCodeRegexp.FindSubmatch(data) results := statusCodeRegexp.FindSubmatch(data)
if len(results) != 2 { if len(results) != 2 {
return nil, errors.New("error status code match") return nil, errors.New("error status code match")
} }
code := string(results[1]) code := string(results[1])
return &CheckLoginResponse{Code: code, Raw: data}, nil return &CheckLoginResponse{Code: code, Raw: data}, nil
} }
// 获取登录信息 // 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) { func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
// 从响应体里面获取需要跳转的url // 从响应体里面获取需要跳转的url
results := redirectUriRegexp.FindSubmatch(body) results := redirectUriRegexp.FindSubmatch(body)
if len(results) != 2 { if len(results) != 2 {
return nil, errors.New("redirect url does not match") return nil, errors.New("redirect url does not match")
} }
path := string(results[1]) path := string(results[1])
resp := NewReturnResponse(c.Client.GetLoginInfo(path)) resp := NewReturnResponse(c.Client.GetLoginInfo(path))
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 loginInfo LoginInfo var loginInfo LoginInfo
// xml结构体序列化储存 // xml结构体序列化储存
if err := resp.ScanXML(&loginInfo); err != nil { if err := resp.ScanXML(&loginInfo); err != nil {
return nil, err return nil, err
} }
return &loginInfo, nil return &loginInfo, nil
} }
// 获取初始化信息 // 获取初始化信息
func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) { func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) {
resp := NewReturnResponse(c.Client.WebInit(request)) resp := NewReturnResponse(c.Client.WebInit(request))
if resp.Err() != nil { if resp.Err() != nil {
return nil, resp.Err() return nil, resp.Err()
} }
var webInitResponse WebInitResponse var webInitResponse WebInitResponse
defer resp.Body.Close() defer resp.Body.Close()
if err := resp.ScanJSON(&webInitResponse); err != nil { if err := resp.ScanJSON(&webInitResponse); err != nil {
return nil, err return nil, err
} }
return &webInitResponse, nil return &webInitResponse, nil
} }
// 通知手机已登录 // 通知手机已登录
func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error { func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error {
resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info)) resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info))
if resp.Err() != nil { if resp.Err() != nil {
return resp.Err() return resp.Err()
} }
var item struct{ BaseResponse BaseResponse } var item struct{ BaseResponse BaseResponse }
defer resp.Body.Close() defer resp.Body.Close()
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return err return err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return item.BaseResponse
} }
return nil return nil
} }
// 异步获取是否有新的消息 // 异步获取是否有新的消息
func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) { func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) {
resp := NewReturnResponse(c.Client.SyncCheck(info, response)) resp := NewReturnResponse(c.Client.SyncCheck(info, response))
if resp.Err() != nil { if resp.Err() != nil {
return nil, resp.Err() return nil, resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := resp.ReadAll() data, err := resp.ReadAll()
if err != nil { if err != nil {
return nil, err return nil, err
} }
results := syncCheckRegexp.FindSubmatch(data) results := syncCheckRegexp.FindSubmatch(data)
if len(results) != 3 { if len(results) != 3 {
return nil, errors.New("parse sync key failed") return nil, errors.New("parse sync key failed")
} }
retCode, selector := string(results[1]), string(results[2]) retCode, selector := string(results[1]), string(results[2])
syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector} syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector}
return syncCheckResponse, nil return syncCheckResponse, nil
} }
// 获取所有的联系人 // 获取所有的联系人
func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) { func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxGetContact(info)) resp := NewReturnResponse(c.Client.WebWxGetContact(info))
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 WebWxContactResponse var item WebWxContactResponse
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return nil, err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return nil, item.BaseResponse return nil, item.BaseResponse
} }
return item.MemberList, nil return item.MemberList, nil
} }
// 获取联系人的详情 // 获取联系人的详情
// 注: Members参数的长度不要大于50 // 注: Members参数的长度不要大于50
func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) { func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request)) resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request))
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 WebWxBatchContactResponse var item WebWxBatchContactResponse
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return nil, err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return nil, item.BaseResponse return nil, item.BaseResponse
} }
return item.ContactList, nil return item.ContactList, nil
} }
// 获取新的消息接口 // 获取新的消息接口
func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*WebWxSyncResponse, error) { func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*WebWxSyncResponse, error) {
resp := NewReturnResponse(c.Client.WebWxSync(request, response, info)) resp := NewReturnResponse(c.Client.WebWxSync(request, response, info))
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 webWxSyncResponse WebWxSyncResponse var webWxSyncResponse WebWxSyncResponse
if err := resp.ScanJSON(&webWxSyncResponse); err != nil { if err := resp.ScanJSON(&webWxSyncResponse); err != nil {
return nil, err return nil, err
} }
return &webWxSyncResponse, nil return &webWxSyncResponse, nil
} }
// 发送消息接口 // 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) error { func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) error {
resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request)) resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// 修改用户备注接口 // 修改用户备注接口
func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error { func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error {
resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName)) resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName))
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// 发送图片消息接口 // 发送图片消息接口
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) 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 resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
var item struct { var item struct {
BaseResponse BaseResponse BaseResponse BaseResponse
MediaId string MediaId string
} }
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return err return err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return 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) return parseBaseResponseError(resp)
} }
// 用户退出 // 用户退出
func (c *Caller) Logout(info *LoginInfo) error { func (c *Caller) Logout(info *LoginInfo) error {
resp := NewReturnResponse(c.Client.Logout(info)) resp := NewReturnResponse(c.Client.Logout(info))
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// 拉好友入群 // 拉好友入群
func (c *Caller) AddFriendIntoChatRoom(req *BaseRequest, info *LoginInfo, group *Group, friends ...*Friend) error { func (c *Caller) AddFriendIntoChatRoom(req *BaseRequest, info *LoginInfo, group *Group, friends ...*Friend) error {
if len(friends) == 0 { if len(friends) == 0 {
return errors.New("no friends found") return errors.New("no friends found")
} }
resp := NewReturnResponse(c.Client.AddMemberIntoChatRoom(req, info, group, friends...)) resp := NewReturnResponse(c.Client.AddMemberIntoChatRoom(req, info, group, friends...))
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// 从群聊中移除用户 // 从群聊中移除用户
func (c *Caller) RemoveFriendFromChatRoom(req *BaseRequest, info *LoginInfo, group *Group, users ...*User) error { func (c *Caller) RemoveFriendFromChatRoom(req *BaseRequest, info *LoginInfo, group *Group, users ...*User) error {
if len(users) == 0 { if len(users) == 0 {
return errors.New("no users found") return errors.New("no users found")
} }
resp := NewReturnResponse(c.Client.RemoveMemberFromChatRoom(req, info, group, users...)) resp := NewReturnResponse(c.Client.RemoveMemberFromChatRoom(req, info, group, users...))
return parseBaseResponseError(resp) 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 { func parseBaseResponseError(resp *ReturnResponse) error {
if resp.Err() != nil { if resp.Err() != nil {
return resp.Err() return resp.Err()
} }
defer resp.Body.Close() defer resp.Body.Close()
var item struct{ BaseResponse BaseResponse } var item struct{ BaseResponse BaseResponse }
if err := resp.ScanJSON(&item); err != nil { if err := resp.ScanJSON(&item); err != nil {
return err return err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return item.BaseResponse
} }
return nil return nil
} }

View File

@ -340,7 +340,7 @@ func (c *Client) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyCon
content := map[string]interface{}{ content := map[string]interface{}{
"BaseRequest": storage.Request, "BaseRequest": storage.Request,
"Opcode": 3, "Opcode": 3,
"SceneList": []int{33}, "SceneList": [1]int{33},
"SceneListCount": 1, "SceneListCount": 1,
"VerifyContent": verifyContent, "VerifyContent": verifyContent,
"VerifyUserList": []interface{}{map[string]string{ "VerifyUserList": []interface{}{map[string]string{

View File

@ -1,353 +1,413 @@
package openwechat package openwechat
import ( import (
"context" "context"
"encoding/xml" "encoding/xml"
"errors" "errors"
"net/http" "fmt"
"os" "net/http"
"strconv" "os"
"strings" "strconv"
"sync" "strings"
"time" "sync"
"unicode" "time"
"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) error { func (m *Message) Reply(msgType int, content, mediaId string) 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) error { 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 { func (m *Message) ReplyImage(file *os.File) 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 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) 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内容
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 // 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:]
} }
} }
} }
} }
//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 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().Unix()*1e4, 10) id := strconv.FormatInt(time.Now().Unix()*1e4, 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 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"`
} }