更新发送消息结构体

This commit is contained in:
eatMoreApple 2021-04-27 11:59:16 +08:00
parent 55cf87e294
commit 72338d99bb
2 changed files with 222 additions and 218 deletions

View File

@ -3,6 +3,7 @@ package openwechat
import ( import (
"fmt" "fmt"
"testing" "testing"
"time"
) )
func defaultBot(modes ...mode) *Bot { func defaultBot(modes ...mode) *Bot {
@ -189,6 +190,7 @@ func TestSendMessage(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
time.Sleep(time.Second)
if err = self.SendTextToFriend(helper, "send test message twice ! received?"); err != nil { if err = self.SendTextToFriend(helper, "send test message twice ! received?"); err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -1,270 +1,271 @@
package openwechat package openwechat
import ( import (
"context" "context"
"encoding/xml" "encoding/xml"
"errors" "errors"
"net/http" "net/http"
"os" "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
} }
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
} }
// 往消息上下文中设置值 // 往消息上下文中设置值
// 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 { //func (m *Message) Agree() error {
@ -276,76 +277,77 @@ func (m *Message) init(bot *Bot) {
// 发送消息的结构体 // 发送消息的结构体
type SendMessage struct { type SendMessage struct {
Type int Type int
Content string Content string
FromUserName string FromUserName string
ToUserName string ToUserName string
LocalID int64 LocalID string
ClientMsgId int64 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 {
return &SendMessage{ id := strconv.FormatInt(time.Now().Unix()*1e4, 10)
Type: msgType, return &SendMessage{
Content: content, Type: msgType,
FromUserName: fromUserName, Content: content,
ToUserName: toUserName, FromUserName: fromUserName,
LocalID: time.Now().Unix() * 1e4, ToUserName: toUserName,
ClientMsgId: time.Now().Unix() * 1e4, LocalID: id,
MediaId: mediaId, ClientMsgId: id,
} 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 {
Alias string OpCode int
AttrStatus int64 Scene int
City string Sex int
Content string VerifyFlag int
NickName string AttrStatus int64
OpCode int QQNum int64
Province string Alias string
QQNum int64 City string
Scene int Content string
Sex int NickName string
Signature string Province string
Ticket string Signature string
UserName string Ticket string
VerifyFlag int UserName string
} }
// 名片消息内容 // 名片消息内容
type Card struct { type Card struct {
XMLName xml.Name `xml:"msg"` XMLName xml.Name `xml:"msg"`
BigHeadImgUrl string `xml:"bigheadimgurl,attr"` ImageStatus int `xml:"imagestatus,attr"`
SmallHeadImgUrl string `xml:"smallheadimgurl,attr"` Scene int `xml:"scene,attr"`
UserName string `xml:"username,attr"` Sex int `xml:"sex,attr"`
NickName string `xml:"nickname,attr"` Certflag int `xml:"certflag,attr"`
ShortPy string `xml:"shortpy,attr"` BigHeadImgUrl string `xml:"bigheadimgurl,attr"`
Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号 SmallHeadImgUrl string `xml:"smallheadimgurl,attr"`
ImageStatus int `xml:"imagestatus,attr"` UserName string `xml:"username,attr"`
Scene int `xml:"scene,attr"` NickName string `xml:"nickname,attr"`
Province string `xml:"province,attr"` ShortPy string `xml:"shortpy,attr"`
City string `xml:"city,attr"` Alias string `xml:"alias,attr"` // Note: 这个是名片用户的微信号
Sign string `xml:"sign,attr"` Province string `xml:"province,attr"`
Sex int `xml:"sex,attr"` City string `xml:"city,attr"`
Certflag int `xml:"certflag,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"`
} }