删除storage接口的依赖

This commit is contained in:
eatMoreApple 2021-04-12 11:59:52 +08:00
parent 1c885a9182
commit f80cb2ebd2
7 changed files with 748 additions and 792 deletions

321
bot.go
View File

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

View File

@ -19,6 +19,7 @@ func TestDefaultBot(t *testing.T) {
return
}
self, _ := bot.GetCurrentUser()
self.SaveAvatar("2.png")
fileHelper, _ := self.FileHelper()
fileHelper.SendText("6666")
group, _ := self.Groups()

View File

@ -84,7 +84,7 @@ func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
}
// 获取初始化信息
func (c *Caller) WebInit(request BaseRequest) (*WebInitResponse, error) {
func (c *Caller) WebInit(request *BaseRequest) (*WebInitResponse, error) {
resp := NewReturnResponse(c.Client.WebInit(request))
if resp.Err() != nil {
return nil, resp.Err()
@ -98,7 +98,7 @@ func (c *Caller) WebInit(request BaseRequest) (*WebInitResponse, error) {
}
// 通知手机已登录
func (c *Caller) WebWxStatusNotify(request BaseRequest, response WebInitResponse, info LoginInfo) error {
func (c *Caller) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) error {
resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info))
if resp.Err() != nil {
return resp.Err()
@ -115,7 +115,7 @@ func (c *Caller) WebWxStatusNotify(request BaseRequest, response WebInitResponse
}
// 异步获取是否有新的消息
func (c *Caller) SyncCheck(info LoginInfo, response WebInitResponse) (*SyncCheckResponse, error) {
func (c *Caller) SyncCheck(info *LoginInfo, response *WebInitResponse) (*SyncCheckResponse, error) {
resp := NewReturnResponse(c.Client.SyncCheck(info, response))
if resp.Err() != nil {
return nil, resp.Err()
@ -135,7 +135,7 @@ func (c *Caller) SyncCheck(info LoginInfo, response WebInitResponse) (*SyncCheck
}
// 获取所有的联系人
func (c *Caller) WebWxGetContact(info LoginInfo) (Members, error) {
func (c *Caller) WebWxGetContact(info *LoginInfo) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxGetContact(info))
if resp.Err() != nil {
return nil, resp.Err()
@ -153,7 +153,7 @@ func (c *Caller) WebWxGetContact(info LoginInfo) (Members, error) {
// 获取联系人的详情
// 注: Members参数的长度不要大于50
func (c *Caller) WebWxBatchGetContact(members Members, request BaseRequest) (Members, error) {
func (c *Caller) WebWxBatchGetContact(members Members, request *BaseRequest) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request))
if resp.Err() != nil {
return nil, resp.Err()
@ -170,7 +170,7 @@ func (c *Caller) WebWxBatchGetContact(members Members, request BaseRequest) (Mem
}
// 获取新的消息接口
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 := NewReturnResponse(c.Client.WebWxSync(request, response, info))
if resp.Err() != nil {
return nil, resp.Err()
@ -184,19 +184,19 @@ func (c *Caller) WebWxSync(request BaseRequest, response WebInitResponse, info L
}
// 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info LoginInfo, request BaseRequest) error {
func (c *Caller) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) error {
resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
return parseBaseResponseError(resp)
}
// 修改用户备注接口
func (c *Caller) WebWxOplog(request BaseRequest, remarkName, toUserName string) error {
func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) error {
resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName))
return parseBaseResponseError(resp)
}
// 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request BaseRequest, info LoginInfo, fromUserName, toUserName string) error {
func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) error {
// 首先尝试上传图片
resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic"))
// 无错误上传成功之后获取请求结果,判断结果是否正常
@ -222,7 +222,7 @@ func (c *Caller) WebWxSendImageMsg(file *os.File, request BaseRequest, info Logi
}
// 用户退出
func (c *Caller) Logout(info LoginInfo) error {
func (c *Caller) Logout(info *LoginInfo) error {
resp := NewReturnResponse(c.Client.Logout(info))
return parseBaseResponseError(resp)
}

628
client.go
View File

@ -1,18 +1,18 @@
package openwechat
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"mime/multipart"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strconv"
"strings"
"time"
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"mime/multipart"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strconv"
"strings"
"time"
)
// http请求客户端
@ -21,387 +21,387 @@ import (
type Client struct{ *http.Client }
func NewClient(client *http.Client) *Client {
return &Client{Client: client}
return &Client{Client: client}
}
// 自动存储cookie
// 设置客户端不自动跳转
func DefaultClient() *Client {
jar, _ := cookiejar.New(nil)
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Jar: jar,
}
return NewClient(client)
jar, _ := cookiejar.New(nil)
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Jar: jar,
}
return NewClient(client)
}
// 获取登录的uuid
func (c *Client) GetLoginUUID() (*http.Response, error) {
path, _ := url.Parse(jsLoginUrl)
params := url.Values{}
params.Add("appid", appId)
params.Add("redirect_uri", webWxNewLoginPage)
params.Add("fun", "new")
params.Add("lang", "zh_CN")
params.Add("_", strconv.FormatInt(time.Now().Unix(), 10))
path.RawQuery = params.Encode()
return c.Get(path.String())
path, _ := url.Parse(jsLoginUrl)
params := url.Values{}
params.Add("appid", appId)
params.Add("redirect_uri", webWxNewLoginPage)
params.Add("fun", "new")
params.Add("lang", "zh_CN")
params.Add("_", strconv.FormatInt(time.Now().Unix(), 10))
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 获取登录的二维吗
func (c *Client) GetLoginQrcode(uuid string) (*http.Response, error) {
path := qrcodeUrl + uuid
return c.Get(path)
path := qrcodeUrl + uuid
return c.Get(path)
}
// 检查是否登录
func (c *Client) CheckLogin(uuid string) (*http.Response, error) {
path, _ := url.Parse(loginUrl)
now := time.Now().Unix()
params := url.Values{}
params.Add("r", strconv.FormatInt(now/1579, 10))
params.Add("_", strconv.FormatInt(now, 10))
params.Add("loginicon", "true")
params.Add("uuid", uuid)
params.Add("tip", "0")
path.RawQuery = params.Encode()
return c.Get(path.String())
path, _ := url.Parse(loginUrl)
now := time.Now().Unix()
params := url.Values{}
params.Add("r", strconv.FormatInt(now/1579, 10))
params.Add("_", strconv.FormatInt(now, 10))
params.Add("loginicon", "true")
params.Add("uuid", uuid)
params.Add("tip", "0")
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 请求获取LoginInfo
func (c *Client) GetLoginInfo(path string) (*http.Response, error) {
return c.Get(path)
return c.Get(path)
}
// 请求获取初始化信息
func (c *Client) WebInit(request BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxInitUrl)
params := url.Values{}
params.Add("_", fmt.Sprintf("%d", time.Now().Unix()))
path.RawQuery = params.Encode()
content := struct{ BaseRequest BaseRequest }{BaseRequest: request}
body, err := ToBuffer(content)
if err != nil {
return nil, err
}
return c.Post(path.String(), jsonContentType, body)
func (c *Client) WebInit(request *BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxInitUrl)
params := url.Values{}
params.Add("_", fmt.Sprintf("%d", time.Now().Unix()))
path.RawQuery = params.Encode()
content := struct{ BaseRequest *BaseRequest }{BaseRequest: request}
body, err := ToBuffer(content)
if err != nil {
return nil, err
}
return c.Post(path.String(), jsonContentType, body)
}
// 通知手机已登录
func (c *Client) WebWxStatusNotify(request BaseRequest, response WebInitResponse, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxStatusNotifyUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
username := response.User.UserName
content := map[string]interface{}{
"BaseRequest": request,
"ClientMsgId": time.Now().Unix(),
"Code": 3,
"FromUserName": username,
"ToUserName": username,
}
path.RawQuery = params.Encode()
buffer, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), buffer)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) WebWxStatusNotify(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxStatusNotifyUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
username := response.User.UserName
content := map[string]interface{}{
"BaseRequest": request,
"ClientMsgId": time.Now().Unix(),
"Code": 3,
"FromUserName": username,
"ToUserName": username,
}
path.RawQuery = params.Encode()
buffer, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), buffer)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 异步检查是否有新的消息返回
func (c *Client) SyncCheck(info LoginInfo, response WebInitResponse) (*http.Response, error) {
path, _ := url.Parse(syncCheckUrl)
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("_", strconv.FormatInt(time.Now().Unix(), 10))
var syncKeyStringSlice []string
// 将SyncKey里面的元素按照特定的格式拼接起来
for _, item := range response.SyncKey.List {
i := fmt.Sprintf("%d_%d", item.Key, item.Val)
syncKeyStringSlice = append(syncKeyStringSlice, i)
}
syncKey := strings.Join(syncKeyStringSlice, "|")
params.Add("synckey", syncKey)
path.RawQuery = params.Encode()
req, _ := http.NewRequest(http.MethodGet, path.String(), nil)
return c.Do(req)
func (c *Client) SyncCheck(info *LoginInfo, response *WebInitResponse) (*http.Response, error) {
path, _ := url.Parse(syncCheckUrl)
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("_", strconv.FormatInt(time.Now().Unix(), 10))
var syncKeyStringSlice []string
// 将SyncKey里面的元素按照特定的格式拼接起来
for _, item := range response.SyncKey.List {
i := fmt.Sprintf("%d_%d", item.Key, item.Val)
syncKeyStringSlice = append(syncKeyStringSlice, i)
}
syncKey := strings.Join(syncKeyStringSlice, "|")
params.Add("synckey", syncKey)
path.RawQuery = params.Encode()
req, _ := http.NewRequest(http.MethodGet, path.String(), nil)
return c.Do(req)
}
// 获取联系人信息
func (c *Client) WebWxGetContact(info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetContactUrl)
params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("skey", info.SKey)
params.Add("req", "0")
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) WebWxGetContact(info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetContactUrl)
params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("skey", info.SKey)
params.Add("req", "0")
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 获取联系人详情
func (c *Client) WebWxBatchGetContact(members Members, request BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxBatchGetContactUrl)
params := url.Values{}
params.Add("type", "ex")
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
path.RawQuery = params.Encode()
list := NewUserDetailItemList(members)
content := map[string]interface{}{
"BaseRequest": request,
"Count": members.Count(),
"List": list,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) WebWxBatchGetContact(members Members, request *BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxBatchGetContactUrl)
params := url.Values{}
params.Add("type", "ex")
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
path.RawQuery = params.Encode()
list := NewUserDetailItemList(members)
content := map[string]interface{}{
"BaseRequest": request,
"Count": members.Count(),
"List": list,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 获取消息接口
func (c *Client) WebWxSync(request BaseRequest, response WebInitResponse, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxSyncUrl)
params := url.Values{}
params.Add("sid", info.WxSid)
params.Add("skey", info.SKey)
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": request,
"SyncKey": response.SyncKey,
"rr": strconv.FormatInt(time.Now().Unix(), 10),
}
data, _ := json.Marshal(content)
body := bytes.NewBuffer(data)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) WebWxSync(request *BaseRequest, response *WebInitResponse, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxSyncUrl)
params := url.Values{}
params.Add("sid", info.WxSid)
params.Add("skey", info.SKey)
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": request,
"SyncKey": response.SyncKey,
"rr": strconv.FormatInt(time.Now().Unix(), 10),
}
data, _ := json.Marshal(content)
body := bytes.NewBuffer(data)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 发送消息
func (c *Client) sendMessage(request BaseRequest, url string, msg *SendMessage) (*http.Response, error) {
content := map[string]interface{}{
"BaseRequest": request,
"Msg": msg,
"Scene": 0,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, url, body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) sendMessage(request *BaseRequest, url string, msg *SendMessage) (*http.Response, error) {
content := map[string]interface{}{
"BaseRequest": request,
"Msg": msg,
"Scene": 0,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, url, body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 发送文本消息
func (c *Client) WebWxSendMsg(msg *SendMessage, info LoginInfo, request BaseRequest) (*http.Response, error) {
msg.Type = TextMessage
path, _ := url.Parse(webWxSendMsgUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
func (c *Client) WebWxSendMsg(msg *SendMessage, info *LoginInfo, request *BaseRequest) (*http.Response, error) {
msg.Type = TextMessage
path, _ := url.Parse(webWxSendMsgUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
}
// 获取用户的头像
func (c *Client) WebWxGetHeadImg(headImageUrl string) (*http.Response, error) {
path := baseUrl + headImageUrl
return c.Get(path)
path := baseUrl + headImageUrl
return c.Get(path)
}
// 上传文件
func (c *Client) WebWxUploadMedia(file *os.File, request BaseRequest, info LoginInfo, forUserName, toUserName, contentType, mediaType string) (*http.Response, error) {
path, _ := url.Parse(webWxUpLoadMediaUrl)
params := url.Values{}
params.Add("f", "json")
path.RawQuery = params.Encode()
sate, err := file.Stat()
if err != nil {
return nil, err
}
buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(file); err != nil {
return nil, err
}
data := buffer.Bytes()
fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
cookies := c.Jar.Cookies(path)
uploadMediaRequest := map[string]interface{}{
"UploadType": 2,
"BaseRequest": request,
"ClientMediaId": time.Now().Unix() * 1e4,
"TotalLen": sate.Size(),
"StartPos": 0,
"DataLen": sate.Size(),
"MediaType": 4,
"FromUserName": forUserName,
"ToUserName": toUserName,
"FileMd5": fileMd5,
}
uploadMediaRequestByte, err := json.Marshal(uploadMediaRequest)
if err != nil {
return nil, err
}
content := map[string]interface{}{
"id": "WU_FILE_0",
"name": file.Name(),
"type": contentType,
"lastModifiedDate": time.Now().Format(http.TimeFormat),
"size": sate.Size(),
"mediatype": mediaType,
"webwx_data_ticket": getWebWxDataTicket(cookies),
"pass_ticket": info.PassTicket,
}
body, err := ToBuffer(content)
if err != nil {
return nil, err
}
writer := multipart.NewWriter(body)
if err = writer.WriteField("uploadmediarequest", string(uploadMediaRequestByte)); err != nil {
return nil, err
}
if w, err := writer.CreateFormFile("filename", file.Name()); err != nil {
return nil, err
} else {
if _, err = w.Write(data); err != nil {
return nil, err
}
}
ct := writer.FormDataContentType()
if err = writer.Close(); err != nil {
return nil, err
}
return c.Post(path.String(), ct, body)
func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, contentType, mediaType string) (*http.Response, error) {
path, _ := url.Parse(webWxUpLoadMediaUrl)
params := url.Values{}
params.Add("f", "json")
path.RawQuery = params.Encode()
sate, err := file.Stat()
if err != nil {
return nil, err
}
buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(file); err != nil {
return nil, err
}
data := buffer.Bytes()
fileMd5 := fmt.Sprintf("%x", md5.Sum(data))
cookies := c.Jar.Cookies(path)
uploadMediaRequest := map[string]interface{}{
"UploadType": 2,
"BaseRequest": request,
"ClientMediaId": time.Now().Unix() * 1e4,
"TotalLen": sate.Size(),
"StartPos": 0,
"DataLen": sate.Size(),
"MediaType": 4,
"FromUserName": forUserName,
"ToUserName": toUserName,
"FileMd5": fileMd5,
}
uploadMediaRequestByte, err := json.Marshal(uploadMediaRequest)
if err != nil {
return nil, err
}
content := map[string]interface{}{
"id": "WU_FILE_0",
"name": file.Name(),
"type": contentType,
"lastModifiedDate": time.Now().Format(http.TimeFormat),
"size": sate.Size(),
"mediatype": mediaType,
"webwx_data_ticket": getWebWxDataTicket(cookies),
"pass_ticket": info.PassTicket,
}
body, err := ToBuffer(content)
if err != nil {
return nil, err
}
writer := multipart.NewWriter(body)
if err = writer.WriteField("uploadmediarequest", string(uploadMediaRequestByte)); err != nil {
return nil, err
}
if w, err := writer.CreateFormFile("filename", file.Name()); err != nil {
return nil, err
} else {
if _, err = w.Write(data); err != nil {
return nil, err
}
}
ct := writer.FormDataContentType()
if err = writer.Close(); err != nil {
return nil, err
}
return c.Post(path.String(), ct, body)
}
// 发送图片
// 这个接口依赖上传文件的接口
// 发送的图片必须是已经成功上传的图片
func (c *Client) WebWxSendMsgImg(msg *SendMessage, request BaseRequest, info LoginInfo) (*http.Response, error) {
msg.Type = ImageMessage
path, _ := url.Parse(webWxSendMsgImgUrl)
params := url.Values{}
params.Add("fun", "async")
params.Add("f", "json")
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
func (c *Client) WebWxSendMsgImg(msg *SendMessage, request *BaseRequest, info *LoginInfo) (*http.Response, error) {
msg.Type = ImageMessage
path, _ := url.Parse(webWxSendMsgImgUrl)
params := url.Values{}
params.Add("fun", "async")
params.Add("f", "json")
params.Add("lang", "zh_CN")
params.Add("pass_ticket", info.PassTicket)
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
}
// 发送文件信息
func (c *Client) WebWxSendAppMsg(msg *SendMessage, request BaseRequest) (*http.Response, error) {
msg.Type = AppMessage
path, _ := url.Parse(webWxSendAppMsgUrl)
params := url.Values{}
params.Add("fun", "async")
params.Add("f", "json")
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
func (c *Client) WebWxSendAppMsg(msg *SendMessage, request *BaseRequest) (*http.Response, error) {
msg.Type = AppMessage
path, _ := url.Parse(webWxSendAppMsgUrl)
params := url.Values{}
params.Add("fun", "async")
params.Add("f", "json")
path.RawQuery = params.Encode()
return c.sendMessage(request, path.String(), msg)
}
// 用户重命名接口
func (c *Client) WebWxOplog(request BaseRequest, remarkName, userName string, ) (*http.Response, error) {
path, _ := url.Parse(webWxOplogUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": request,
"CmdId": 2,
"RemarkName": remarkName,
"UserName": userName,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) WebWxOplog(request *BaseRequest, remarkName, userName string, ) (*http.Response, error) {
path, _ := url.Parse(webWxOplogUrl)
params := url.Values{}
params.Add("lang", "zh_CN")
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": request,
"CmdId": 2,
"RemarkName": remarkName,
"UserName": userName,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 添加用户为好友接口
func (c *Client) WebWxVerifyUser(storage WechatStorage, info RecommendInfo, verifyContent string) (*http.Response, error) {
loginInfo := storage.GetLoginInfo()
path, _ := url.Parse(webWxVerifyUserUrl)
params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("lang", "zh_CN")
params.Add("pass_ticket", loginInfo.PassTicket)
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": storage.GetBaseRequest(),
"Opcode": 3,
"SceneList": []int{33},
"SceneListCount": 1,
"VerifyContent": verifyContent,
"VerifyUserList": []interface{}{map[string]string{
"Value": info.UserName,
"VerifyUserTicket": info.Ticket,
}},
"VerifyUserListSize": 1,
"skey": loginInfo.SKey,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
func (c *Client) WebWxVerifyUser(storage *Storage, info RecommendInfo, verifyContent string) (*http.Response, error) {
loginInfo := storage.LoginInfo
path, _ := url.Parse(webWxVerifyUserUrl)
params := url.Values{}
params.Add("r", strconv.FormatInt(time.Now().Unix(), 10))
params.Add("lang", "zh_CN")
params.Add("pass_ticket", loginInfo.PassTicket)
path.RawQuery = params.Encode()
content := map[string]interface{}{
"BaseRequest": storage.Request,
"Opcode": 3,
"SceneList": []int{33},
"SceneListCount": 1,
"VerifyContent": verifyContent,
"VerifyUserList": []interface{}{map[string]string{
"Value": info.UserName,
"VerifyUserTicket": info.Ticket,
}},
"VerifyUserListSize": 1,
"skey": loginInfo.SKey,
}
body, _ := ToBuffer(content)
req, _ := http.NewRequest(http.MethodPost, path.String(), body)
req.Header.Add("Content-Type", jsonContentType)
return c.Do(req)
}
// 获取图片消息的图片响应
func (c *Client) WebWxGetMsgImg(msg *Message, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetMsgImgUrl)
params := url.Values{}
params.Add("MsgID", msg.MsgId)
params.Add("skey", info.SKey)
params.Add("type", "slave")
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) WebWxGetMsgImg(msg *Message, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetMsgImgUrl)
params := url.Values{}
params.Add("MsgID", msg.MsgId)
params.Add("skey", info.SKey)
params.Add("type", "slave")
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 获取语音消息的语音响应
func (c *Client) WebWxGetVoice(msg *Message, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetVoiceUrl)
params := url.Values{}
params.Add("msgid", msg.MsgId)
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) WebWxGetVoice(msg *Message, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetVoiceUrl)
params := url.Values{}
params.Add("msgid", msg.MsgId)
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 获取视频消息的视频响应
func (c *Client) WebWxGetVideo(msg *Message, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetVideoUrl)
params := url.Values{}
params.Add("msgid", msg.MsgId)
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) WebWxGetVideo(msg *Message, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetVideoUrl)
params := url.Values{}
params.Add("msgid", msg.MsgId)
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 获取文件消息的文件响应
func (c *Client) WebWxGetMedia(msg *Message, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetMediaUrl)
params := url.Values{}
params.Add("sender", msg.FromUserName)
params.Add("mediaid", msg.MediaId)
params.Add("encryfilename", msg.EncryFileName)
params.Add("fromuser", fmt.Sprintf("%d", info.WxUin))
params.Add("pass_ticket", info.PassTicket)
params.Add("webwx_data_ticket", getWebWxDataTicket(c.Jar.Cookies(path)))
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) WebWxGetMedia(msg *Message, info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetMediaUrl)
params := url.Values{}
params.Add("sender", msg.FromUserName)
params.Add("mediaid", msg.MediaId)
params.Add("encryfilename", msg.EncryFileName)
params.Add("fromuser", fmt.Sprintf("%d", info.WxUin))
params.Add("pass_ticket", info.PassTicket)
params.Add("webwx_data_ticket", getWebWxDataTicket(c.Jar.Cookies(path)))
path.RawQuery = params.Encode()
return c.Get(path.String())
}
// 用户退出
func (c *Client) Logout(info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxLogoutUrl)
params := url.Values{}
params.Add("redirect", "1")
params.Add("type", "1")
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
func (c *Client) Logout(info *LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxLogoutUrl)
params := url.Values{}
params.Add("redirect", "1")
params.Add("type", "1")
params.Add("skey", info.SKey)
path.RawQuery = params.Encode()
return c.Get(path.String())
}

View File

@ -127,8 +127,8 @@ func (m *Message) IsSendByGroup() bool {
// 回复消息
func (m *Message) Reply(msgType int, content, mediaId string) error {
msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
info := m.Bot.storage.GetLoginInfo()
request := m.Bot.storage.GetBaseRequest()
info := m.Bot.storage.LoginInfo
request := m.Bot.storage.Request
return m.Bot.Caller.WebWxSendMsg(msg, info, request)
}
@ -139,8 +139,8 @@ func (m *Message) ReplyText(content string) error {
// 回复图片消息
func (m *Message) ReplyImage(file *os.File) error {
info := m.Bot.storage.GetLoginInfo()
request := m.Bot.storage.GetBaseRequest()
info := m.Bot.storage.LoginInfo
request := m.Bot.storage.Request
return m.Bot.Caller.WebWxSendImageMsg(file, request, info, m.Bot.self.UserName, m.FromUserName)
}
@ -199,16 +199,16 @@ func (m *Message) GetFile() (*http.Response, error) {
return nil, errors.New("invalid message type")
}
if m.IsPicture() {
return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.GetLoginInfo())
return m.Bot.Caller.Client.WebWxGetMsgImg(m, m.Bot.storage.LoginInfo)
}
if m.IsVoice() {
return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.GetLoginInfo())
return m.Bot.Caller.Client.WebWxGetVoice(m, m.Bot.storage.LoginInfo)
}
if m.IsVideo() {
return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.GetLoginInfo())
return m.Bot.Caller.Client.WebWxGetVideo(m, m.Bot.storage.LoginInfo)
}
if m.IsMedia() {
return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.GetLoginInfo())
return m.Bot.Caller.Client.WebWxGetMedia(m, m.Bot.storage.LoginInfo)
}
return nil, errors.New("unsupported type")
}

View File

@ -1,49 +1,3 @@
package openwechat
// WechatStorage
// 可以根据自己的情况来实现该接口
type WechatStorage interface {
SetLoginInfo(loginInfo LoginInfo)
SetBaseRequest(baseRequest BaseRequest)
SetWebInitResponse(webInitResponse WebInitResponse)
GetLoginInfo() LoginInfo
GetBaseRequest() BaseRequest
GetWebInitResponse() WebInitResponse
}
// implement WechatStorage
// WechatStorage接口的实现
type SimpleWechatStorage struct {
loginInfo LoginInfo
baseRequest BaseRequest
webInitResponse WebInitResponse
}
func NewSimpleWechatStorage() *SimpleWechatStorage {
return &SimpleWechatStorage{}
}
func (s *SimpleWechatStorage) SetLoginInfo(loginInfo LoginInfo) {
s.loginInfo = loginInfo
}
func (s *SimpleWechatStorage) SetBaseRequest(baseRequest BaseRequest) {
s.baseRequest = baseRequest
}
func (s *SimpleWechatStorage) SetWebInitResponse(webInitResponse WebInitResponse) {
s.webInitResponse = webInitResponse
}
func (s *SimpleWechatStorage) GetLoginInfo() LoginInfo {
return s.loginInfo
}
func (s *SimpleWechatStorage) GetBaseRequest() BaseRequest {
return s.baseRequest
}
func (s *SimpleWechatStorage) GetWebInitResponse() WebInitResponse {
return s.webInitResponse
}

508
user.go
View File

@ -1,349 +1,355 @@
package openwechat
import (
"bytes"
"fmt"
"net/http"
"os"
"bytes"
"fmt"
"net/http"
"os"
)
type User struct {
Uin int
HideInputBarFlag int
StarFriend int
Sex int
AppAccountFlag int
VerifyFlag int
ContactFlag int
WebWxPluginSwitch int
HeadImgFlag int
SnsFlag int
UserName string
NickName string
HeadImgUrl string
RemarkName string
PYInitial string
PYQuanPin string
RemarkPYInitial string
RemarkPYQuanPin string
Signature string
MemberCount int
MemberList Members
OwnerUin int
Statues int
AttrStatus int
Province string
City string
Alias string
UniFriend int
DisplayName string
ChatRoomId int
KeyWord string
EncryChatRoomId string
IsOwner int
Uin int
HideInputBarFlag int
StarFriend int
Sex int
AppAccountFlag int
VerifyFlag int
ContactFlag int
WebWxPluginSwitch int
HeadImgFlag int
SnsFlag int
UserName string
NickName string
HeadImgUrl string
RemarkName string
PYInitial string
PYQuanPin string
RemarkPYInitial string
RemarkPYQuanPin string
Signature string
MemberCount int
MemberList Members
OwnerUin int
Statues int
AttrStatus int
Province string
City string
Alias string
UniFriend int
DisplayName string
ChatRoomId int
KeyWord string
EncryChatRoomId string
IsOwner int
Self *Self
Self *Self
}
// implement fmt.Stringer
func (u *User) String() string {
return fmt.Sprintf("<User:%s>", u.NickName)
return fmt.Sprintf("<User:%s>", u.NickName)
}
// 获取用户头像
func (u *User) GetAvatarResponse() (*http.Response, error) {
return u.Self.Bot.Caller.Client.WebWxGetHeadImg(u.HeadImgUrl)
return u.Self.Bot.Caller.Client.WebWxGetHeadImg(u.HeadImgUrl)
}
// 下载用户头像
func (u *User) SaveAvatar(filename string) error {
resp, err := u.GetAvatarResponse()
if err != nil {
return err
}
defer resp.Body.Close()
buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(resp.Body); err != nil {
return err
}
return os.WriteFile(filename, buffer.Bytes(), os.ModePerm)
resp, err := u.GetAvatarResponse()
if err != nil {
return err
}
defer resp.Body.Close()
buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(resp.Body); err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(buffer.Bytes())
return err
}
func (u *User) sendMsg(msg *SendMessage) error {
msg.FromUserName = u.Self.UserName
msg.ToUserName = u.UserName
info := u.Self.Bot.storage.GetLoginInfo()
request := u.Self.Bot.storage.GetBaseRequest()
return u.Self.Bot.Caller.WebWxSendMsg(msg, info, request)
msg.FromUserName = u.Self.UserName
msg.ToUserName = u.UserName
info := u.Self.Bot.storage.LoginInfo
request := u.Self.Bot.storage.Request
return u.Self.Bot.Caller.WebWxSendMsg(msg, info, request)
}
func (u *User) sendText(content string) error {
msg := NewTextSendMessage(content, u.Self.UserName, u.UserName)
return u.sendMsg(msg)
msg := NewTextSendMessage(content, u.Self.UserName, u.UserName)
return u.sendMsg(msg)
}
func (u *User) sendImage(file *os.File) error {
request := u.Self.Bot.storage.GetBaseRequest()
info := u.Self.Bot.storage.GetLoginInfo()
return u.Self.Bot.Caller.WebWxSendImageMsg(file, request, info, u.Self.UserName, u.UserName)
request := u.Self.Bot.storage.Request
info := u.Self.Bot.storage.LoginInfo
return u.Self.Bot.Caller.WebWxSendImageMsg(file, request, info, u.Self.UserName, u.UserName)
}
func (u *User) setRemarkName(remarkName string) error {
request := u.Self.Bot.storage.GetBaseRequest()
return u.Self.Bot.Caller.WebWxOplog(request, remarkName, u.UserName)
request := u.Self.Bot.storage.Request
return u.Self.Bot.Caller.WebWxOplog(request, remarkName, u.UserName)
}
// 获取用户的详情
func (u *User) Detail() (*User, error) {
members := Members{u}
request := u.Self.Bot.storage.GetBaseRequest()
newMembers, err := u.Self.Bot.Caller.WebWxBatchGetContact(members, request)
if err != nil {
return nil, err
}
user := newMembers.First()
user.Self = u.Self
return user, nil
members := Members{u}
request := u.Self.Bot.storage.Request
newMembers, err := u.Self.Bot.Caller.WebWxBatchGetContact(members, request)
if err != nil {
return nil, err
}
user := newMembers.First()
user.Self = u.Self
return user, nil
}
type Self struct {
*User
Bot *Bot
fileHelper *Friend
members Members
friends Friends
groups Groups
*User
Bot *Bot
fileHelper *Friend
members Members
friends Friends
groups Groups
}
// 获取所有的好友、群组、公众号信息
func (s *Self) Members(update ...bool) (Members, error) {
// 首先判断缓存里有没有,如果没有则去更新缓存
if s.members == nil {
if err := s.updateMembers(); err != nil {
return nil, err
}
return s.members, nil
}
// 判断是否需要更新,如果传入的参数不为nil,则取最后一个
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
// 如果需要更新,则直接更新缓存
if isUpdate {
if err := s.updateMembers(); err != nil {
return nil, err
}
}
return s.members, nil
// 首先判断缓存里有没有,如果没有则去更新缓存
if s.members == nil {
if err := s.updateMembers(); err != nil {
return nil, err
}
return s.members, nil
}
// 判断是否需要更新,如果传入的参数不为nil,则取最后一个
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
// 如果需要更新,则直接更新缓存
if isUpdate {
if err := s.updateMembers(); err != nil {
return nil, err
}
}
return s.members, nil
}
// 更新联系人处理
func (s *Self) updateMembers() error {
info := s.Bot.storage.GetLoginInfo()
members, err := s.Bot.Caller.WebWxGetContact(info)
if err != nil {
return err
}
members.SetOwner(s)
s.members = members
return nil
info := s.Bot.storage.LoginInfo
members, err := s.Bot.Caller.WebWxGetContact(info)
if err != nil {
return err
}
members.SetOwner(s)
s.members = members
return nil
}
// 获取文件传输助手对象封装成Friend返回
func (s *Self) FileHelper() (*Friend, error) {
// 如果缓存里有,直接返回,否则去联系人里面找
if s.fileHelper != nil {
return s.fileHelper, nil
}
members, err := s.Members()
if err != nil {
return nil, err
}
users := members.SearchByUserName(1, "filehelper")
if users == nil {
return nil, noSuchUserFoundError
}
s.fileHelper = &Friend{users.First()}
return s.fileHelper, nil
// 如果缓存里有,直接返回,否则去联系人里面找
if s.fileHelper != nil {
return s.fileHelper, nil
}
members, err := s.Members()
if err != nil {
return nil, err
}
users := members.SearchByUserName(1, "filehelper")
if users == nil {
return nil, noSuchUserFoundError
}
s.fileHelper = &Friend{users.First()}
return s.fileHelper, nil
}
// 获取所有的好友
func (s *Self) Friends(update ...bool) (Friends, error) {
if s.friends == nil {
if err := s.updateFriends(true); err != nil {
return nil, err
}
}
return s.friends, nil
if s.friends == nil {
if err := s.updateFriends(true); err != nil {
return nil, err
}
}
return s.friends, nil
}
// 获取所有的群组
func (s *Self) Groups(update ...bool) (Groups, error) {
if s.groups == nil {
if err := s.updateGroups(update...); err != nil {
return nil, err
}
}
return s.groups, nil
if s.groups == nil {
if err := s.updateGroups(update...); err != nil {
return nil, err
}
}
return s.groups, nil
}
// 更新好友处理
func (s *Self) updateFriends(update ...bool) error {
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
if isUpdate || s.members == nil {
if err := s.updateMembers(); err != nil {
return err
}
}
var friends Friends
for _, member := range s.members {
if isFriend(*member) {
friend := &Friend{member}
friend.Self = s
friends = append(friends, friend)
}
}
s.friends = friends
return nil
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
if isUpdate || s.members == nil {
if err := s.updateMembers(); err != nil {
return err
}
}
var friends Friends
for _, member := range s.members {
if isFriend(*member) {
friend := &Friend{member}
friend.Self = s
friends = append(friends, friend)
}
}
s.friends = friends
return nil
}
// 更新群组处理
func (s *Self) updateGroups(update ...bool) error {
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
if isUpdate || s.members == nil {
if err := s.updateMembers(); err != nil {
return err
}
}
var groups Groups
for _, member := range s.members {
if isGroup(*member) {
group := &Group{member}
groups = append(groups, group)
}
}
s.groups = groups
return nil
var isUpdate bool
if len(update) > 0 {
isUpdate = update[len(update)-1]
}
if isUpdate || s.members == nil {
if err := s.updateMembers(); err != nil {
return err
}
}
var groups Groups
for _, member := range s.members {
if isGroup(*member) {
group := &Group{member}
groups = append(groups, group)
}
}
s.groups = groups
return nil
}
// 更新所有的联系人信息
func (s *Self) UpdateMembersDetail() error {
// 先获取所有的联系人
members, err := s.Members()
if err != nil {
return err
}
// 获取他们的数量
count := members.Count()
// 一次更新50个,分情况讨论
// 先获取所有的联系人
members, err := s.Members()
if err != nil {
return err
}
// 获取他们的数量
count := members.Count()
// 一次更新50个,分情况讨论
// 获取总的需要更新的次数
var times int
if count < 50 {
times = 1
} else {
times = count / 50
}
var newMembers Members
request := s.Bot.storage.GetBaseRequest()
var pMembers Members
// 分情况依次更新
for i := 1; i <= times; i++ {
if times == 1 {
pMembers = members
} else {
pMembers = members[(i-1)*50 : i*50]
}
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
// 最后判断是否全部更新完毕
total := times * 50
if total < count {
// 将全部剩余的更新完毕
left := count - total
pMembers = members[total : total+left]
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
newMembers.SetOwner(s)
s.members = newMembers
return nil
// 获取总的需要更新的次数
var times int
if count < 50 {
times = 1
} else {
times = count / 50
}
var newMembers Members
request := s.Bot.storage.Request
var pMembers Members
// 分情况依次更新
for i := 1; i <= times; i++ {
if times == 1 {
pMembers = members
} else {
pMembers = members[(i-1)*50 : i*50]
}
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
// 最后判断是否全部更新完毕
total := times * 50
if total < count {
// 将全部剩余的更新完毕
left := count - total
pMembers = members[total : total+left]
nMembers, err := s.Bot.Caller.WebWxBatchGetContact(pMembers, request)
if err != nil {
return err
}
newMembers = append(newMembers, nMembers...)
}
newMembers.SetOwner(s)
s.members = newMembers
return nil
}
type Members []*User
func (m Members) Count() int {
return len(m)
return len(m)
}
func (m Members) First() *User {
if m.Count() > 0 {
return m[0]
}
return nil
if m.Count() > 0 {
return m[0]
}
return nil
}
func (m Members) Last() *User {
if m.Count() > 0 {
return m[m.Count()-1]
}
return nil
if m.Count() > 0 {
return m[m.Count()-1]
}
return nil
}
func (m Members) SetOwner(s *Self) {
for _, member := range m {
member.Self = s
}
for _, member := range m {
member.Self = s
}
}
func (m Members) SearchByUserName(limit int, username string) (results Members) {
return m.Search(limit, func(user *User) bool { return user.UserName == username })
return m.Search(limit, func(user *User) bool { return user.UserName == username })
}
func (m Members) SearchByNickName(limit int, nickName string) (results Members) {
return m.Search(limit, func(user *User) bool { return user.NickName == nickName })
return m.Search(limit, func(user *User) bool { return user.NickName == nickName })
}
func (m Members) SearchByRemarkName(limit int, remarkName string) (results Members) {
return m.Search(limit, func(user *User) bool { return user.RemarkName == remarkName })
return m.Search(limit, func(user *User) bool { return user.RemarkName == remarkName })
}
func (m Members) Search(limit int, condFuncList ...func(user *User) bool) (results Members) {
if condFuncList == nil {
return m
}
if limit <= 0 {
limit = m.Count()
}
for _, member := range m {
if results.Count() == limit {
break
}
var passCount int
for _, condFunc := range condFuncList {
if condFunc(member) {
passCount++
} else {
break
}
}
if passCount == len(condFuncList) {
results = append(results, member)
}
}
return
if condFuncList == nil {
return m
}
if limit <= 0 {
limit = m.Count()
}
for _, member := range m {
if results.Count() == limit {
break
}
var passCount int
for _, condFunc := range condFuncList {
if condFunc(member) {
passCount++
} else {
break
}
}
if passCount == len(condFuncList) {
results = append(results, member)
}
}
return
}