From f80cb2ebd24bec4aa37c0accdfe2a2b2a6ca71cc Mon Sep 17 00:00:00 2001 From: eatMoreApple <15055461510@163.com> Date: Mon, 12 Apr 2021 11:59:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4storage=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=9A=84=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.go | 321 +++++++++++++-------------- bot_test.go | 1 + caller.go | 20 +- client.go | 628 ++++++++++++++++++++++++++-------------------------- message.go | 16 +- stroage.go | 46 ---- user.go | 508 +++++++++++++++++++++--------------------- 7 files changed, 748 insertions(+), 792 deletions(-) diff --git a/bot.go b/bot.go index ffb5e1c..77be920 100644 --- a/bot.go +++ b/bot.go @@ -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)) } diff --git a/bot_test.go b/bot_test.go index 5f733a4..68fc582 100644 --- a/bot_test.go +++ b/bot_test.go @@ -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() diff --git a/caller.go b/caller.go index 06e0190..b15f0f9 100644 --- a/caller.go +++ b/caller.go @@ -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) } diff --git a/client.go b/client.go index 4c6db13..272a7f7 100644 --- a/client.go +++ b/client.go @@ -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()) } diff --git a/message.go b/message.go index f0accc1..512a472 100644 --- a/message.go +++ b/message.go @@ -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") } diff --git a/stroage.go b/stroage.go index 859a304..5aca72d 100644 --- a/stroage.go +++ b/stroage.go @@ -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 -} diff --git a/user.go b/user.go index 82d3a81..1ba6d49 100644 --- a/user.go +++ b/user.go @@ -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("", u.NickName) + return fmt.Sprintf("", 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 }