diff --git a/bot_test.go b/bot_test.go index a401dfe..fec65e1 100644 --- a/bot_test.go +++ b/bot_test.go @@ -1,6 +1,7 @@ package openwechat import ( + "os" "testing" "time" ) @@ -238,8 +239,8 @@ func TestHotLogin(t *testing.T) { } func TestFriendHelper(t *testing.T) { - bot := defaultBot(Desktop) - if err := bot.Login(); err != nil { + bot := defaultBot() + if err := bot.HotLogin(NewJsonFileHotReloadStorage("2.json"), true); err != nil { t.Error(err) return } @@ -253,7 +254,11 @@ func TestFriendHelper(t *testing.T) { t.Error(err) return } - msg, err := fh.SendText("test message") + f, _ := os.Open("webwxgetmsgimg.jpeg") + //f, _ := os.Open("2.jpeg") + defer f.Close() + msg, err := fh.SendImage(f) + //msg, err := fh.SendText("hh") if err != nil { t.Error(err) return @@ -263,7 +268,7 @@ func TestFriendHelper(t *testing.T) { func TestRevokeMessage(t *testing.T) { bot := defaultBot(Desktop) - if err := bot.Login(); err != nil { + if err := bot.HotLogin(NewJsonFileHotReloadStorage("2.json")); err != nil { t.Error(err) return } diff --git a/caller.go b/caller.go index 953d338..b96d55b 100644 --- a/caller.go +++ b/caller.go @@ -2,6 +2,7 @@ package openwechat import ( "errors" + "fmt" "os" ) @@ -203,29 +204,45 @@ func (c *Caller) WebWxOplog(request *BaseRequest, remarkName, toUserName string) // 发送图片消息接口 func (c *Caller) WebWxSendImageMsg(file *os.File, request *BaseRequest, info *LoginInfo, fromUserName, toUserName string) (*SentMessage, error) { // 首先尝试上传图片 - resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic")) + sate, err := file.Stat() + if err != nil { + return nil, err + } + + var resp *ReturnResponse + if sate.Size() <= chunkSize { + resp = NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "pic")) + } else { + resp = NewReturnResponse(c.Client.WebWxUploadMediaByChunk(file, request, info, fromUserName, toUserName, "pic")) + } // 无错误上传成功之后获取请求结果,判断结果是否正常 if resp.Err() != nil { return nil, resp.Err() } defer resp.Body.Close() + data, _ := resp.ReadAll() + fmt.Println(string(data)) var item struct { BaseResponse BaseResponse MediaId string } - if err := resp.ScanJSON(&item); err != nil { + + if err = resp.ScanJSON(&item); err != nil { return nil, err } if !item.BaseResponse.Ok() { return nil, item.BaseResponse } + if len(item.MediaId) == 0 { + return nil, errors.New("upload failed") + } // 构造新的图片类型的信息 msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId) // 发送图片信息 resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info)) sendSuccessMsg := &SentMessage{SendMessage: msg} - err := parseMessageResponseError(resp, sendSuccessMsg) + err = parseMessageResponseError(resp, sendSuccessMsg) return sendSuccessMsg, err } diff --git a/client.go b/client.go index 83b47a2..3c7e9ca 100644 --- a/client.go +++ b/client.go @@ -46,6 +46,7 @@ func DefaultClient(urlManager UrlManager) *Client { // 抽象Do方法,将所有的有效的cookie存入Client.cookies // 方便热登陆时获取 func (c *Client) Do(req *http.Request) (*http.Response, error) { + req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36") resp, err := c.Client.Do(req) if err != nil { return resp, err @@ -251,7 +252,12 @@ func (c *Client) WebWxGetHeadImg(headImageUrl string) (*http.Response, error) { } // 上传文件 -func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, contentType, mediaType string) (*http.Response, error) { +func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) { + // 获取文件上传的类型 + contentType, err := GetFileContentType(file) + if err != nil { + return nil, err + } path, _ := url.Parse(c.webWxUpLoadMediaUrl) params := url.Values{} params.Add("f", "json") @@ -260,13 +266,21 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log if err != nil { return nil, err } + cookies := c.Jar.Cookies(path) + webWxDataTicket := getWebWxDataTicket(cookies) + + // 文件复位 + if _, err = file.Seek(0, 0); 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, @@ -283,14 +297,15 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log 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), + "lastModifiedDate": sate.ModTime().Format(TimeFormat), "size": sate.Size(), "mediatype": mediaType, - "webwx_data_ticket": getWebWxDataTicket(cookies), + "webwx_data_ticket": webWxDataTicket, "pass_ticket": info.PassTicket, } body, err := ToBuffer(content) @@ -313,10 +328,131 @@ func (c *Client) WebWxUploadMedia(file *os.File, request *BaseRequest, info *Log return nil, err } req, _ := http.NewRequest(http.MethodPost, path.String(), body) + req.Header.Set("Content-Type", ct) return c.Do(req) } +func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, info *LoginInfo, forUserName, toUserName, mediaType string) (*http.Response, error) { + // 获取文件上传的类型 + contentType, err := GetFileContentType(file) + if err != nil { + return nil, err + } + path, _ := url.Parse(c.webWxUpLoadMediaUrl) + params := url.Values{} + params.Add("f", "json") + path.RawQuery = params.Encode() + sate, err := file.Stat() + if err != nil { + return nil, err + } + cookies := c.Jar.Cookies(path) + webWxDataTicket := getWebWxDataTicket(cookies) + + // 将文件的游标复原到原点 + // 上面获取文件的类型的时候已经读取了512个字节 + if _, err = file.Seek(0, 0); 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)) + + 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 + } + + chunks := sate.Size() / chunkSize + if chunks*chunkSize < sate.Size() { + chunks++ + } + + var resp *http.Response + + for chunk := 0; int64(chunk) < chunks; chunk++ { + content := map[string]interface{}{ + "id": "WU_FILE_0", + "name": file.Name(), + "type": contentType, + "lastModifiedDate": sate.ModTime().Format(TimeFormat), + "size": sate.Size(), + "mediatype": mediaType, + "webwx_data_ticket": webWxDataTicket, + "pass_ticket": info.PassTicket, + "chunks": chunks, + "chunk": chunk, + } + 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 + } + + var isLastTime bool + if int64(chunk)+1 == chunks { + isLastTime = true + } + + if w, err := writer.CreateFormFile("filename", file.Name()); err != nil { + return nil, err + } else { + var chunkData []byte + // 判断是不是最后一次 + if !isLastTime { + chunkData = data[int64(chunk)*chunkSize : (int64(chunk)+1)*chunkSize] + } else { + chunkData = data[int64(chunk)*chunkSize:] + } + if _, err = w.Write(chunkData); err != nil { + return nil, err + } + } + ct := writer.FormDataContentType() + if err = writer.Close(); err != nil { + return nil, err + } + req, _ := http.NewRequest(http.MethodPost, path.String(), body) + req.Header.Set("Content-Type", ct) + //req.Header.Add("Referer", c.baseUrl) + //req.Header.Add("Origin", c.baseUrl) + //// Host: file.wx2.qq.com + //req.Header.Add("Host", "file.wx2.qq.com") + // 发送数据 + resp, err = c.Do(req) + + // 如果不是最后一次, 解析有没有错误 + if !isLastTime { + returnResp := NewReturnResponse(resp, err) + if err := parseBaseResponseError(returnResp); err != nil { + return nil, err + } + } + } + + return resp, err +} + // 发送图片 // 这个接口依赖上传文件的接口 // 发送的图片必须是已经成功上传的图片 diff --git a/global.go b/global.go index 7f99878..765d136 100644 --- a/global.go +++ b/global.go @@ -1,94 +1,94 @@ package openwechat import ( - "errors" - "regexp" + "errors" + "regexp" ) var ( - uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`) - statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`) - syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`) - redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`) + uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`) + statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`) + syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`) + redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`) ) const ( - appId = "wx782c26e4c19acffb" - jsLoginUrl = "https://login.wx.qq.com/jslogin" - qrcodeUrl = "https://login.weixin.qq.com/qrcode/" - loginUrl = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login" + appId = "wx782c26e4c19acffb" + jsLoginUrl = "https://login.wx.qq.com/jslogin" + qrcodeUrl = "https://login.weixin.qq.com/qrcode/" + loginUrl = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login" - // Normal urls + // Normal urls - baseNormalUrl = "https://wx2.qq.com" - webWxNewLoginPageNormalUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage" - webWxInitNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit" - webWxStatusNotifyNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" - webWxSyncNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync" - webWxSendMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" - webWxGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" - webWxSendMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" - webWxSendAppMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" - webWxBatchGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" - webWxOplogNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" - webWxVerifyUserNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" - syncCheckNormalUrl = "https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck" - webWxUpLoadMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" - webWxGetMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" - webWxGetVoiceNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" - webWxGetVideoNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" - webWxLogoutNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" - webWxGetMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" - webWxUpdateChatRoomNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" - webWxRevokeMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" + baseNormalUrl = "https://wx2.qq.com" + webWxNewLoginPageNormalUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage" + webWxInitNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit" + webWxStatusNotifyNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" + webWxSyncNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync" + webWxSendMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" + webWxGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" + webWxSendMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" + webWxSendAppMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" + webWxBatchGetContactNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" + webWxOplogNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" + webWxVerifyUserNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" + syncCheckNormalUrl = "https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck" + webWxUpLoadMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" + webWxGetMsgImgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" + webWxGetVoiceNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" + webWxGetVideoNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" + webWxLogoutNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" + webWxGetMediaNormalUrl = "https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" + webWxUpdateChatRoomNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" + webWxRevokeMsgNormalUrl = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" - // Desktop urls + // Desktop urls - baseDesktopUrl = "https://wx.qq.com" - webWxNewLoginPageDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop" - webWxInitDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit" - webWxStatusNotifyDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" - webWxSyncDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync" - webWxSendMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" - webWxGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" - webWxSendMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" - webWxSendAppMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" - webWxBatchGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" - webWxOplogDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" - webWxVerifyUserDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" - syncCheckDesktopUrl = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck" - webWxUpLoadMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" - webWxGetMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" - webWxGetVoiceDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" - webWxGetVideoDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" - webWxLogoutDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" - webWxGetMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" - webWxUpdateChatRoomDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" - webWxRevokeMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" + baseDesktopUrl = "https://wx.qq.com" + webWxNewLoginPageDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop" + webWxInitDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit" + webWxStatusNotifyDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify" + webWxSyncDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync" + webWxSendMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg" + webWxGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact" + webWxSendMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg" + webWxSendAppMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg" + webWxBatchGetContactDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact" + webWxOplogDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog" + webWxVerifyUserDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser" + syncCheckDesktopUrl = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck" + webWxUpLoadMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia" + webWxGetMsgImgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg" + webWxGetVoiceDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice" + webWxGetVideoDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo" + webWxLogoutDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout" + webWxGetMediaDesktopUrl = "https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia" + webWxUpdateChatRoomDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom" + webWxRevokeMsgDesktopUrl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxrevokemsg" - jsonContentType = "application/json; charset=utf-8" - uosPatchClientVersion = "2.0.0" - uosPatchExtspam = "Gp8ICJkIEpkICggwMDAwMDAwMRAGGoAI1GiJSIpeO1RZTq9QBKsRbPJdi84ropi16EYI10WB6g74sGmRwSNXjPQnYUKYotKkvLGpshucCaeWZMOylnc6o2AgDX9grhQQx7fm2DJRTyuNhUlwmEoWhjoG3F0ySAWUsEbH3bJMsEBwoB//0qmFJob74ffdaslqL+IrSy7LJ76/G5TkvNC+J0VQkpH1u3iJJs0uUYyLDzdBIQ6Ogd8LDQ3VKnJLm4g/uDLe+G7zzzkOPzCjXL+70naaQ9medzqmh+/SmaQ6uFWLDQLcRln++wBwoEibNpG4uOJvqXy+ql50DjlNchSuqLmeadFoo9/mDT0q3G7o/80P15ostktjb7h9bfNc+nZVSnUEJXbCjTeqS5UYuxn+HTS5nZsPVxJA2O5GdKCYK4x8lTTKShRstqPfbQpplfllx2fwXcSljuYi3YipPyS3GCAqf5A7aYYwJ7AvGqUiR2SsVQ9Nbp8MGHET1GxhifC692APj6SJxZD3i1drSYZPMMsS9rKAJTGz2FEupohtpf2tgXm6c16nDk/cw+C7K7me5j5PLHv55DFCS84b06AytZPdkFZLj7FHOkcFGJXitHkX5cgww7vuf6F3p0yM/W73SoXTx6GX4G6Hg2rYx3O/9VU2Uq8lvURB4qIbD9XQpzmyiFMaytMnqxcZJcoXCtfkTJ6pI7a92JpRUvdSitg967VUDUAQnCXCM/m0snRkR9LtoXAO1FUGpwlp1EfIdCZFPKNnXMeqev0j9W9ZrkEs9ZWcUEexSj5z+dKYQBhIICviYUQHVqBTZSNy22PlUIeDeIs11j7q4t8rD8LPvzAKWVqXE+5lS1JPZkjg4y5hfX1Dod3t96clFfwsvDP6xBSe1NBcoKbkyGxYK0UvPGtKQEE0Se2zAymYDv41klYE9s+rxp8e94/H8XhrL9oGm8KWb2RmYnAE7ry9gd6e8ZuBRIsISlJAE/e8y8xFmP031S6Lnaet6YXPsFpuFsdQs535IjcFd75hh6DNMBYhSfjv456cvhsb99+fRw/KVZLC3yzNSCbLSyo9d9BI45Plma6V8akURQA/qsaAzU0VyTIqZJkPDTzhuCl92vD2AD/QOhx6iwRSVPAxcRFZcWjgc2wCKh+uCYkTVbNQpB9B90YlNmI3fWTuUOUjwOzQRxJZj11NsimjOJ50qQwTTFj6qQvQ1a/I+MkTx5UO+yNHl718JWcR3AXGmv/aa9rD1eNP8ioTGlOZwPgmr2sor2iBpKTOrB83QgZXP+xRYkb4zVC+LoAXEoIa1+zArywlgREer7DLePukkU6wHTkuSaF+ge5Of1bXuU4i938WJHj0t3D8uQxkJvoFi/EYN/7u2P1zGRLV4dHVUsZMGCCtnO6BBigFMAA=" + jsonContentType = "application/json; charset=utf-8" + uosPatchClientVersion = "2.0.0" + uosPatchExtspam = "Gp8ICJkIEpkICggwMDAwMDAwMRAGGoAI1GiJSIpeO1RZTq9QBKsRbPJdi84ropi16EYI10WB6g74sGmRwSNXjPQnYUKYotKkvLGpshucCaeWZMOylnc6o2AgDX9grhQQx7fm2DJRTyuNhUlwmEoWhjoG3F0ySAWUsEbH3bJMsEBwoB//0qmFJob74ffdaslqL+IrSy7LJ76/G5TkvNC+J0VQkpH1u3iJJs0uUYyLDzdBIQ6Ogd8LDQ3VKnJLm4g/uDLe+G7zzzkOPzCjXL+70naaQ9medzqmh+/SmaQ6uFWLDQLcRln++wBwoEibNpG4uOJvqXy+ql50DjlNchSuqLmeadFoo9/mDT0q3G7o/80P15ostktjb7h9bfNc+nZVSnUEJXbCjTeqS5UYuxn+HTS5nZsPVxJA2O5GdKCYK4x8lTTKShRstqPfbQpplfllx2fwXcSljuYi3YipPyS3GCAqf5A7aYYwJ7AvGqUiR2SsVQ9Nbp8MGHET1GxhifC692APj6SJxZD3i1drSYZPMMsS9rKAJTGz2FEupohtpf2tgXm6c16nDk/cw+C7K7me5j5PLHv55DFCS84b06AytZPdkFZLj7FHOkcFGJXitHkX5cgww7vuf6F3p0yM/W73SoXTx6GX4G6Hg2rYx3O/9VU2Uq8lvURB4qIbD9XQpzmyiFMaytMnqxcZJcoXCtfkTJ6pI7a92JpRUvdSitg967VUDUAQnCXCM/m0snRkR9LtoXAO1FUGpwlp1EfIdCZFPKNnXMeqev0j9W9ZrkEs9ZWcUEexSj5z+dKYQBhIICviYUQHVqBTZSNy22PlUIeDeIs11j7q4t8rD8LPvzAKWVqXE+5lS1JPZkjg4y5hfX1Dod3t96clFfwsvDP6xBSe1NBcoKbkyGxYK0UvPGtKQEE0Se2zAymYDv41klYE9s+rxp8e94/H8XhrL9oGm8KWb2RmYnAE7ry9gd6e8ZuBRIsISlJAE/e8y8xFmP031S6Lnaet6YXPsFpuFsdQs535IjcFd75hh6DNMBYhSfjv456cvhsb99+fRw/KVZLC3yzNSCbLSyo9d9BI45Plma6V8akURQA/qsaAzU0VyTIqZJkPDTzhuCl92vD2AD/QOhx6iwRSVPAxcRFZcWjgc2wCKh+uCYkTVbNQpB9B90YlNmI3fWTuUOUjwOzQRxJZj11NsimjOJ50qQwTTFj6qQvQ1a/I+MkTx5UO+yNHl718JWcR3AXGmv/aa9rD1eNP8ioTGlOZwPgmr2sor2iBpKTOrB83QgZXP+xRYkb4zVC+LoAXEoIa1+zArywlgREer7DLePukkU6wHTkuSaF+ge5Of1bXuU4i938WJHj0t3D8uQxkJvoFi/EYN/7u2P1zGRLV4dHVUsZMGCCtnO6BBigFMAA=" ) // 消息类型 const ( - TextMessage = 1 - ImageMessage = 3 - AppMessage = 6 + TextMessage = 1 + ImageMessage = 3 + AppMessage = 6 ) // 登录状态 const ( - statusSuccess = "200" - statusScanned = "201" - statusTimeout = "400" - statusWait = "408" + statusSuccess = "200" + statusScanned = "201" + statusTimeout = "400" + statusWait = "408" ) // errors var ( - noSuchUserFoundError = errors.New("no such user found") + noSuchUserFoundError = errors.New("no such user found") ) // ALL跟search函数搭配 @@ -97,6 +97,10 @@ const ALL = 0 // 性别 const ( - MALE = 1 - FEMALE = 2 + MALE = 1 + FEMALE = 2 ) + +const chunkSize int64 = 512 * 1024 + +const TimeFormat = "Mon Jan 02 2006 15:04:05 GMT+0800 (中国标准时间)" diff --git a/parser.go b/parser.go index b0ff4f4..a2c3e3d 100644 --- a/parser.go +++ b/parser.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "math/rand" + "mime/multipart" "net/http" "strconv" "strings" @@ -18,6 +19,7 @@ func ToBuffer(v interface{}) (*bytes.Buffer, error) { return bytes.NewBuffer(buf), nil } +// 获取随机设备id func GetRandomDeviceId() string { rand.Seed(time.Now().Unix()) var builder strings.Builder @@ -38,6 +40,7 @@ func getWebWxDataTicket(cookies []*http.Cookie) string { return "" } +// Form Xml 格式化 func XmlFormString(text string) string { lt := strings.ReplaceAll(text, "<", "<") gt := strings.ReplaceAll(lt, ">", ">") @@ -52,3 +55,12 @@ func getTotalDuration(delay ...time.Duration) time.Duration { } return total } + +// 获取文件上传的类型 +func GetFileContentType(file multipart.File) (string, error) { + data := make([]byte, 512) + if _, err := file.Read(data); err != nil { + return "", err + } + return http.DetectContentType(data), nil +} diff --git a/user.go b/user.go index 9e93dc6..5e60b9d 100644 --- a/user.go +++ b/user.go @@ -9,7 +9,7 @@ import ( "strings" ) -// 抽象的用户结构,包含 好友 群组 公众号 +// 抽象的用户结构: 好友 群组 公众号 type User struct { Uin int HideInputBarFlag int @@ -451,6 +451,7 @@ func (m Members) MPs() Mps { return mps } +// 获取当前Members的详情 func (m Members) detail(self *Self) error { // 获取他们的数量 members := m