添加同意好友请求支持
This commit is contained in:
parent
72338d99bb
commit
2d4d7e7d78
17
bot_test.go
17
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()
|
||||
}
|
||||
|
366
caller.go
366
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
|
||||
}
|
||||
|
@ -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{
|
||||
|
514
message.go
514
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, ":<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"`
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user