添加同意好友请求支持

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
}
}
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
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
}

View File

@ -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{

View File

@ -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, ":<br/>")
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, ":<br/>")
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"`
}