修改同步消息时的deviceId

This commit is contained in:
eatmoreapple 2021-08-23 20:53:39 +08:00
parent 9eb84837cb
commit 93564fabb5
5 changed files with 438 additions and 438 deletions

494
bot.go
View File

@ -1,43 +1,43 @@
package openwechat package openwechat
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"log" "log"
"net/url" "net/url"
"sync" "sync"
) )
type Bot struct { type Bot struct {
ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像 ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像
LoginCallBack func(body []byte) // 登陆回调 LoginCallBack func(body []byte) // 登陆回调
LogoutCallBack func(bot *Bot) // 退出回调 LogoutCallBack func(bot *Bot) // 退出回调
UUIDCallback func(uuid string) // 获取UUID的回调函数 UUIDCallback func(uuid string) // 获取UUID的回调函数
MessageHandler MessageHandler // 获取消息成功的handle MessageHandler MessageHandler // 获取消息成功的handle
GetMessageErrorHandler func(err error) // 获取消息发生错误的handle GetMessageErrorHandler func(err error) // 获取消息发生错误的handle
IsHot bool // 是否为热登录模式 IsHot bool // 是否为热登录模式
once sync.Once once sync.Once
err error err error
context context.Context context context.Context
cancel context.CancelFunc cancel context.CancelFunc
Caller *Caller Caller *Caller
self *Self self *Self
Storage *Storage Storage *Storage
HotReloadStorage HotReloadStorage HotReloadStorage HotReloadStorage
} }
// Alive 判断当前用户是否正常在线 // Alive 判断当前用户是否正常在线
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.context.Done(): case <-b.context.Done():
return false return false
default: default:
return true return true
} }
} }
// GetCurrentUser 获取当前的用户 // GetCurrentUser 获取当前的用户
@ -47,10 +47,10 @@ func (b *Bot) Alive() bool {
// } // }
// fmt.Println(self.NickName) // fmt.Println(self.NickName)
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
} }
// HotLogin 热登录,可实现重复登录, // HotLogin 热登录,可实现重复登录,
@ -59,314 +59,314 @@ func (b *Bot) GetCurrentUser() (*Self, error) {
// err := bot.HotLogin(Storage, true) // err := bot.HotLogin(Storage, true)
// fmt.Println(err) // fmt.Println(err)
func (b *Bot) HotLogin(storage HotReloadStorage, retry ...bool) error { func (b *Bot) HotLogin(storage HotReloadStorage, retry ...bool) error {
b.IsHot = true b.IsHot = true
b.HotReloadStorage = storage b.HotReloadStorage = storage
var err error var err error
// 如果load出错了,就执行正常登陆逻辑 // 如果load出错了,就执行正常登陆逻辑
// 第一次没有数据load都会出错的 // 第一次没有数据load都会出错的
item, err := NewHotReloadStorageItem(storage) item, err := NewHotReloadStorageItem(storage)
if err != nil { if err != nil {
return b.Login() return b.Login()
} }
defer storage.Close() defer storage.Close()
if err = b.hotLoginInit(*item); err != nil { if err = b.hotLoginInit(*item); err != nil {
return err return err
} }
// 如果webInit出错,则说明可能身份信息已经失效 // 如果webInit出错,则说明可能身份信息已经失效
// 如果retry为True的话,则进行正常登陆 // 如果retry为True的话,则进行正常登陆
if err = b.WebInit(); err != nil { if err = b.WebInit(); err != nil {
if len(retry) > 0 && retry[0] { if len(retry) > 0 && retry[0] {
return b.Login() return b.Login()
} }
} }
return err return err
} }
// 热登陆初始化 // 热登陆初始化
func (b *Bot) hotLoginInit(item HotReloadStorageItem) error { func (b *Bot) hotLoginInit(item HotReloadStorageItem) error {
cookies := item.Cookies cookies := item.Cookies
for u, ck := range cookies { for u, ck := range cookies {
path, err := url.Parse(u) path, err := url.Parse(u)
if err != nil { if err != nil {
return err return err
} }
b.Caller.Client.Jar.SetCookies(path, ck) b.Caller.Client.Jar.SetCookies(path, ck)
} }
b.Storage.LoginInfo = item.LoginInfo b.Storage.LoginInfo = item.LoginInfo
b.Storage.Request = item.BaseRequest b.Storage.Request = item.BaseRequest
b.Caller.Client.Domain = item.WechatDomain b.Caller.Client.Domain = item.WechatDomain
return nil return nil
} }
// Login 用户登录 // Login 用户登录
// 该方法会一直阻塞,直到用户扫码登录,或者二维码过期 // 该方法会一直阻塞,直到用户扫码登录,或者二维码过期
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:
// 判断是否有登录回调,如果有执行它 // 判断是否有登录回调,如果有执行它
if b.LoginCallBack != nil { if b.LoginCallBack != nil {
b.LoginCallBack(resp.Raw) b.LoginCallBack(resp.Raw)
} }
return b.HandleLogin(resp.Raw) return b.HandleLogin(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 ErrLoginTimeout return ErrLoginTimeout
case StatusWait: case StatusWait:
continue continue
} }
} }
} }
// Logout 用户退出 // Logout 用户退出
func (b *Bot) Logout() error { func (b *Bot) Logout() error {
if b.Alive() { if b.Alive() {
if b.LogoutCallBack != nil { if b.LogoutCallBack != nil {
b.LogoutCallBack(b) b.LogoutCallBack(b)
} }
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")
} }
// HandleLogin 登录逻辑 // HandleLogin 登录逻辑
func (b *Bot) HandleLogin(data []byte) error { func (b *Bot) HandleLogin(data []byte) error {
// 获取登录的一些基本的信息 // 获取登录的一些基本的信息
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
// 如果是热登陆,则将当前的重要信息写入hotReloadStorage // 如果是热登陆,则将当前的重要信息写入hotReloadStorage
if b.IsHot { if b.IsHot {
if err = b.DumpHotReloadStorage(); err != nil { if err = b.DumpHotReloadStorage(); err != nil {
return err return err
} }
} }
return b.WebInit() return b.WebInit()
} }
// WebInit 根据有效凭证获取和初始化用户信息 // WebInit 根据有效凭证获取和初始化用户信息
func (b *Bot) WebInit() error { func (b *Bot) WebInit() error {
req := b.Storage.Request req := b.Storage.Request
info := b.Storage.LoginInfo info := b.Storage.LoginInfo
// 获取初始化的用户信息和一些必要的参数 // 获取初始化的用户信息和一些必要的参数
resp, err := b.Caller.WebInit(req) resp, err := b.Caller.WebInit(req)
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(req, resp, info); err != nil { if err = b.Caller.WebWxStatusNotify(req, resp, info); err != nil {
return err return err
} }
// 开启协程,轮询获取是否有新的消息返回 // 开启协程,轮询获取是否有新的消息返回
// FIX: 当bot在线的情况下执行热登录,会开启多次事件监听 // FIX: 当bot在线的情况下执行热登录,会开启多次事件监听
go b.once.Do(func() { go b.once.Do(func() {
if b.GetMessageErrorHandler == nil { if b.GetMessageErrorHandler == nil {
b.GetMessageErrorHandler = b.stopAsyncCALL b.GetMessageErrorHandler = b.stopAsyncCALL
} }
if err = b.asyncCall(); err != nil { if err = b.asyncCall(); err != nil {
b.GetMessageErrorHandler(err) b.GetMessageErrorHandler(err)
} }
}) })
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() {
// 长轮询检查是否有消息返回 // 长轮询检查是否有消息返回
resp, err = b.Caller.SyncCheck(b.Storage.LoginInfo, b.Storage.Response) resp, err = b.Caller.SyncCheck(b.Storage.Request, b.Storage.LoginInfo, b.Storage.Response)
if err != nil { if err != nil {
return err return err
} }
// 如果不是正常的状态码返回,发生了错误,直接退出 // 如果不是正常的状态码返回,发生了错误,直接退出
if !resp.Success() { if !resp.Success() {
return resp return resp
} }
// 如果Selector不为0则获取消息 // 如果Selector不为0则获取消息
if !resp.NorMal() { if !resp.NorMal() {
if err = b.getNewWechatMessage(); err != nil { if err = b.getNewWechatMessage(); err != nil {
return err return err
} }
} }
} }
return err return err
} }
// 当获取消息发生错误时, 默认的错误处理行为 // 当获取消息发生错误时, 默认的错误处理行为
func (b *Bot) stopAsyncCALL(err error) { func (b *Bot) stopAsyncCALL(err error) {
b.cancel() b.cancel()
b.err = err b.err = err
b.self = nil b.self = nil
log.Printf("exit with : %s", err.Error()) log.Printf("exit with : %s", err.Error())
} }
// 获取新的消息 // 获取新的消息
func (b *Bot) getNewWechatMessage() error { func (b *Bot) getNewWechatMessage() 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
} }
// Block 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞 // Block 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞
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")
} }
<-b.context.Done() <-b.context.Done()
return nil return nil
} }
// CrashReason 获取当前Bot崩溃的原因 // CrashReason 获取当前Bot崩溃的原因
func (b *Bot) CrashReason() error { func (b *Bot) CrashReason() error {
return b.err return b.err
} }
// MessageOnSuccess setter for Bot.MessageHandler // MessageOnSuccess setter for Bot.MessageHandler
func (b *Bot) MessageOnSuccess(h func(msg *Message)) { func (b *Bot) MessageOnSuccess(h func(msg *Message)) {
b.MessageHandler = h b.MessageHandler = h
} }
// MessageOnError setter for Bot.GetMessageErrorHandler // MessageOnError setter for Bot.GetMessageErrorHandler
func (b *Bot) MessageOnError(h func(err error)) { func (b *Bot) MessageOnError(h func(err error)) {
b.GetMessageErrorHandler = h b.GetMessageErrorHandler = h
} }
// DumpHotReloadStorage 写入HotReloadStorage // DumpHotReloadStorage 写入HotReloadStorage
func (b *Bot) DumpHotReloadStorage() error { func (b *Bot) DumpHotReloadStorage() error {
if b.HotReloadStorage == nil { if b.HotReloadStorage == nil {
return errors.New("HotReloadStorage can not be nil") return errors.New("HotReloadStorage can not be nil")
} }
cookies := b.Caller.Client.GetCookieMap() cookies := b.Caller.Client.GetCookieMap()
item := HotReloadStorageItem{ item := HotReloadStorageItem{
BaseRequest: b.Storage.Request, BaseRequest: b.Storage.Request,
Cookies: cookies, Cookies: cookies,
LoginInfo: b.Storage.LoginInfo, LoginInfo: b.Storage.LoginInfo,
WechatDomain: b.Caller.Client.Domain, WechatDomain: b.Caller.Client.Domain,
} }
data, err := json.Marshal(item) data, err := json.Marshal(item)
if err != nil { if err != nil {
return err return err
} }
if _, err = b.HotReloadStorage.Write(data); err != nil { if _, err = b.HotReloadStorage.Write(data); err != nil {
return err return err
} }
return b.HotReloadStorage.Close() return b.HotReloadStorage.Close()
} }
// OnLogin is a setter for LoginCallBack // OnLogin is a setter for LoginCallBack
func (b *Bot) OnLogin(f func(body []byte)) { func (b *Bot) OnLogin(f func(body []byte)) {
b.LoginCallBack = f b.LoginCallBack = f
} }
// OnScanned is a setter for ScanCallBack // OnScanned is a setter for ScanCallBack
func (b *Bot) OnScanned(f func(body []byte)) { func (b *Bot) OnScanned(f func(body []byte)) {
b.ScanCallBack = f b.ScanCallBack = f
} }
// OnLogout is a setter for LogoutCallBack // OnLogout is a setter for LogoutCallBack
func (b *Bot) OnLogout(f func(bot *Bot)) { func (b *Bot) OnLogout(f func(bot *Bot)) {
b.LogoutCallBack = f b.LogoutCallBack = f
} }
// NewBot Bot的构造方法需要自己传入Caller // NewBot Bot的构造方法需要自己传入Caller
func NewBot(caller *Caller) *Bot { func NewBot(caller *Caller) *Bot {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return &Bot{Caller: caller, Storage: &Storage{}, context: ctx, cancel: cancel} return &Bot{Caller: caller, Storage: &Storage{}, context: ctx, cancel: cancel}
} }
// DefaultBot 默认的Bot的构造方法, // DefaultBot 默认的Bot的构造方法,
// mode不传入默认为openwechat.Normal,详情见mode // mode不传入默认为openwechat.Normal,详情见mode
// bot := openwechat.DefaultBot(openwechat.Desktop) // bot := openwechat.DefaultBot(openwechat.Desktop)
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]
} }
caller := DefaultCaller() caller := DefaultCaller()
caller.Client.mode = m caller.Client.mode = m
bot := NewBot(caller) bot := NewBot(caller)
bot.UUIDCallback = PrintlnQrcodeUrl bot.UUIDCallback = PrintlnQrcodeUrl
return bot return bot
} }
// GetQrcodeUrl 通过uuid获取登录二维码的url // GetQrcodeUrl 通过uuid获取登录二维码的url
func GetQrcodeUrl(uuid string) string { func GetQrcodeUrl(uuid string) string {
return qrcode + uuid return qrcode + uuid
} }
// PrintlnQrcodeUrl 打印登录二维码 // PrintlnQrcodeUrl 打印登录二维码
func PrintlnQrcodeUrl(uuid string) { func PrintlnQrcodeUrl(uuid string) {
println("访问下面网址扫描二维码登录") println("访问下面网址扫描二维码登录")
println(GetQrcodeUrl(uuid)) println(GetQrcodeUrl(uuid))
} }

View File

@ -135,8 +135,8 @@ func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitRespon
} }
// SyncCheck 异步获取是否有新的消息 // SyncCheck 异步获取是否有新的消息
func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) { func (c *Caller) SyncCheck(request *BaseRequest, info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) {
resp, err := c.Client.SyncCheck(info, response) resp, err := c.Client.SyncCheck(request, info, response)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -195,14 +195,14 @@ func (c *Client) WebWxStatusNotify(request *BaseRequest, response *WebInitRespon
} }
// SyncCheck 异步检查是否有新的消息返回 // SyncCheck 异步检查是否有新的消息返回
func (c *Client) SyncCheck(info *LoginInfo, response *WebInitResponse) (*http.Response, error) { func (c *Client) SyncCheck(request *BaseRequest, info *LoginInfo, response *WebInitResponse) (*http.Response, error) {
path, _ := url.Parse(c.Domain.SyncHost() + synccheck) path, _ := url.Parse(c.Domain.SyncHost() + synccheck)
params := url.Values{} params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10)) params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("skey", info.SKey) params.Add("skey", info.SKey)
params.Add("sid", info.WxSid) params.Add("sid", info.WxSid)
params.Add("uin", strconv.Itoa(info.WxUin)) params.Add("uin", strconv.Itoa(info.WxUin))
params.Add("deviceid", GetRandomDeviceId()) params.Add("deviceid", request.DeviceID)
params.Add("_", strconv.FormatInt(time.Now().Unix(), 10)) params.Add("_", strconv.FormatInt(time.Now().Unix(), 10))
var syncKeyStringSlice = make([]string, response.SyncKey.Count) var syncKeyStringSlice = make([]string, response.SyncKey.Count)
// 将SyncKey里面的元素按照特定的格式拼接起来 // 将SyncKey里面的元素按照特定的格式拼接起来

286
items.go
View File

@ -1,8 +1,8 @@
package openwechat package openwechat
import ( import (
"errors" "errors"
"fmt" "fmt"
) )
/* /*
@ -11,232 +11,232 @@ import (
// LoginInfo 登录信息 // LoginInfo 登录信息
type LoginInfo struct { type LoginInfo struct {
Ret int `xml:"ret"` Ret int `xml:"ret"`
WxUin int `xml:"wxuin"` WxUin int `xml:"wxuin"`
IsGrayScale int `xml:"isgrayscale"` IsGrayScale int `xml:"isgrayscale"`
Message string `xml:"message"` Message string `xml:"message"`
SKey string `xml:"skey"` SKey string `xml:"skey"`
WxSid string `xml:"wxsid"` WxSid string `xml:"wxsid"`
PassTicket string `xml:"pass_ticket"` PassTicket string `xml:"pass_ticket"`
} }
// errors // errors
const ( const (
errParamError = "param error" errParamError = "param error"
errTicketError = "ticket error" errTicketError = "ticket error"
errLoginEnvError = "login env error" errLoginEnvError = "login env error"
errLoginFailedWarn = "failed login warn" errLoginFailedWarn = "failed login warn"
errLoginFailedCheck = "failed login check" errLoginFailedCheck = "failed login check"
errCookieInvalidError = "cookie invalid error" errCookieInvalidError = "cookie invalid error"
errOptTooOften = "opt too often" errOptTooOften = "opt too often"
) )
var ( var (
ErrParamError = errors.New(errParamError) ErrParamError = errors.New(errParamError)
ErrTicketError = errors.New(errTicketError) ErrTicketError = errors.New(errTicketError)
ErrLoginEnvError = errors.New(errLoginEnvError) ErrLoginEnvError = errors.New(errLoginEnvError)
ErrLoginFailedWarn = errors.New(errLoginFailedWarn) ErrLoginFailedWarn = errors.New(errLoginFailedWarn)
ErrLoginFailedCheck = errors.New(errLoginFailedCheck) ErrLoginFailedCheck = errors.New(errLoginFailedCheck)
ErrCookieInvalidError = errors.New(errCookieInvalidError) ErrCookieInvalidError = errors.New(errCookieInvalidError)
ErrOptTooOften = errors.New(errOptTooOften) ErrOptTooOften = errors.New(errOptTooOften)
ErrBaseResponseError error ErrBaseResponseError error
) )
func (l LoginInfo) Ok() bool { func (l LoginInfo) Ok() bool {
return l.Ret == 0 return l.Ret == 0
} }
func (l LoginInfo) Error() string { func (l LoginInfo) Error() string {
return l.Message return l.Message
} }
// BaseRequest 初始的请求信息 // BaseRequest 初始的请求信息
// 几乎所有的请求都要携带该参数 // 几乎所有的请求都要携带该参数
type BaseRequest struct { type BaseRequest struct {
Uin int Uin int
Sid, Skey, DeviceID string Sid, Skey, DeviceID string
} }
// BaseResponse 大部分返回对象都携带该信息 // BaseResponse 大部分返回对象都携带该信息
type BaseResponse struct { type BaseResponse struct {
Ret int Ret int
ErrMsg string 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 {
if err := getResponseErrorWithRetCode(b.Ret); err != nil { if err := getResponseErrorWithRetCode(b.Ret); err != nil {
return err.Error() return err.Error()
} }
return "" return ""
} }
func getResponseErrorWithRetCode(code int) error { func getResponseErrorWithRetCode(code int) error {
switch code { switch code {
case 0: case 0:
return nil return nil
case 1: case 1:
return ErrParamError return ErrParamError
case -14: case -14:
return ErrTicketError return ErrTicketError
case 1100: case 1100:
return ErrLoginFailedWarn return ErrLoginFailedWarn
case 1101: case 1101:
return ErrLoginFailedCheck return ErrLoginFailedCheck
case 1102: case 1102:
return ErrCookieInvalidError return ErrCookieInvalidError
case 1203: case 1203:
return ErrLoginEnvError return ErrLoginEnvError
case 1205: case 1205:
return ErrOptTooOften return ErrOptTooOften
default: default:
ErrBaseResponseError = fmt.Errorf("base response ret code %d", code) ErrBaseResponseError = fmt.Errorf("base response ret code %d", code)
return ErrBaseResponseError return ErrBaseResponseError
} }
} }
type SyncKey struct { type SyncKey struct {
Count int Count int
List []struct{ Key, Val int64 } List []struct{ Key, Val int64 }
} }
// WebInitResponse 初始化的相应信息 // WebInitResponse 初始化的相应信息
type WebInitResponse struct { type WebInitResponse struct {
Count int Count int
ClientVersion int ClientVersion int
GrayScale int GrayScale int
InviteStartCount int InviteStartCount int
MPSubscribeMsgCount int MPSubscribeMsgCount int
ClickReportInterval int ClickReportInterval int
SystemTime int64 SystemTime int64
ChatSet string ChatSet string
SKey string SKey string
BaseResponse BaseResponse BaseResponse BaseResponse
SyncKey SyncKey SyncKey SyncKey
User User User User
MPSubscribeMsgList []MPSubscribeMsg MPSubscribeMsgList []MPSubscribeMsg
ContactList []User ContactList []User
} }
// MPSubscribeMsg 公众号的订阅信息 // MPSubscribeMsg 公众号的订阅信息
type MPSubscribeMsg struct { type MPSubscribeMsg struct {
MPArticleCount int MPArticleCount int
Time int64 Time int64
UserName string UserName string
NickName string 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 = make(UserDetailItemList, len(members)) var list = make(UserDetailItemList, len(members))
for index, member := range members { for index, member := range members {
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId} item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
list[index] = item list[index] = 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 errParamError return errParamError
case "-14": case "-14":
return errTicketError return errTicketError
case "1100": case "1100":
return errLoginFailedWarn return errLoginFailedWarn
case "1101": case "1101":
return errLoginFailedCheck return errLoginFailedCheck
case "1102": case "1102":
return errCookieInvalidError return errCookieInvalidError
case "1203": case "1203":
return errLoginEnvError return errLoginEnvError
case "1205": case "1205":
return errOptTooOften return errOptTooOften
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
ContinueFlag int ContinueFlag int
DelContactCount int DelContactCount int
ModChatRoomMemberCount int ModChatRoomMemberCount int
ModContactCount int ModContactCount int
Skey string Skey string
SyncCheckKey SyncKey SyncCheckKey SyncKey
SyncKey SyncKey SyncKey SyncKey
BaseResponse BaseResponse BaseResponse BaseResponse
ModChatRoomMemberList Members ModChatRoomMemberList Members
AddMsgList []*Message AddMsgList []*Message
} }
type WebWxContactResponse struct { type WebWxContactResponse struct {
MemberCount int MemberCount int
Seq int Seq int
BaseResponse BaseResponse BaseResponse BaseResponse
MemberList []*User MemberList []*User
} }
type WebWxBatchContactResponse struct { type WebWxBatchContactResponse struct {
Count int Count int
BaseResponse BaseResponse BaseResponse BaseResponse
ContactList []*User ContactList []*User
} }
type CheckLoginResponse struct { type CheckLoginResponse struct {
Code string Code string
Raw []byte Raw []byte
} }
type MessageResponse struct { type MessageResponse struct {
BaseResponse BaseResponse BaseResponse BaseResponse
LocalID string LocalID string
MsgID string MsgID string
} }
type UploadResponse struct { type UploadResponse struct {
BaseResponse BaseResponse BaseResponse BaseResponse
MediaId string MediaId string
} }
type PushLoginResponse struct { type PushLoginResponse struct {
Ret string `json:"ret"` Ret string `json:"ret"`
Msg string `json:"msg"` Msg string `json:"msg"`
UUID string `json:"uuid"` UUID string `json:"uuid"`
} }
func (p PushLoginResponse) Ok() bool { func (p PushLoginResponse) Ok() bool {
return p.Ret == "0" && p.UUID != "" return p.Ret == "0" && p.UUID != ""
} }

View File

@ -1,26 +1,26 @@
package openwechat package openwechat
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
"net/http" "net/http"
"os" "os"
) )
// Storage 身份信息, 维持整个登陆的Session会话 // Storage 身份信息, 维持整个登陆的Session会话
type Storage struct { type Storage struct {
LoginInfo *LoginInfo LoginInfo *LoginInfo
Request *BaseRequest Request *BaseRequest
Response *WebInitResponse Response *WebInitResponse
} }
type HotReloadStorageItem struct { type HotReloadStorageItem struct {
Cookies map[string][]*http.Cookie Cookies map[string][]*http.Cookie
BaseRequest *BaseRequest BaseRequest *BaseRequest
LoginInfo *LoginInfo LoginInfo *LoginInfo
WechatDomain WechatDomain WechatDomain WechatDomain
} }
// HotReloadStorage 热登陆存储接口 // HotReloadStorage 热登陆存储接口
@ -29,53 +29,53 @@ type HotReloadStorage io.ReadWriteCloser
// JsonFileHotReloadStorage 实现HotReloadStorage接口 // JsonFileHotReloadStorage 实现HotReloadStorage接口
// 默认以json文件的形式存储 // 默认以json文件的形式存储
type JsonFileHotReloadStorage struct { type JsonFileHotReloadStorage struct {
FileName string FileName string
file *os.File file *os.File
} }
func (j *JsonFileHotReloadStorage) Read(p []byte) (n int, err error) { func (j *JsonFileHotReloadStorage) Read(p []byte) (n int, err error) {
if j.file == nil { if j.file == nil {
j.file, err = os.Open(j.FileName) j.file, err = os.Open(j.FileName)
if err != nil { if err != nil {
return 0, err return 0, err
} }
} }
return j.file.Read(p) return j.file.Read(p)
} }
func (j *JsonFileHotReloadStorage) Write(p []byte) (n int, err error) { func (j *JsonFileHotReloadStorage) Write(p []byte) (n int, err error) {
j.file, err = os.Create(j.FileName) j.file, err = os.Create(j.FileName)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return j.file.Write(p) return j.file.Write(p)
} }
func (j *JsonFileHotReloadStorage) Close() error { func (j *JsonFileHotReloadStorage) Close() error {
if j.file != nil { if j.file != nil {
return j.file.Close() return j.file.Close()
} }
return nil return nil
} }
func NewJsonFileHotReloadStorage(filename string) HotReloadStorage { func NewJsonFileHotReloadStorage(filename string) HotReloadStorage {
return &JsonFileHotReloadStorage{FileName: filename} return &JsonFileHotReloadStorage{FileName: filename}
} }
var _ HotReloadStorage = &JsonFileHotReloadStorage{} var _ HotReloadStorage = &JsonFileHotReloadStorage{}
func NewHotReloadStorageItem(storage HotReloadStorage) (*HotReloadStorageItem, error) { func NewHotReloadStorageItem(storage HotReloadStorage) (*HotReloadStorageItem, error) {
if storage == nil { if storage == nil {
return nil, errors.New("storage can't be nil") return nil, errors.New("storage can't be nil")
} }
var buffer bytes.Buffer var buffer bytes.Buffer
if _, err := buffer.ReadFrom(storage); err != nil { if _, err := buffer.ReadFrom(storage); err != nil {
return nil, err return nil, err
} }
var item HotReloadStorageItem var item HotReloadStorageItem
if err := json.NewDecoder(&buffer).Decode(&item); err != nil { if err := json.NewDecoder(&buffer).Decode(&item); err != nil {
return nil, err return nil, err
} }
return &item, nil return &item, nil
} }