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

View File

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

View File

@ -195,14 +195,14 @@ func (c *Client) WebWxStatusNotify(request *BaseRequest, response *WebInitRespon
}
// 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)
params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("skey", info.SKey)
params.Add("sid", info.WxSid)
params.Add("uin", strconv.Itoa(info.WxUin))
params.Add("deviceid", GetRandomDeviceId())
params.Add("deviceid", request.DeviceID)
params.Add("_", strconv.FormatInt(time.Now().Unix(), 10))
var syncKeyStringSlice = make([]string, response.SyncKey.Count)
// 将SyncKey里面的元素按照特定的格式拼接起来

286
items.go
View File

@ -1,8 +1,8 @@
package openwechat
import (
"errors"
"fmt"
"errors"
"fmt"
)
/*
@ -11,232 +11,232 @@ import (
// LoginInfo 登录信息
type LoginInfo struct {
Ret int `xml:"ret"`
WxUin int `xml:"wxuin"`
IsGrayScale int `xml:"isgrayscale"`
Message string `xml:"message"`
SKey string `xml:"skey"`
WxSid string `xml:"wxsid"`
PassTicket string `xml:"pass_ticket"`
Ret int `xml:"ret"`
WxUin int `xml:"wxuin"`
IsGrayScale int `xml:"isgrayscale"`
Message string `xml:"message"`
SKey string `xml:"skey"`
WxSid string `xml:"wxsid"`
PassTicket string `xml:"pass_ticket"`
}
// errors
const (
errParamError = "param error"
errTicketError = "ticket error"
errLoginEnvError = "login env error"
errLoginFailedWarn = "failed login warn"
errLoginFailedCheck = "failed login check"
errCookieInvalidError = "cookie invalid error"
errOptTooOften = "opt too often"
errParamError = "param error"
errTicketError = "ticket error"
errLoginEnvError = "login env error"
errLoginFailedWarn = "failed login warn"
errLoginFailedCheck = "failed login check"
errCookieInvalidError = "cookie invalid error"
errOptTooOften = "opt too often"
)
var (
ErrParamError = errors.New(errParamError)
ErrTicketError = errors.New(errTicketError)
ErrLoginEnvError = errors.New(errLoginEnvError)
ErrLoginFailedWarn = errors.New(errLoginFailedWarn)
ErrLoginFailedCheck = errors.New(errLoginFailedCheck)
ErrCookieInvalidError = errors.New(errCookieInvalidError)
ErrOptTooOften = errors.New(errOptTooOften)
ErrBaseResponseError error
ErrParamError = errors.New(errParamError)
ErrTicketError = errors.New(errTicketError)
ErrLoginEnvError = errors.New(errLoginEnvError)
ErrLoginFailedWarn = errors.New(errLoginFailedWarn)
ErrLoginFailedCheck = errors.New(errLoginFailedCheck)
ErrCookieInvalidError = errors.New(errCookieInvalidError)
ErrOptTooOften = errors.New(errOptTooOften)
ErrBaseResponseError error
)
func (l LoginInfo) Ok() bool {
return l.Ret == 0
return l.Ret == 0
}
func (l LoginInfo) Error() string {
return l.Message
return l.Message
}
// BaseRequest 初始的请求信息
// 几乎所有的请求都要携带该参数
type BaseRequest struct {
Uin int
Sid, Skey, DeviceID string
Uin int
Sid, Skey, DeviceID string
}
// BaseResponse 大部分返回对象都携带该信息
type BaseResponse struct {
Ret int
ErrMsg string
Ret int
ErrMsg string
}
func (b BaseResponse) Ok() bool {
return b.Ret == 0
return b.Ret == 0
}
func (b BaseResponse) Error() string {
if err := getResponseErrorWithRetCode(b.Ret); err != nil {
return err.Error()
}
return ""
if err := getResponseErrorWithRetCode(b.Ret); err != nil {
return err.Error()
}
return ""
}
func getResponseErrorWithRetCode(code int) error {
switch code {
case 0:
return nil
case 1:
return ErrParamError
case -14:
return ErrTicketError
case 1100:
return ErrLoginFailedWarn
case 1101:
return ErrLoginFailedCheck
case 1102:
return ErrCookieInvalidError
case 1203:
return ErrLoginEnvError
case 1205:
return ErrOptTooOften
default:
ErrBaseResponseError = fmt.Errorf("base response ret code %d", code)
return ErrBaseResponseError
}
switch code {
case 0:
return nil
case 1:
return ErrParamError
case -14:
return ErrTicketError
case 1100:
return ErrLoginFailedWarn
case 1101:
return ErrLoginFailedCheck
case 1102:
return ErrCookieInvalidError
case 1203:
return ErrLoginEnvError
case 1205:
return ErrOptTooOften
default:
ErrBaseResponseError = fmt.Errorf("base response ret code %d", code)
return ErrBaseResponseError
}
}
type SyncKey struct {
Count int
List []struct{ Key, Val int64 }
Count int
List []struct{ Key, Val int64 }
}
// WebInitResponse 初始化的相应信息
type WebInitResponse struct {
Count int
ClientVersion int
GrayScale int
InviteStartCount int
MPSubscribeMsgCount int
ClickReportInterval int
SystemTime int64
ChatSet string
SKey string
BaseResponse BaseResponse
SyncKey SyncKey
User User
MPSubscribeMsgList []MPSubscribeMsg
ContactList []User
Count int
ClientVersion int
GrayScale int
InviteStartCount int
MPSubscribeMsgCount int
ClickReportInterval int
SystemTime int64
ChatSet string
SKey string
BaseResponse BaseResponse
SyncKey SyncKey
User User
MPSubscribeMsgList []MPSubscribeMsg
ContactList []User
}
// MPSubscribeMsg 公众号的订阅信息
type MPSubscribeMsg struct {
MPArticleCount int
Time int64
UserName string
NickName string
MPArticleList []struct {
Title string
Cover string
Digest string
Url string
}
MPArticleCount int
Time int64
UserName string
NickName string
MPArticleList []struct {
Title string
Cover string
Digest string
Url string
}
}
type UserDetailItem struct {
UserName string
EncryChatRoomId string
UserName string
EncryChatRoomId string
}
type UserDetailItemList []UserDetailItem
func NewUserDetailItemList(members Members) UserDetailItemList {
var list = make(UserDetailItemList, len(members))
for index, member := range members {
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
list[index] = item
}
return list
var list = make(UserDetailItemList, len(members))
for index, member := range members {
item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId}
list[index] = item
}
return list
}
type SyncCheckResponse struct {
RetCode string
Selector string
RetCode string
Selector string
}
func (s *SyncCheckResponse) Success() bool {
return s.RetCode == "0"
return s.RetCode == "0"
}
func (s *SyncCheckResponse) NorMal() bool {
return s.Success() && s.Selector == "0"
return s.Success() && s.Selector == "0"
}
// 实现error接口
func (s *SyncCheckResponse) Error() string {
switch s.RetCode {
case "0":
return ""
case "1":
return errParamError
case "-14":
return errTicketError
case "1100":
return errLoginFailedWarn
case "1101":
return errLoginFailedCheck
case "1102":
return errCookieInvalidError
case "1203":
return errLoginEnvError
case "1205":
return errOptTooOften
default:
return fmt.Sprintf("sync check response error code %s", s.RetCode)
}
switch s.RetCode {
case "0":
return ""
case "1":
return errParamError
case "-14":
return errTicketError
case "1100":
return errLoginFailedWarn
case "1101":
return errLoginFailedCheck
case "1102":
return errCookieInvalidError
case "1203":
return errLoginEnvError
case "1205":
return errOptTooOften
default:
return fmt.Sprintf("sync check response error code %s", s.RetCode)
}
}
type WebWxSyncResponse struct {
AddMsgCount int
ContinueFlag int
DelContactCount int
ModChatRoomMemberCount int
ModContactCount int
Skey string
SyncCheckKey SyncKey
SyncKey SyncKey
BaseResponse BaseResponse
ModChatRoomMemberList Members
AddMsgList []*Message
AddMsgCount int
ContinueFlag int
DelContactCount int
ModChatRoomMemberCount int
ModContactCount int
Skey string
SyncCheckKey SyncKey
SyncKey SyncKey
BaseResponse BaseResponse
ModChatRoomMemberList Members
AddMsgList []*Message
}
type WebWxContactResponse struct {
MemberCount int
Seq int
BaseResponse BaseResponse
MemberList []*User
MemberCount int
Seq int
BaseResponse BaseResponse
MemberList []*User
}
type WebWxBatchContactResponse struct {
Count int
BaseResponse BaseResponse
ContactList []*User
Count int
BaseResponse BaseResponse
ContactList []*User
}
type CheckLoginResponse struct {
Code string
Raw []byte
Code string
Raw []byte
}
type MessageResponse struct {
BaseResponse BaseResponse
LocalID string
MsgID string
BaseResponse BaseResponse
LocalID string
MsgID string
}
type UploadResponse struct {
BaseResponse BaseResponse
MediaId string
BaseResponse BaseResponse
MediaId string
}
type PushLoginResponse struct {
Ret string `json:"ret"`
Msg string `json:"msg"`
UUID string `json:"uuid"`
Ret string `json:"ret"`
Msg string `json:"msg"`
UUID string `json:"uuid"`
}
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
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"os"
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"os"
)
// Storage 身份信息, 维持整个登陆的Session会话
type Storage struct {
LoginInfo *LoginInfo
Request *BaseRequest
Response *WebInitResponse
LoginInfo *LoginInfo
Request *BaseRequest
Response *WebInitResponse
}
type HotReloadStorageItem struct {
Cookies map[string][]*http.Cookie
BaseRequest *BaseRequest
LoginInfo *LoginInfo
WechatDomain WechatDomain
Cookies map[string][]*http.Cookie
BaseRequest *BaseRequest
LoginInfo *LoginInfo
WechatDomain WechatDomain
}
// HotReloadStorage 热登陆存储接口
@ -29,53 +29,53 @@ type HotReloadStorage io.ReadWriteCloser
// JsonFileHotReloadStorage 实现HotReloadStorage接口
// 默认以json文件的形式存储
type JsonFileHotReloadStorage struct {
FileName string
file *os.File
FileName string
file *os.File
}
func (j *JsonFileHotReloadStorage) Read(p []byte) (n int, err error) {
if j.file == nil {
j.file, err = os.Open(j.FileName)
if err != nil {
return 0, err
}
}
return j.file.Read(p)
if j.file == nil {
j.file, err = os.Open(j.FileName)
if err != nil {
return 0, err
}
}
return j.file.Read(p)
}
func (j *JsonFileHotReloadStorage) Write(p []byte) (n int, err error) {
j.file, err = os.Create(j.FileName)
if err != nil {
return 0, err
}
return j.file.Write(p)
j.file, err = os.Create(j.FileName)
if err != nil {
return 0, err
}
return j.file.Write(p)
}
func (j *JsonFileHotReloadStorage) Close() error {
if j.file != nil {
return j.file.Close()
}
return nil
if j.file != nil {
return j.file.Close()
}
return nil
}
func NewJsonFileHotReloadStorage(filename string) HotReloadStorage {
return &JsonFileHotReloadStorage{FileName: filename}
return &JsonFileHotReloadStorage{FileName: filename}
}
var _ HotReloadStorage = &JsonFileHotReloadStorage{}
func NewHotReloadStorageItem(storage HotReloadStorage) (*HotReloadStorageItem, error) {
if storage == nil {
return nil, errors.New("storage can't be nil")
}
var buffer bytes.Buffer
if _, err := buffer.ReadFrom(storage); err != nil {
return nil, err
}
var item HotReloadStorageItem
if storage == nil {
return nil, errors.New("storage can't be nil")
}
var buffer bytes.Buffer
if _, err := buffer.ReadFrom(storage); err != nil {
return nil, err
}
var item HotReloadStorageItem
if err := json.NewDecoder(&buffer).Decode(&item); err != nil {
return nil, err
}
return &item, nil
if err := json.NewDecoder(&buffer).Decode(&item); err != nil {
return nil, err
}
return &item, nil
}