优化对象的字段的排列

This commit is contained in:
eatMoreApple 2021-04-27 11:12:46 +08:00
parent 7ddcde9ab6
commit d84fbe1681
3 changed files with 302 additions and 303 deletions

322
bot.go
View File

@ -1,223 +1,223 @@
package openwechat package openwechat
import ( import (
"errors" "errors"
"fmt" "fmt"
) )
type Bot struct { type Bot struct {
Caller *Caller ScanCallBack func(body []byte)
self *Self LoginCallBack func(body []byte)
storage *Storage UUIDCallback func(uuid string)
ScanCallBack func(body []byte) MessageHandler func(msg *Message)
LoginCallBack func(body []byte) err error
UUIDCallback func(uuid string) exit chan bool
MessageHandler func(msg *Message) Caller *Caller
err error self *Self
exit chan bool storage *Storage
} }
// 判断当前用户是否正常在线 // 判断当前用户是否正常在线
func (b *Bot) Alive() bool { func (b *Bot) Alive() bool {
if b.self == nil { if b.self == nil {
return false return false
} }
select { select {
case <-b.exit: case <-b.exit:
return false return false
default: default:
return true return true
} }
} }
// 获取当前的用户 // 获取当前的用户
func (b *Bot) GetCurrentUser() (*Self, error) { func (b *Bot) GetCurrentUser() (*Self, error) {
if b.self == nil { if b.self == nil {
return nil, errors.New("user not login") return nil, errors.New("user not login")
} }
return b.self, nil return b.self, nil
} }
// 用户登录 // 用户登录
// 该方法会一直阻塞,直到用户扫码登录,或者二维码过期 // 该方法会一直阻塞,直到用户扫码登录,或者二维码过期
func (b *Bot) Login() error { func (b *Bot) Login() error {
uuid, err := b.Caller.GetLoginUUID() uuid, err := b.Caller.GetLoginUUID()
if err != nil { if err != nil {
return err return err
} }
if b.UUIDCallback != nil { if b.UUIDCallback != nil {
b.UUIDCallback(uuid) b.UUIDCallback(uuid)
} }
for { for {
resp, err := b.Caller.CheckLogin(uuid) resp, err := b.Caller.CheckLogin(uuid)
if err != nil { if err != nil {
return err return err
} }
switch resp.Code { switch resp.Code {
case statusSuccess: case statusSuccess:
return b.login(resp.Raw) return b.login(resp.Raw)
case statusScanned: case statusScanned:
if b.ScanCallBack != nil { if b.ScanCallBack != nil {
b.ScanCallBack(resp.Raw) b.ScanCallBack(resp.Raw)
} }
case statusTimeout: case statusTimeout:
return errors.New("login time out") return errors.New("login time out")
case statusWait: case statusWait:
continue continue
} }
} }
} }
func (b *Bot) Logout() error { func (b *Bot) Logout() error {
if b.Alive() { if b.Alive() {
info := b.storage.LoginInfo info := b.storage.LoginInfo
if err := b.Caller.Logout(info); err != nil { if err := b.Caller.Logout(info); err != nil {
return err return err
} }
b.stopAsyncCALL(errors.New("logout")) b.stopAsyncCALL(errors.New("logout"))
return nil return nil
} }
return errors.New("user not login") return errors.New("user not login")
} }
// 登录逻辑 // 登录逻辑
func (b *Bot) login(data []byte) error { func (b *Bot) login(data []byte) error {
// 判断是否有登录回调,如果有执行它 // 判断是否有登录回调,如果有执行它
if b.LoginCallBack != nil { if b.LoginCallBack != nil {
b.LoginCallBack(data) b.LoginCallBack(data)
} }
// 获取登录的一些基本的信息 // 获取登录的一些基本的信息
info, err := b.Caller.GetLoginInfo(data) info, err := b.Caller.GetLoginInfo(data)
if err != nil { if err != nil {
return err return err
} }
// 将LoginInfo存到storage里面 // 将LoginInfo存到storage里面
b.storage.LoginInfo = info b.storage.LoginInfo = info
// 构建BaseRequest // 构建BaseRequest
request := &BaseRequest{ request := &BaseRequest{
Uin: info.WxUin, Uin: info.WxUin,
Sid: info.WxSid, Sid: info.WxSid,
Skey: info.SKey, Skey: info.SKey,
DeviceID: GetRandomDeviceId(), DeviceID: GetRandomDeviceId(),
} }
// 将BaseRequest存到storage里面方便后续调用 // 将BaseRequest存到storage里面方便后续调用
b.storage.Request = request b.storage.Request = request
// 获取初始化的用户信息和一些必要的参数 // 获取初始化的用户信息和一些必要的参数
resp, err := b.Caller.WebInit(request) resp, err := b.Caller.WebInit(request)
if err != nil { if err != nil {
return err return err
} }
// 设置当前的用户 // 设置当前的用户
b.self = &Self{Bot: b, User: &resp.User} b.self = &Self{Bot: b, User: &resp.User}
b.self.Self = b.self b.self.Self = b.self
b.storage.Response = resp b.storage.Response = resp
// 通知手机客户端已经登录 // 通知手机客户端已经登录
if err = b.Caller.WebWxStatusNotify(request, resp, info); err != nil { if err = b.Caller.WebWxStatusNotify(request, resp, info); err != nil {
return err return err
} }
// 开启协程,轮训获取是否有新的消息返回 // 开启协程,轮训获取是否有新的消息返回
go func() { go func() {
b.stopAsyncCALL(b.asyncCall()) b.stopAsyncCALL(b.asyncCall())
}() }()
return nil return nil
} }
// 轮训请求 // 轮训请求
// 根据状态码判断是否有新的请求 // 根据状态码判断是否有新的请求
func (b *Bot) asyncCall() error { func (b *Bot) asyncCall() error {
var ( var (
err error err error
resp *SyncCheckResponse resp *SyncCheckResponse
) )
for b.Alive() { for b.Alive() {
info := b.storage.LoginInfo info := b.storage.LoginInfo
response := b.storage.Response response := b.storage.Response
resp, err = b.Caller.SyncCheck(info, response) resp, err = b.Caller.SyncCheck(info, response)
if err != nil { if err != nil {
return err return err
} }
// 如果不是正常的状态码返回,发生了错误,直接退出 // 如果不是正常的状态码返回,发生了错误,直接退出
if !resp.Success() { if !resp.Success() {
return fmt.Errorf("unknow code got %s", resp.RetCode) return fmt.Errorf("unknow code got %s", resp.RetCode)
} }
// 如果Selector不为0则获取消息 // 如果Selector不为0则获取消息
if !resp.NorMal() { if !resp.NorMal() {
if err = b.getMessage(); err != nil { if err = b.getMessage(); err != nil {
return err return err
} }
} }
} }
return err return err
} }
func (b *Bot) stopAsyncCALL(err error) { func (b *Bot) stopAsyncCALL(err error) {
b.exit <- true b.exit <- true
b.err = err b.err = err
b.self = nil b.self = nil
} }
// 获取新的消息 // 获取新的消息
func (b *Bot) getMessage() error { func (b *Bot) getMessage() error {
resp, err := b.Caller.WebWxSync(b.storage.Request, b.storage.Response, b.storage.LoginInfo) resp, err := b.Caller.WebWxSync(b.storage.Request, b.storage.Response, b.storage.LoginInfo)
if err != nil { if err != nil {
return err return err
} }
// 更新SyncKey并且重新存入storage // 更新SyncKey并且重新存入storage
b.storage.Response.SyncKey = resp.SyncKey b.storage.Response.SyncKey = resp.SyncKey
// 遍历所有的新的消息,依次处理 // 遍历所有的新的消息,依次处理
for _, message := range resp.AddMsgList { for _, message := range resp.AddMsgList {
// 根据不同的消息类型来进行处理,方便后续统一调用 // 根据不同的消息类型来进行处理,方便后续统一调用
message.init(b) message.init(b)
// 调用自定义的处理方法 // 调用自定义的处理方法
if handler := b.MessageHandler; handler != nil { if handler := b.MessageHandler; handler != nil {
handler(message) handler(message)
} }
} }
return nil return nil
} }
// 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞 // 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞
func (b *Bot) Block() error { func (b *Bot) Block() error {
if b.self == nil { if b.self == nil {
return errors.New("`Block` must be called after user login") return errors.New("`Block` must be called after user login")
} }
if _, closed := <-b.exit; !closed { if _, closed := <-b.exit; !closed {
return errors.New("can not call `Block` after user logout") return errors.New("can not call `Block` after user logout")
} }
close(b.exit) close(b.exit)
return nil return nil
} }
func NewBot(caller *Caller) *Bot { func NewBot(caller *Caller) *Bot {
return &Bot{Caller: caller, storage: &Storage{}, exit: make(chan bool)} return &Bot{Caller: caller, storage: &Storage{}, exit: make(chan bool)}
} }
func DefaultBot(modes ...mode) *Bot { func DefaultBot(modes ...mode) *Bot {
var m mode var m mode
if len(modes) == 0 { if len(modes) == 0 {
m = Normal m = Normal
} else { } else {
m = modes[0] m = modes[0]
} }
urlManager := GetUrlManagerByMode(m) urlManager := GetUrlManagerByMode(m)
return NewBot(DefaultCaller(urlManager)) return NewBot(DefaultCaller(urlManager))
} }
type Storage struct { type Storage struct {
LoginInfo *LoginInfo LoginInfo *LoginInfo
Request *BaseRequest Request *BaseRequest
Response *WebInitResponse Response *WebInitResponse
} }
func GetQrcodeUrl(uuid string) string { func GetQrcodeUrl(uuid string) string {
return qrcodeUrl + uuid return qrcodeUrl + uuid
} }
func PrintlnQrcodeUrl(uuid string) { func PrintlnQrcodeUrl(uuid string) {
println("访问下面网址扫描二维码登录") println("访问下面网址扫描二维码登录")
println(GetQrcodeUrl(uuid)) println(GetQrcodeUrl(uuid))
} }

220
items.go
View File

@ -8,173 +8,173 @@ import "fmt"
// 登录信息 // 登录信息
type LoginInfo struct { type LoginInfo struct {
Ret int `xml:"ret"` Ret int `xml:"ret"`
Message string `xml:"message"` WxUin int `xml:"wxuin"`
SKey string `xml:"skey"` IsGrayScale int `xml:"isgrayscale"`
WxSid string `xml:"wxsid"` Message string `xml:"message"`
WxUin int `xml:"wxuin"` SKey string `xml:"skey"`
PassTicket string `xml:"pass_ticket"` WxSid string `xml:"wxsid"`
IsGrayScale int `xml:"isgrayscale"` PassTicket string `xml:"pass_ticket"`
} }
// 初始的请求信息 // 初始的请求信息
// 几乎所有的请求都要携带该参数 // 几乎所有的请求都要携带该参数
type BaseRequest struct { type BaseRequest struct {
Uin int Uin int
Sid, Skey, DeviceID string Sid, Skey, DeviceID string
} }
// 大部分返回对象都携带该信息 // 大部分返回对象都携带该信息
type BaseResponse struct { type BaseResponse struct {
ErrMsg string Ret int
Ret int ErrMsg string
} }
func (b BaseResponse) Ok() bool { func (b BaseResponse) Ok() bool {
return b.Ret == 0 return b.Ret == 0
} }
func (b BaseResponse) Error() string { func (b BaseResponse) Error() string {
switch b.Ret { switch b.Ret {
case 0: case 0:
return "" return ""
case 1: case 1:
return "param error" return "param error"
case -14: case -14:
return "ticket error" return "ticket error"
case 1100: case 1100:
return "not login warn" return "not login warn"
case 1101: case 1101:
return "not login check" return "not login check"
case 1102: case 1102:
return "cookie invalid error" return "cookie invalid error"
case 1203: case 1203:
return "login env error" return "login env error"
case 1205: case 1205:
return "opt too often" return "opt too often"
default: default:
return fmt.Sprintf("base response ret code %d", b.Ret) return fmt.Sprintf("base response ret code %d", b.Ret)
} }
} }
type SyncKey struct { type SyncKey struct {
Count int Count int
List []struct{ Key, Val int64 } List []struct{ Key, Val int64 }
} }
// 初始化的相应信息 // 初始化的相应信息
type WebInitResponse struct { type WebInitResponse struct {
BaseResponse BaseResponse Count int
Count int ClientVersion int
ChatSet string GrayScale int
SKey string InviteStartCount int
SyncKey SyncKey MPSubscribeMsgCount int
User User ClickReportInterval int
ClientVersion int SystemTime int64
SystemTime int64 ChatSet string
GrayScale int SKey string
InviteStartCount int BaseResponse BaseResponse
MPSubscribeMsgCount int SyncKey SyncKey
MPSubscribeMsgList []MPSubscribeMsg User User
ClickReportInterval int MPSubscribeMsgList []MPSubscribeMsg
ContactList []User ContactList []User
} }
// 公众号的订阅信息 // 公众号的订阅信息
type MPSubscribeMsg struct { type MPSubscribeMsg struct {
UserName string MPArticleCount int
Time int64 Time int64
NickName string UserName string
MPArticleCount int NickName string
MPArticleList []struct { MPArticleList []struct {
Title string Title string
Cover string Cover string
Digest string Digest string
Url string Url string
} }
} }
type UserDetailItem struct { type UserDetailItem struct {
UserName string UserName string
EncryChatRoomId string EncryChatRoomId string
} }
type UserDetailItemList []UserDetailItem type UserDetailItemList []UserDetailItem
func NewUserDetailItemList(members Members) UserDetailItemList { func NewUserDetailItemList(members Members) UserDetailItemList {
var list UserDetailItemList var list UserDetailItemList
for _, member := range members { for _, member := range members {
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId} item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
list = append(list, item) list = append(list, item)
} }
return list return list
} }
type SyncCheckResponse struct { type SyncCheckResponse struct {
RetCode string RetCode string
Selector string Selector string
} }
func (s *SyncCheckResponse) Success() bool { func (s *SyncCheckResponse) Success() bool {
return s.RetCode == "0" return s.RetCode == "0"
} }
func (s *SyncCheckResponse) NorMal() bool { func (s *SyncCheckResponse) NorMal() bool {
return s.Success() && s.Selector == "0" return s.Success() && s.Selector == "0"
} }
// 实现error接口 // 实现error接口
func (s *SyncCheckResponse) Error() string { func (s *SyncCheckResponse) Error() string {
switch s.RetCode { switch s.RetCode {
case "0": case "0":
return "" return ""
case "1": case "1":
return "param error" return "param error"
case "-14": case "-14":
return "ticker error" return "ticker error"
case "1100": case "1100":
return "not login warn" return "not login warn"
case "1101": case "1101":
return "not login check" return "not login check"
case "1102": case "1102":
return "cookie invalid error" return "cookie invalid error"
case "1203": case "1203":
return "login env error" return "login env error"
case "1205": case "1205":
return "opt too often" return "opt too often"
default: default:
return fmt.Sprintf("sync check response error code %s", s.RetCode) return fmt.Sprintf("sync check response error code %s", s.RetCode)
} }
} }
type WebWxSyncResponse struct { type WebWxSyncResponse struct {
AddMsgCount int AddMsgCount int
AddMsgList []*Message ContinueFlag int
BaseResponse BaseResponse DelContactCount int
ContinueFlag int ModChatRoomMemberCount int
DelContactCount int ModContactCount int
ModChatRoomMemberCount int Skey string
ModChatRoomMemberList Members SyncCheckKey SyncKey
ModContactCount int SyncKey SyncKey
Skey string BaseResponse BaseResponse
SyncCheckKey SyncKey ModChatRoomMemberList Members
SyncKey SyncKey AddMsgList []*Message
} }
type WebWxContactResponse struct { type WebWxContactResponse struct {
BaseResponse BaseResponse MemberCount int
MemberCount int Seq int
MemberList []*User BaseResponse BaseResponse
Seq int MemberList []*User
} }
type WebWxBatchContactResponse struct { type WebWxBatchContactResponse struct {
BaseResponse BaseResponse Count int
ContactList []*User BaseResponse BaseResponse
Count int ContactList []*User
} }
type CheckLoginResponse struct { type CheckLoginResponse struct {
Code string Code string
Raw []byte Raw []byte
} }

View File

@ -13,44 +13,43 @@ import (
) )
type Message struct { type Message struct {
IsAt bool
AppInfo struct { AppInfo struct {
AppID string
Type int Type int
AppID string
} }
AppMsgType int AppMsgType int
Content string HasProductId int
CreateTime int64 ImgHeight int
EncryFileName string ImgStatus int
FileName string ImgWidth int
FileSize string ForwardFlag int
ForwardFlag int MsgType int
FromUserName string Status int
HasProductId int StatusNotifyCode int
ImgHeight int SubMsgType int
ImgStatus int VoiceLength int
ImgWidth int CreateTime int64
MediaId string NewMsgId int64
MsgId string PlayLength int64
MsgType int MediaId string
NewMsgId int64 MsgId string
OriContent string EncryFileName string
PlayLength int64 FileName string
RecommendInfo RecommendInfo FileSize string
Status int Content string
StatusNotifyCode int FromUserName string
StatusNotifyUserName string OriContent string
SubMsgType int StatusNotifyUserName string
Ticket string Ticket string
ToUserName string ToUserName string
Url string Url string
VoiceLength int
IsAt bool
Bot *Bot
senderInGroupUserName string senderInGroupUserName string
RecommendInfo RecommendInfo
Bot *Bot
mu sync.RWMutex mu sync.RWMutex
item map[string]interface{}
Context context.Context Context context.Context
item map[string]interface{}
} }
// 获取消息的发送者 // 获取消息的发送者