增加免扫码登录接口

This commit is contained in:
eatMoreApple 2021-08-04 15:58:30 +08:00
parent 3e14a0d5ed
commit 07b32939c9
4 changed files with 967 additions and 932 deletions

539
caller.go
View File

@ -1,385 +1,400 @@
package openwechat package openwechat
import ( import (
"bytes" "bytes"
"errors" "encoding/json"
"net/http" "errors"
"net/url" "net/http"
"os" "net/url"
"os"
) )
// Caller 调用请求和解析请求 // Caller 调用请求和解析请求
// 上层模块可以直接获取封装后的请求结果 // 上层模块可以直接获取封装后的请求结果
type Caller struct { type Caller struct {
Client *Client Client *Client
path *url.URL path *url.URL
} }
// NewCaller Constructor for Caller // NewCaller Constructor for Caller
func NewCaller(client *Client) *Caller { func NewCaller(client *Client) *Caller {
return &Caller{Client: client} return &Caller{Client: client}
} }
// DefaultCaller Default Constructor for Caller // DefaultCaller Default Constructor for Caller
func DefaultCaller() *Caller { func DefaultCaller() *Caller {
return NewCaller(DefaultClient()) return NewCaller(DefaultClient())
} }
// GetLoginUUID 获取登录的uuid // GetLoginUUID 获取登录的uuid
func (c *Caller) GetLoginUUID() (string, error) { func (c *Caller) GetLoginUUID() (string, error) {
resp, err := c.Client.GetLoginUUID() resp, err := c.Client.GetLoginUUID()
if err != nil { if err != nil {
return "", err return "", err
} }
defer resp.Body.Close() defer resp.Body.Close()
var buffer bytes.Buffer var buffer bytes.Buffer
if _, err := buffer.ReadFrom(resp.Body); err != nil { if _, err := buffer.ReadFrom(resp.Body); err != nil {
return "", err return "", err
} }
// 正则匹配uuid字符串 // 正则匹配uuid字符串
results := uuidRegexp.FindSubmatch(buffer.Bytes()) results := uuidRegexp.FindSubmatch(buffer.Bytes())
if len(results) != 2 { if len(results) != 2 {
// 如果没有匹配到,可能微信的接口做了修改或者当前机器的ip被加入了黑名单 // 如果没有匹配到,可能微信的接口做了修改或者当前机器的ip被加入了黑名单
return "", errors.New("uuid does not match") return "", errors.New("uuid does not match")
} }
return string(results[1]), nil return string(results[1]), nil
} }
// CheckLogin 检查是否登录成功 // CheckLogin 检查是否登录成功
func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) { func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
resp, err := c.Client.CheckLogin(uuid) resp, err := c.Client.CheckLogin(uuid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var buffer bytes.Buffer var buffer bytes.Buffer
if _, err := buffer.ReadFrom(resp.Body); err != nil { if _, err := buffer.ReadFrom(resp.Body); err != nil {
return nil, err return nil, err
} }
// 正则匹配检测的code // 正则匹配检测的code
// 具体code参考global.go // 具体code参考global.go
results := statusCodeRegexp.FindSubmatch(buffer.Bytes()) results := statusCodeRegexp.FindSubmatch(buffer.Bytes())
if len(results) != 2 { if len(results) != 2 {
return nil, errors.New("error status code match") return nil, errors.New("error status code match")
} }
code := string(results[1]) code := string(results[1])
return &CheckLoginResponse{Code: code, Raw: buffer.Bytes()}, nil return &CheckLoginResponse{Code: code, Raw: buffer.Bytes()}, nil
} }
// GetLoginInfo 获取登录信息 // GetLoginInfo 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) { func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
// 从响应体里面获取需要跳转的url // 从响应体里面获取需要跳转的url
results := redirectUriRegexp.FindSubmatch(body) results := redirectUriRegexp.FindSubmatch(body)
if len(results) != 2 { if len(results) != 2 {
return nil, errors.New("redirect url does not match") return nil, errors.New("redirect url does not match")
} }
path, err := url.Parse(string(results[1])) path, err := url.Parse(string(results[1]))
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.Client.Domain = WechatDomain(path.Host) c.Client.Domain = WechatDomain(path.Host)
resp, err := c.Client.GetLoginInfo(path.String()) resp, err := c.Client.GetLoginInfo(path.String())
if err != nil { if err != nil {
uErr, ok := err.(*url.Error) uErr, ok := err.(*url.Error)
if ok && (uErr.Err.Error() == ErrMissLocationHeader.Error()) { if ok && (uErr.Err.Error() == ErrMissLocationHeader.Error()) {
return nil, ErrLoginForbiddenError return nil, ErrLoginForbiddenError
} }
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var loginInfo LoginInfo var loginInfo LoginInfo
// xml结构体序列化储存 // xml结构体序列化储存
if err := scanXml(resp, &loginInfo); err != nil { if err := scanXml(resp, &loginInfo); err != nil {
return nil, err return nil, err
} }
if !loginInfo.Ok() { if !loginInfo.Ok() {
return nil, loginInfo return nil, loginInfo
} }
return &loginInfo, nil return &loginInfo, nil
} }
// WebInit 获取初始化信息 // WebInit 获取初始化信息
func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) { func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) {
resp, err := c.Client.WebInit(request) resp, err := c.Client.WebInit(request)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var webInitResponse WebInitResponse var webInitResponse WebInitResponse
defer resp.Body.Close() defer resp.Body.Close()
if err := scanJson(resp, &webInitResponse); err != nil { if err := scanJson(resp, &webInitResponse); err != nil {
return nil, err return nil, err
} }
return &webInitResponse, nil return &webInitResponse, nil
} }
// WebWxStatusNotify 通知手机已登录 // WebWxStatusNotify 通知手机已登录
func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error { func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error {
resp, err := c.Client.WebWxStatusNotify(request, response, info) resp, err := c.Client.WebWxStatusNotify(request, response, info)
if err != nil { if err != nil {
return err return err
} }
var item struct{ BaseResponse BaseResponse } var item struct{ BaseResponse BaseResponse }
defer resp.Body.Close() defer resp.Body.Close()
if err := scanJson(resp, &item); err != nil { if err := scanJson(resp, &item); err != nil {
return err return err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return item.BaseResponse
} }
return nil return nil
} }
// SyncCheck 异步获取是否有新的消息 // SyncCheck 异步获取是否有新的消息
func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) { func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) {
resp, err := c.Client.SyncCheck(info, response) resp, err := c.Client.SyncCheck(info, response)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var buffer bytes.Buffer var buffer bytes.Buffer
if _, err := buffer.ReadFrom(resp.Body); err != nil { if _, err := buffer.ReadFrom(resp.Body); err != nil {
return nil, err return nil, err
} }
results := syncCheckRegexp.FindSubmatch(buffer.Bytes()) results := syncCheckRegexp.FindSubmatch(buffer.Bytes())
if len(results) != 3 { if len(results) != 3 {
return nil, errors.New("parse sync key failed") return nil, errors.New("parse sync key failed")
} }
retCode, selector := string(results[1]), string(results[2]) retCode, selector := string(results[1]), string(results[2])
syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector} syncCheckResponse := &SyncCheckResponse{RetCode: retCode, Selector: selector}
return syncCheckResponse, nil return syncCheckResponse, nil
} }
// WebWxGetContact 获取所有的联系人 // WebWxGetContact 获取所有的联系人
func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) { func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) {
resp, err := c.Client.WebWxGetContact(info) resp, err := c.Client.WebWxGetContact(info)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var item WebWxContactResponse var item WebWxContactResponse
if err := scanJson(resp, &item); err != nil { if err := scanJson(resp, &item); err != nil {
return nil, err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return nil, item.BaseResponse return nil, item.BaseResponse
} }
return item.MemberList, nil return item.MemberList, nil
} }
// WebWxBatchGetContact 获取联系人的详情 // WebWxBatchGetContact 获取联系人的详情
// 注: Members参数的长度不要大于50 // 注: Members参数的长度不要大于50
func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) { func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) {
resp, err := c.Client.WebWxBatchGetContact(members, request) resp, err := c.Client.WebWxBatchGetContact(members, request)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var item WebWxBatchContactResponse var item WebWxBatchContactResponse
if err := scanJson(resp, &item); err != nil { if err := scanJson(resp, &item); err != nil {
return nil, err return nil, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return nil, item.BaseResponse return nil, item.BaseResponse
} }
return item.ContactList, nil return item.ContactList, nil
} }
// WebWxSync 获取新的消息接口 // WebWxSync 获取新的消息接口
func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*WebWxSyncResponse, error) { func (c *Caller) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*WebWxSyncResponse, error) {
resp, err := c.Client.WebWxSync(request, response, info) resp, err := c.Client.WebWxSync(request, response, info)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var webWxSyncResponse WebWxSyncResponse var webWxSyncResponse WebWxSyncResponse
if err := scanJson(resp, &webWxSyncResponse); err != nil { if err := scanJson(resp, &webWxSyncResponse); err != nil {
return nil, err return nil, err
} }
return &webWxSyncResponse, nil return &webWxSyncResponse, nil
} }
// WebWxSendMsg 发送消息接口 // WebWxSendMsg 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) (*SentMessage, error) { func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) (*SentMessage, error) {
resp, err := c.Client.WebWxSendMsg(msg, info, request) resp, err := c.Client.WebWxSendMsg(msg, info, request)
return getSuccessSentMessage(msg, resp, err) return getSuccessSentMessage(msg, resp, err)
} }
// WebWxOplog 修改用户备注接口 // WebWxOplog 修改用户备注接口
func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error { func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error {
resp, err := c.Client.WebWxOplog(request, remarkName, toUserName) resp, err := c.Client.WebWxOplog(request, remarkName, toUserName)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
func (c *Caller) UploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*UploadResponse, error) { func (c *Caller) UploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*UploadResponse, error) {
// 首先尝试上传图片 // 首先尝试上传图片
resp, err := c.Client.WebWxUploadMediaByChunk(file, request, info, fromUserName, toUserName) resp, err := c.Client.WebWxUploadMediaByChunk(file, request, info, fromUserName, toUserName)
// 无错误上传成功之后获取请求结果,判断结果是否正常 // 无错误上传成功之后获取请求结果,判断结果是否正常
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var item UploadResponse var item UploadResponse
if err := scanJson(resp, &item); err != nil { if err := scanJson(resp, &item); err != nil {
return &item, err return &item, err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return &item, item.BaseResponse return &item, item.BaseResponse
} }
if len(item.MediaId) == 0 { if len(item.MediaId) == 0 {
return &item, errors.New("upload failed") return &item, errors.New("upload failed")
} }
return &item, nil return &item, nil
} }
// WebWxSendImageMsg 发送图片消息接口 // WebWxSendImageMsg 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) { func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
// 首先尝试上传图片 // 首先尝试上传图片
resp, err := c.UploadMedia(file, request, info, fromUserName, toUserName) resp, err := c.UploadMedia(file, request, info, fromUserName, toUserName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 构造新的图片类型的信息 // 构造新的图片类型的信息
msg := NewMediaSendMessage(MsgTypeImage, fromUserName, toUserName, resp.MediaId) msg := NewMediaSendMessage(MsgTypeImage, fromUserName, toUserName, resp.MediaId)
// 发送图片信息 // 发送图片信息
resp1, err := c.Client.WebWxSendMsgImg(msg, request, info) resp1, err := c.Client.WebWxSendMsgImg(msg, request, info)
return getSuccessSentMessage(msg, resp1, err) return getSuccessSentMessage(msg, resp1, err)
} }
func (c *Caller) WebWxSendFile(file *os.File, req *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) { func (c *Caller) WebWxSendFile(file *os.File, req *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) {
resp, err := c.UploadMedia(file, req, info, fromUserName, toUserName) resp, err := c.UploadMedia(file, req, info, fromUserName, toUserName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 构造新的文件类型的信息 // 构造新的文件类型的信息
stat, _ := file.Stat() stat, _ := file.Stat()
appMsg := NewFileAppMessage(stat, resp.MediaId) appMsg := NewFileAppMessage(stat, resp.MediaId)
content, err := appMsg.XmlByte() content, err := appMsg.XmlByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
msg := NewSendMessage(AppMessage, string(content), fromUserName, toUserName, "") msg := NewSendMessage(AppMessage, string(content), fromUserName, toUserName, "")
return c.WebWxSendAppMsg(msg, req) return c.WebWxSendAppMsg(msg, req)
} }
// WebWxSendAppMsg 发送媒体消息 // WebWxSendAppMsg 发送媒体消息
func (c *Caller) WebWxSendAppMsg(msg *SendMessage, req *BaseRequest) (*SentMessage, error) { func (c *Caller) WebWxSendAppMsg(msg *SendMessage, req *BaseRequest) (*SentMessage, error) {
resp, err := c.Client.WebWxSendAppMsg(msg, req) resp, err := c.Client.WebWxSendAppMsg(msg, req)
return getSuccessSentMessage(msg, resp, err) return getSuccessSentMessage(msg, resp, err)
} }
// Logout 用户退出 // Logout 用户退出
func (c *Caller) Logout(info *LoginInfo) error { func (c *Caller) Logout(info *LoginInfo) error {
resp, err := c.Client.Logout(info) resp, err := c.Client.Logout(info)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// AddFriendIntoChatRoom 拉好友入群 // AddFriendIntoChatRoom 拉好友入群
func (c *Caller) AddFriendIntoChatRoom(req *BaseRequest, info *LoginInfo, group *Group, friends ...*Friend) error { func (c *Caller) AddFriendIntoChatRoom(req *BaseRequest, info *LoginInfo, group *Group, friends ...*Friend) error {
if len(friends) == 0 { if len(friends) == 0 {
return errors.New("no friends found") return errors.New("no friends found")
} }
resp, err := c.Client.AddMemberIntoChatRoom(req, info, group, friends...) resp, err := c.Client.AddMemberIntoChatRoom(req, info, group, friends...)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// RemoveFriendFromChatRoom 从群聊中移除用户 // RemoveFriendFromChatRoom 从群聊中移除用户
func (c *Caller) RemoveFriendFromChatRoom(req *BaseRequest, info *LoginInfo, group *Group, users ...*User) error { func (c *Caller) RemoveFriendFromChatRoom(req *BaseRequest, info *LoginInfo, group *Group, users ...*User) error {
if len(users) == 0 { if len(users) == 0 {
return errors.New("no users found") return errors.New("no users found")
} }
resp, err := c.Client.RemoveMemberFromChatRoom(req, info, group, users...) resp, err := c.Client.RemoveMemberFromChatRoom(req, info, group, users...)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// WebWxVerifyUser 同意加好友请求 // WebWxVerifyUser 同意加好友请求
func (c *Caller) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyContent string) error { func (c *Caller) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyContent string) error {
resp, err := c.Client.WebWxVerifyUser(storage, info, verifyContent) resp, err := c.Client.WebWxVerifyUser(storage, info, verifyContent)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// WebWxRevokeMsg 撤回消息操作 // WebWxRevokeMsg 撤回消息操作
func (c *Caller) WebWxRevokeMsg(msg *SentMessage, request *BaseRequest) error { func (c *Caller) WebWxRevokeMsg(msg *SentMessage, request *BaseRequest) error {
resp, err := c.Client.WebWxRevokeMsg(msg, request) resp, err := c.Client.WebWxRevokeMsg(msg, request)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// WebWxStatusAsRead 将消息设置为已读 // WebWxStatusAsRead 将消息设置为已读
func (c *Caller) WebWxStatusAsRead(request *BaseRequest, info *LoginInfo, msg *Message) error { func (c *Caller) WebWxStatusAsRead(request *BaseRequest, info *LoginInfo, msg *Message) error {
resp, err := c.Client.WebWxStatusAsRead(request, info, msg) resp, err := c.Client.WebWxStatusAsRead(request, info, msg)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
} }
// WebWxRelationPin 将联系人是否置顶 // WebWxRelationPin 将联系人是否置顶
func (c *Caller) WebWxRelationPin(request *BaseRequest, user *User, op uint8) error { func (c *Caller) WebWxRelationPin(request *BaseRequest, user *User, op uint8) error {
resp, err := c.Client.WebWxRelationPin(request, op, user) resp, err := c.Client.WebWxRelationPin(request, op, user)
if err != nil { if err != nil {
return err return err
} }
return parseBaseResponseError(resp) return parseBaseResponseError(resp)
}
// WebWxPushLogin 免扫码登陆接口
func (c *Caller) WebWxPushLogin(uin int) (*PushLoginResponse, error) {
resp, err := c.Client.WebWxPushLogin(uin)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var item PushLoginResponse
if err := json.NewDecoder(resp.Body).Decode(&item); err != nil {
return nil, err
}
return &item, nil
} }
// 处理响应返回的结果是否正常 // 处理响应返回的结果是否正常
func parseBaseResponseError(resp *http.Response) error { func parseBaseResponseError(resp *http.Response) error {
defer resp.Body.Close() defer resp.Body.Close()
var item struct{ BaseResponse BaseResponse } var item struct{ BaseResponse BaseResponse }
if err := scanJson(resp, &item); err != nil { if err := scanJson(resp, &item); err != nil {
return err return err
} }
if !item.BaseResponse.Ok() { if !item.BaseResponse.Ok() {
return item.BaseResponse return item.BaseResponse
} }
return nil return nil
} }
func parseMessageResponseError(resp *http.Response, msg *SentMessage) error { func parseMessageResponseError(resp *http.Response, msg *SentMessage) error {
defer resp.Body.Close() defer resp.Body.Close()
var messageResp MessageResponse var messageResp MessageResponse
if err := scanJson(resp, &messageResp); err != nil { if err := scanJson(resp, &messageResp); err != nil {
return err return err
} }
if !messageResp.BaseResponse.Ok() { if !messageResp.BaseResponse.Ok() {
return messageResp.BaseResponse return messageResp.BaseResponse
} }
// 发送成功之后将msgId赋值给SendMessage // 发送成功之后将msgId赋值给SendMessage
msg.MsgId = messageResp.MsgID msg.MsgId = messageResp.MsgID
return nil return nil
} }
func getSuccessSentMessage(msg *SendMessage, resp *http.Response, err error) (*SentMessage, error) { func getSuccessSentMessage(msg *SendMessage, resp *http.Response, err error) (*SentMessage, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sendSuccessMsg := &SentMessage{SendMessage: msg} sendSuccessMsg := &SentMessage{SendMessage: msg}
err = parseMessageResponseError(resp, sendSuccessMsg) err = parseMessageResponseError(resp, sendSuccessMsg)
return sendSuccessMsg, err return sendSuccessMsg, err
} }

1013
client.go

File diff suppressed because it is too large Load Diff

288
items.go
View File

@ -1,8 +1,8 @@
package openwechat package openwechat
import ( import (
"errors" "errors"
"fmt" "fmt"
) )
/* /*
@ -11,222 +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 {
Ret string `json:"ret"`
Msg string `json:"msg"`
UUID string `json:"uuid"`
}
func (p PushLoginResponse) Ok() bool {
return p.Ret == "0" && p.UUID != ""
} }

59
url.go
View File

@ -5,48 +5,49 @@ type mode string
// 向外暴露2种模式 // 向外暴露2种模式
const ( const (
Normal mode = "normal" Normal mode = "normal"
Desktop mode = "desktop" // 突破网页版登录限制 Desktop mode = "desktop" // 突破网页版登录限制
) )
const ( const (
webwxinit = "/cgi-bin/mmwebwx-bin/webwxinit" webwxinit = "/cgi-bin/mmwebwx-bin/webwxinit"
webwxstatusnotify = "/cgi-bin/mmwebwx-bin/webwxstatusnotify" webwxstatusnotify = "/cgi-bin/mmwebwx-bin/webwxstatusnotify"
webwxsync = "/cgi-bin/mmwebwx-bin/webwxsync" webwxsync = "/cgi-bin/mmwebwx-bin/webwxsync"
webwxsendmsg = "/cgi-bin/mmwebwx-bin/webwxsendmsg" webwxsendmsg = "/cgi-bin/mmwebwx-bin/webwxsendmsg"
webwxgetcontact = "/cgi-bin/mmwebwx-bin/webwxgetcontact" webwxgetcontact = "/cgi-bin/mmwebwx-bin/webwxgetcontact"
webwxsendmsgimg = "/cgi-bin/mmwebwx-bin/webwxsendmsgimg" webwxsendmsgimg = "/cgi-bin/mmwebwx-bin/webwxsendmsgimg"
webwxsendappmsg = "/cgi-bin/mmwebwx-bin/webwxsendappmsg" webwxsendappmsg = "/cgi-bin/mmwebwx-bin/webwxsendappmsg"
webwxbatchgetcontact = "/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" webwxbatchgetcontact = "/cgi-bin/mmwebwx-bin/webwxbatchgetcontact"
webwxoplog = "/cgi-bin/mmwebwx-bin/webwxoplog" webwxoplog = "/cgi-bin/mmwebwx-bin/webwxoplog"
webwxverifyuser = "/cgi-bin/mmwebwx-bin/webwxverifyuser" webwxverifyuser = "/cgi-bin/mmwebwx-bin/webwxverifyuser"
synccheck = "/cgi-bin/mmwebwx-bin/synccheck" synccheck = "/cgi-bin/mmwebwx-bin/synccheck"
webwxuploadmedia = "/cgi-bin/mmwebwx-bin/webwxuploadmedia" webwxuploadmedia = "/cgi-bin/mmwebwx-bin/webwxuploadmedia"
webwxgetmsgimg = "/cgi-bin/mmwebwx-bin/webwxgetmsgimg" webwxgetmsgimg = "/cgi-bin/mmwebwx-bin/webwxgetmsgimg"
webwxgetvoice = "/cgi-bin/mmwebwx-bin/webwxgetvoice" webwxgetvoice = "/cgi-bin/mmwebwx-bin/webwxgetvoice"
webwxgetvideo = "/cgi-bin/mmwebwx-bin/webwxgetvideo" webwxgetvideo = "/cgi-bin/mmwebwx-bin/webwxgetvideo"
webwxlogout = "/cgi-bin/mmwebwx-bin/webwxlogout" webwxlogout = "/cgi-bin/mmwebwx-bin/webwxlogout"
webwxgetmedia = "/cgi-bin/mmwebwx-bin/webwxgetmedia" webwxgetmedia = "/cgi-bin/mmwebwx-bin/webwxgetmedia"
webwxupdatechatroom = "/cgi-bin/mmwebwx-bin/webwxupdatechatroom" webwxupdatechatroom = "/cgi-bin/mmwebwx-bin/webwxupdatechatroom"
webwxrevokemsg = "/cgi-bin/mmwebwx-bin/webwxrevokemsg" webwxrevokemsg = "/cgi-bin/mmwebwx-bin/webwxrevokemsg"
webwxcheckupload = "/cgi-bin/mmwebwx-bin/webwxcheckupload" webwxcheckupload = "/cgi-bin/mmwebwx-bin/webwxcheckupload"
webwxpushloginurl = "/cgi-bin/mmwebwx-bin/webwxpushloginurl"
webwxnewloginpage = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage" webwxnewloginpage = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage"
jslogin = "https://login.wx.qq.com/jslogin" jslogin = "https://login.wx.qq.com/jslogin"
login = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login" login = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login"
qrcode = "https://login.weixin.qq.com/qrcode/" qrcode = "https://login.weixin.qq.com/qrcode/"
) )
type WechatDomain string type WechatDomain string
func (w WechatDomain) BaseHost() string { func (w WechatDomain) BaseHost() string {
return "https://" + string(w) return "https://" + string(w)
} }
func (w WechatDomain) FileHost() string { func (w WechatDomain) FileHost() string {
return "https://file." + string(w) return "https://file." + string(w)
} }
func (w WechatDomain) SyncHost() string { func (w WechatDomain) SyncHost() string {
return "https://webpush." + string(w) return "https://webpush." + string(w)
} }