package service import ( "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "regexp" "strconv" "strings" "time" "xiawan/wx/api/req" "xiawan/wx/api/utils" "xiawan/wx/api/vo" "xiawan/wx/clientsdk/baseinfo" "xiawan/wx/srv/wxface" ) // GetA8KeyService 授权链接 func GetA8KeyService(queryKey string, m req.GetA8KeyRequestModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.GetA8KeyRequest(m.OpCode, m.Scene, m.ReqUrl, baseinfo.ThrIdGetA8Key) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // JSLoginService 小程序授权 func JsLoginService(queryKey string, m req.AppletModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.JSLoginRequest(m.AppId) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // JSOperateWxDataService 小程序云函数操作 func JSOperateWxDataService(queryKey string, m req.AppletModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.JSOperateWxDataRequest(m.AppId, m.Opt, m.Data) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } func SdkOauthAuthorizeService(queryKey string, m req.AppletModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SdkOauthAuthorizeRequest(m.AppId, m.SdkName, m.PackageName) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // 二维码授权登录 func QRConnectAuthorizeService(queryKey string, m req.QRConnectAuthorizeModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SendQRConnectAuthorize(m.QrUrl) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // 二维码授权登录确认 func QRConnectAuthorizeConfirmService(queryKey string, m req.QRConnectAuthorizeModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SendQRConnectAuthorizeConfirm(m.QrUrl) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // 授权链接 func GetMpA8Service(queryKey string, m req.GetMpA8KeyModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SendGetMpA8Request(m.Url, m.Opcode) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } // 授权公众号登录 func AuthMpLoginService(queryKey string, m req.GetMpA8KeyModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() m.Opcode = 2 resp, err := reqInvoker.SendGetMpA8Request(m.Url, m.Opcode) if err != nil { return vo.NewFail(err.Error()) } if resp.BaseResponse.GetRet() == 0 { if m.Scene == 0 { v, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) var passTicket = regexp.MustCompile(`var _pass_ticket = '([^']*)'`) var appmsgToken = regexp.MustCompile(`var _appmsg_token = '([^']*)'`) var qrticket = regexp.MustCompile(`var qrticket = '([^']*)'`) data := url.Values{} data.Add("uin", "777") data.Add("key", "777") data.Add("pass_ticket", passTicket.FindStringSubmatch(v)[1]) data.Add("appmsg_token", appmsgToken.FindStringSubmatch(v)[1]) data.Add("f", "json") data.Add("param", "qrticket") data.Add("qrticket", qrticket.FindStringSubmatch(v)[1]) rsp := utils.HttpPost("https://mp.weixin.qq.com/wap/loginauthqrcode?action=confirm", data, cookie) return vo.NewSuccessObj(rsp, "授权登录成功") } else if m.Scene == 1 { v, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) var ticket = regexp.MustCompile(`_ticket = "(.*?)",`) var uuid = regexp.MustCompile(`_uuid = "(.*?)",`) var passTicket = regexp.MustCompile(`_pass_ticket = "(.*?)",`) var appmsgToken = regexp.MustCompile(`_appmsg_token = "(.*?)",`) var msgId = regexp.MustCompile(`_msgid = "(.*?)",`) var secondOpenId = regexp.MustCompile(`_second_openid = "(.*?)",`) data := url.Values{} data.Add("ticket", ticket.FindStringSubmatch(v)[1]) data.Add("uuid", uuid.FindStringSubmatch(v)[1]) data.Add("action", "check") data.Add("uin", "777") data.Add("key", "777") data.Add("pass_ticket", passTicket.FindStringSubmatch(v)[1]) data.Add("appmsg_token", appmsgToken.FindStringSubmatch(v)[1]) data.Add("code", "invalid") data.Add("type", "bind_second_admin") data.Add("msgid", msgId.FindStringSubmatch(v)[1]) data.Add("second_openid", secondOpenId.FindStringSubmatch(v)[1]) data.Add("expire_time_type", "0") data.Add("allow", "1") rsp := utils.HttpPost("https://mp.weixin.qq.com/safe/safeconfirm_reply", data, cookie) return vo.NewSuccessObj(rsp, "授权验证成功") } else if m.Scene == 2 { v, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) var ticket = regexp.MustCompile(`_ticket = "(.*?)",`) var uuid = regexp.MustCompile(`_uuid = "(.*?)",`) var passTicket = regexp.MustCompile(`_pass_ticket = "(.*?)",`) var appmsgToken = regexp.MustCompile(`_appmsg_token = "(.*?)",`) var msgId = regexp.MustCompile(`_msgid = "(.*?)",`) data := url.Values{} data.Add("ticket", ticket.FindStringSubmatch(v)[1]) data.Add("uuid", uuid.FindStringSubmatch(v)[1]) data.Add("action", "check") data.Add("uin", "777") data.Add("key", "777") data.Add("pass_ticket", passTicket.FindStringSubmatch(v)[1]) data.Add("appmsg_token", appmsgToken.FindStringSubmatch(v)[1]) data.Add("code", "invalid") data.Add("type", "appkey") data.Add("msgid", msgId.FindStringSubmatch(v)[1]) data.Add("allow", "1") rsp := utils.HttpPost("https://mp.weixin.qq.com/safe/safeconfirm_reply", data, cookie) return vo.NewSuccessObj(rsp, "授权验证成功") } else if m.Scene == 3 { v, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) var passTicket = regexp.MustCompile(`pass_ticket: '(.*?)',`) var qrcheckTicket = regexp.MustCompile(`qrcheck_ticket: '(.*?)',`) var appmsgToken = regexp.MustCompile(`window.appmsg_token = "(.*?)";`) data := url.Values{} data.Add("f", "json") data.Add("action", "scan") data.Add("qrcheck_ticket", qrcheckTicket.FindStringSubmatch(v)[1]) data.Add("uin", "777") data.Add("key", "777") data.Add("pass_ticket", passTicket.FindStringSubmatch(v)[1]) data.Add("appmsg_token", appmsgToken.FindStringSubmatch(v)[1]) rsp := utils.HttpPost("https://mp.weixin.qq.com/wap/qrcheckoper", data, cookie) return vo.NewSuccessObj(rsp, "授权验证成功") } else if m.Scene == 4 { v, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) var passTicket = regexp.MustCompile(`pass_ticket: '(.*?)',`) var appmsgToken = regexp.MustCompile(`appmsg_token: '(.*?)',`) var qrcheckTicket = regexp.MustCompile(`qrcheck_ticket: '(.*?)',`) data := url.Values{} data.Add("f", "json") data.Add("action", "confirm") data.Add("operate_type", "1") data.Add("qrcheck_ticket", qrcheckTicket.FindStringSubmatch(v)[1]) data.Add("uin", "777") data.Add("key", "777") data.Add("pass_ticket", passTicket.FindStringSubmatch(v)[1]) data.Add("appmsg_token", appmsgToken.FindStringSubmatch(v)[1]) rsp := utils.HttpPost("https://mp.weixin.qq.com/mp/wapsafeqrcode", data, cookie) return vo.NewSuccessObj(rsp, "授权验证成功") } } return vo.NewSuccessObj(resp, "失败") }) } // 获取公众号历史消息 func GetAppletHistoryMsg(queryKey string, m req.GetMpHistoryMsgModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() var Opcode = uint32(2) // resp, err := reqInvoker.SendQWAcceptChatRoomRequest(m.Url, Opcode) if err != nil { return vo.NewFail(err.Error()) } if resp.BaseResponse.GetRet() == 0 { rsp := utils.HttpGet(resp.GetFullURL(), resp.HttpHeader) return vo.NewSuccessObj(rsp, "成功") } return vo.NewSuccessObj(resp, "失败") }) } // GetAppMsgExtService 阅读公众号文章 func GetAppMsgExtService(queryKey string, m req.ReadParam) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() var Opcode = uint32(2) resp, err := reqInvoker.SendGetMpA8Request(m.Url, Opcode) if err != nil { return vo.NewFail(err.Error()) } if resp.BaseResponse.GetRet() != 0 { return vo.NewFail("获取文章信息失败") } // 解析 urlObj, err := url.Parse(resp.GetFullURL()) if err != nil { return vo.NewFail(fmt.Sprintf("解析URL异常:%v", err.Error())) } query := urlObj.Query() if _, ok := query["pass_ticket"]; !ok { return vo.NewFail("获取pass_ticket失败") } // 获取HTML页面和Cookie _, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) // 构建阅读请求 postUrl := "https://mp.weixin.qq.com/mp/getappmsgext?f=json&mock=&fasttmplajax=1&f=json&uin=&key=&pass_ticket=" + query["pass_ticket"][0] + "&wxtoken=&devicetype=iOS13.3.1&clientversion=17000c2d&__biz=" + url.QueryEscape(query["__biz"][0]) + "&appmsg_token=&x5=0&f=json&wx_header=1&pass_ticket=" + query["pass_ticket"][0] postData := url.Values{} postData.Add("r", "") postData.Add("__biz", query["__biz"][0]) postData.Add("appmsg_type", "9") postData.Add("mid", query["mid"][0]) postData.Add("sn", query["sn"][0]) postData.Add("idx", query["idx"][0]) postData.Add("scene", "126") postData.Add("title", "") postData.Add("ct", time.Now().String()) postData.Add("abtest_cookie", "") postData.Add("devicetype", "iOS13.3.1") postData.Add("version", "17000c2d") postData.Add("is_need_ticket", "0") postData.Add("is_need_ad", "0") postData.Add("comment_id", "") postData.Add("is_need_reward", "0") postData.Add("both_ad", "0") postData.Add("reward_uin_count", "0") postData.Add("send_time", "") postData.Add("msg_daily_idx", "1") postData.Add("is_original", "0") postData.Add("is_only_read", "1") postData.Add("req_id", "") postData.Add("pass_ticket", query["pass_ticket"][0]) postData.Add("is_temp_url", "0") postData.Add("item_show_type", "0") postData.Add("tmp_version", "1") postData.Add("more_read_type", "0") postData.Add("appmsg_like_type", "2") postData.Add("related_video_sn", "") postData.Add("vid", "") postData.Add("is_pay_subscribe", "0") postData.Add("pay_subscribe_uin_count", "0") postData.Add("has_red_packet_cover", "0") postData.Add("album_id", "") result := utils.HttpPost(postUrl, postData, cookie) return vo.NewSuccessObj(result, "阅读成功") }) } // GetAppMsgExtLikeService 点赞公众号文章 func GetAppMsgExtLikeService(queryKey string, m req.ReadParam) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() var Opcode = uint32(2) resp, err := reqInvoker.SendGetMpA8Request(m.Url, Opcode) if err != nil { return vo.NewFail(err.Error()) } if resp.BaseResponse.GetRet() != 0 { return vo.NewFail("获取文章信息失败") } // 解析 urlObj, err := url.Parse(resp.GetFullURL()) if err != nil { return vo.NewFail(fmt.Sprintf("解析URL异常:%v", err.Error())) } query := urlObj.Query() if _, ok := query["pass_ticket"]; !ok { return vo.NewFail("获取pass_ticket失败") } // 获取HTML页面和Cookie bodyStr, cookie := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) // 提取appmsg_token,用正则表达式 var appmsgToken string reAppmsgToken := regexp.MustCompile(`var appmsg_token = ["'](.*?)["']`) appmsgTokenMatches := reAppmsgToken.FindStringSubmatch(bodyStr) if len(appmsgTokenMatches) > 1 { appmsgToken = appmsgTokenMatches[1] } else { // 尝试另一种格式 reAppmsgToken = regexp.MustCompile(`appmsg_token="(.*?)"`) appmsgTokenMatches = reAppmsgToken.FindStringSubmatch(bodyStr) if len(appmsgTokenMatches) > 1 { appmsgToken = appmsgTokenMatches[1] } } // 构建阅读请求 (和阅读一样的请求,为了保证点赞成功) postUrl := "https://mp.weixin.qq.com/mp/getappmsgext?f=json&mock=&fasttmplajax=1&f=json&uin=&key=&pass_ticket=" + query["pass_ticket"][0] + "&wxtoken=&devicetype=iOS13.3.1&clientversion=17000c2d&__biz=" + url.QueryEscape(query["__biz"][0]) + "&appmsg_token=&x5=0&f=json&wx_header=1&pass_ticket=" + query["pass_ticket"][0] postData := url.Values{} postData.Add("r", "") postData.Add("__biz", query["__biz"][0]) postData.Add("appmsg_type", "9") postData.Add("mid", query["mid"][0]) postData.Add("sn", query["sn"][0]) postData.Add("idx", query["idx"][0]) postData.Add("scene", "126") postData.Add("title", "") postData.Add("ct", time.Now().String()) postData.Add("abtest_cookie", "") postData.Add("devicetype", "iOS13.3.1") postData.Add("version", strconv.FormatInt(int64(baseinfo.ClientVersion), 16)) postData.Add("is_need_ticket", "0") postData.Add("is_need_ad", "0") postData.Add("comment_id", "") postData.Add("is_need_reward", "0") postData.Add("both_ad", "0") postData.Add("reward_uin_count", "0") postData.Add("send_time", "") postData.Add("msg_daily_idx", "1") postData.Add("is_original", "0") postData.Add("is_only_read", "1") postData.Add("req_id", "") postData.Add("pass_ticket", query["pass_ticket"][0]) postData.Add("is_temp_url", "0") postData.Add("item_show_type", "0") postData.Add("tmp_version", "1") postData.Add("more_read_type", "0") postData.Add("appmsg_like_type", "2") postData.Add("related_video_sn", "") postData.Add("vid", "") postData.Add("is_pay_subscribe", "0") postData.Add("pay_subscribe_uin_count", "0") postData.Add("has_red_packet_cover", "0") postData.Add("album_id", "") result := utils.HttpPost(postUrl, postData, cookie) // 提取appmsgid,用正则表达式 reAppmsgid := regexp.MustCompile(`var appmsgid = ['"+][0-9]*['"+] \|\| ['"+][0-9]*['"+] \|\| ['"+][0-9]*['"+]`) appmsgidStr := reAppmsgid.FindString(bodyStr) reDigits := regexp.MustCompile(`[0-9]+`) appmsgid := reDigits.FindString(appmsgidStr) if appmsgid == "" { return vo.NewFail("获取文章appmsgid失败") } // 构建点赞请求 likeUrl := "https://mp.weixin.qq.com/mp/appmsg_like?__biz=" + url.QueryEscape(query["__biz"][0]) + "&mid=" + query["mid"][0] + "&idx=" + query["idx"][0] + "&like=1&f=json&appmsgid=" + appmsgid + "&itemidx=&fasttmplajax=1&f=json&uin=777&key=777&pass_ticket=" + query["pass_ticket"][0] + "&wxtoken=777&devicetype=iOS13.3.1&clientversion=" + strconv.FormatInt(int64(baseinfo.ClientVersion), 16) + "&appmsg_token=" + appmsgToken + "&x5=0&f=json&wx_header=1" // 创建自定义header,确保Cookie正确传递 header := make(map[string]string) header["Cookie"] = strings.Join(cookie, "; ") header["Content-Type"] = "application/x-www-form-urlencoded" header["User-Agent"] = "Mozilla/5.0 (iPad; CPU OS 12_4_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.15(0x17000f31) NetType/WIFI Language/zh_CN" header["Referer"] = resp.GetFullURL() header["Origin"] = "https://mp.weixin.qq.com" header["X-Requested-With"] = "XMLHttpRequest" likeData := url.Values{} likeData.Add("scene", "142") likeData.Add("appmsg_like_type", "1") likeData.Add("item_show_type", "0") likeData.Add("client_version", strconv.FormatInt(int64(baseinfo.ClientVersion), 16)) likeData.Add("is_temp_url", "0") likeData.Add("style", "1") likeData.Add("exptype", "") likeData.Add("expsessionid", "") likeData.Add("request_id", time.Now().Format("20060102150405")+strconv.Itoa(int(time.Now().UnixNano()%1000))) likeData.Add("device_type", "iOS13.3.1") likeData.Add("appmsg_token", appmsgToken) // 使用从页面提取的appmsg_token // 使用自定义的HTTP请求函数 req, err := http.NewRequest("POST", likeUrl, strings.NewReader(likeData.Encode())) if err != nil { return vo.NewFail(fmt.Sprintf("创建请求失败:%v", err.Error())) } for k, v := range header { req.Header.Set(k, v) } // 添加更多调试信息 fmt.Printf("点赞请求URL: %s\n", likeUrl) fmt.Printf("点赞请求参数: %s\n", likeData.Encode()) fmt.Printf("点赞请求Cookie: %s\n", header["Cookie"]) fmt.Printf("点赞请求appmsg_token: %s\n", appmsgToken) client := &http.Client{} httpResp, err := client.Do(req) var likeResult string if err != nil { likeResult = fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) } else { defer httpResp.Body.Close() respBody, _ := ioutil.ReadAll(httpResp.Body) likeResult = string(respBody) fmt.Printf("点赞响应: %s\n", likeResult) } return vo.NewSuccessObj(map[string]interface{}{ "read": result, "like": likeResult, }, "点赞成功") }) } // GetAppMsgReadCountService 获取公众号文章阅读数 func GetAppMsgReadCountService(queryKey string, m req.ReadParam) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() var Opcode = uint32(2) resp, err := reqInvoker.SendGetMpA8Request(m.Url, Opcode) if err != nil { return vo.NewFail(err.Error()) } if resp.BaseResponse.GetRet() != 0 { return vo.NewFail("获取文章信息失败") } // 获取HTML页面内容 htmlContent, _ := utils.GetHTML(resp.GetFullURL(), resp.HttpHeader) // 用正则表达式提取阅读数 readNumRegex := regexp.MustCompile(`var read_num_new = '(\d+)' \* 1;`) readNumMatches := readNumRegex.FindStringSubmatch(htmlContent) if len(readNumMatches) < 2 { return vo.NewFail("未找到阅读数信息") } readCount := readNumMatches[1] fullURL := resp.GetFullURL() for _, h := range resp.HttpHeader { if h.GetName() == "X-WECHAT-UIN" { fullURL += "&uin=" + h.GetKey() + "&countrycode=CN" } else if h.GetName() == "X-WECHAT-ACCTMODE" { fullURL += "&acctmode=" + h.GetKey() } else if h.GetName() == "X-WECHAT-KEY" { fullURL += "&key=" + h.GetKey() } } result := map[string]string{ "read_count": readCount, "article_url": fullURL, } return vo.NewSuccessObj(result, "获取阅读数成功") }) } // ChannelsLoginService 视频号助手扫码登录 func ChannelsLoginService(queryKey string, m req.ChannelsLoginModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() resp1, err := reqInvoker.GetA8KeyRequest(2, 4, m.Url, baseinfo.ThrIdGetA8Key) if err != nil { return vo.NewFail("第一步失败: " + err.Error()) } if resp1.BaseResponse.GetRet() != 0 { return vo.NewFail(fmt.Sprintf("第一步失败, ret=%d, errMsg=%s", resp1.BaseResponse.GetRet(), resp1.BaseResponse.GetErrMsg().GetStr())) } fullURL := resp1.GetFullURL() if fullURL == "" { return vo.NewFail("第一步成功但未返回完整 URL") } parsedURL, err := url.Parse(fullURL) if err != nil { return vo.NewFail("解析 URL 失败: " + err.Error()) } query := parsedURL.Query() token := query.Get("token") exportkey := query.Get("exportkey") rid := fmt.Sprintf("%x-%x", time.Now().UnixNano()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFFFFFF) scanURL := fmt.Sprintf("https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/auth/scan-qrcode?token=%s&exportKey=%s&_rid=%s&exportkey=%s&_pageUrl=https://channels.weixin.qq.com/mobile/confirm_login.html", url.QueryEscape(token), url.QueryEscape(exportkey), rid, url.QueryEscape(exportkey)) scanData := map[string]string{ "_rid": rid, "exportkey": exportkey, "_pageUrl": "https://channels.weixin.qq.com/mobile/confirm_login.html", } scanDataJSON, _ := json.Marshal(scanData) scanResp := utils.HttpPostJSON(scanURL, string(scanDataJSON), nil) // 解析 scan-qrcode 响应 var scanResult map[string]interface{} if err := json.Unmarshal([]byte(scanResp), &scanResult); err != nil { return vo.NewFail("解析 scan-qrcode 响应失败: " + err.Error()) } if errCode, ok := scanResult["errCode"].(float64); !ok || errCode != 0 { return vo.NewFail(fmt.Sprintf("scan-qrcode 失败: %v", scanResult)) } data, ok := scanResult["data"].(map[string]interface{}) if !ok { return vo.NewFail("scan-qrcode 响应格式错误") } finderList, ok := data["finderList"].([]interface{}) if !ok || len(finderList) == 0 { return vo.NewFail("未找到 finderList") } var selectedFinder map[string]interface{} if m.FinderUsername != "" && m.FinderUsername != "string" { found := false for _, finder := range finderList { finderMap, ok := finder.(map[string]interface{}) if !ok { continue } if username, ok := finderMap["finderUsername"].(string); ok && username == m.FinderUsername { selectedFinder = finderMap found = true break } } if !found { return vo.NewFail(fmt.Sprintf("未找到指定的 finderUsername: %s", m.FinderUsername)) } } else { // 如果未指定或为默认值,选择第一个 firstFinder, ok := finderList[0].(map[string]interface{}) if !ok { return vo.NewFail("finderList 格式错误") } selectedFinder = firstFinder } finderUsername, ok := selectedFinder["finderUsername"].(string) if !ok { return vo.NewFail("未找到 finderUsername") } rid2 := fmt.Sprintf("%x-%x", time.Now().UnixNano()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFFFFFF) authURL := fmt.Sprintf("https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/auth/finder-handle-auth?token=%s&exportKey=%s&action=0&finderUsername=%s&_rid=%s&exportkey=%s&_pageUrl=https://channels.weixin.qq.com/mobile/confirm_login.html", url.QueryEscape(token), url.QueryEscape(exportkey), url.QueryEscape(finderUsername), rid2, url.QueryEscape(exportkey)) authData := map[string]string{ "_rid": rid2, "exportkey": exportkey, "_pageUrl": "https://channels.weixin.qq.com/mobile/confirm_login.html", } authDataJSON, _ := json.Marshal(authData) authResp := utils.HttpPostJSON(authURL, string(authDataJSON), nil) var authResult map[string]interface{} if err := json.Unmarshal([]byte(authResp), &authResult); err != nil { return vo.NewFail("解析 finder-handle-auth 响应失败: " + err.Error()) } if errCode, ok := authResult["errCode"].(float64); !ok || errCode != 0 { return vo.NewFail(fmt.Sprintf("finder-handle-auth 失败: %v", authResult)) } result := map[string]interface{}{ "scan_qrcode": scanResult, "handle_auth": authResult, "full_url": fullURL, "finder_username": finderUsername, } return vo.NewSuccessObj(result, "视频号助手登录成功") }) } // ShopLoginConfirmService 微信小店确认登录 func ShopLoginConfirmService(queryKey string, m req.ShopLoginConfirmModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { //取基本信息 wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() //判断在线情况 if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } //获取请求管理器 reqInvoker := connect.GetWXReqInvoker() // 步骤1: 调用GetA8Key获取授权 (Scene=4) // 这一步建立授权会话 resp1, err := reqInvoker.GetA8KeyRequest(2, 4, m.Url, baseinfo.ThrIdGetA8Key) if err != nil { return vo.NewFail("GetA8Key失败: " + err.Error()) } if resp1.BaseResponse.GetRet() != 0 { return vo.NewFail(fmt.Sprintf("GetA8Key失败, ret=%d, errMsg=%s", resp1.BaseResponse.GetRet(), resp1.BaseResponse.GetErrMsg().GetStr())) } // 步骤2: 调用CGI 8887模拟扫码事件上报 // 这一步模拟用户扫码行为,告诉服务器我们已经"扫码"了 _, err = reqInvoker.SendScanQrcodeEventReportRequest(resp1.GetFullURL()) if err != nil { return vo.NewFail("扫码事件上报失败: " + err.Error()) } // // 步骤3: 调用CGI 971获取确认信息 (包含6500+字节的token/session数据) resp971, err := reqInvoker.SendExtDeviceLoginConfirmGetRequest(resp1.GetFullURL()) if err != nil { return vo.NewFail("获取确认信息失败: " + err.Error()) } if resp971.BaseResponse.GetRet() != 0 { return vo.NewFail(fmt.Sprintf("获取确认信息失败, ret=%d, errMsg=%s", resp971.BaseResponse.GetRet(), resp971.BaseResponse.GetErrMsg().GetStr())) } // 步骤4: 调用CGI 972确认登录 resp, err := reqInvoker.SendExtDeviceLoginConfirmOkRequest(resp1.GetFullURL()) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } func ShopLoginConfirmGetService(queryKey string, m req.ShopLoginConfirmModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } reqInvoker := connect.GetWXReqInvoker() url := strings.TrimSpace(m.Url) resp971, err := reqInvoker.SendExtDeviceLoginConfirmGetRequest(url) if err == nil && resp971.BaseResponse.GetRet() == 0 { return vo.NewSuccessObj(resp971, "") } // resp1, err := reqInvoker.GetA8KeyRequest(2, 4, url, baseinfo.ThrIdGetA8Key) // if err != nil { // return vo.NewFail("GetA8Key失败: " + err.Error()) // } // if resp1.BaseResponse.GetRet() != 0 { // return vo.NewFail(fmt.Sprintf("GetA8Key失败, ret=%d, errMsg=%s", resp1.BaseResponse.GetRet(), resp1.BaseResponse.GetErrMsg().GetStr())) // // } // _, _ = reqInvoker.SendScanQrcodeEventReportRequest(resp1.GetFullURL()) // resp971, err = reqInvoker.SendExtDeviceLoginConfirmGetRequest(resp1.GetFullURL()) if err != nil { return vo.NewFail("获取确认信息失败: " + err.Error()) } return vo.NewSuccessObj(resp971, "") }) } func ShopLoginConfirmOkService(queryKey string, m req.ShopLoginConfirmModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SendExtDeviceLoginConfirmOkRequest(m.Url) if err != nil { return vo.NewFail(err.Error()) } return vo.NewSuccessObj(resp, "") }) } func ScanQrcodeEventReportService(queryKey string, m req.ShopLoginConfirmModel) vo.DTO { return checkExIdPerformNoCreateConnect(queryKey, func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO { wxAccount := connect.GetWXAccount() loginState := wxAccount.GetLoginState() if loginState == baseinfo.MMLoginStateNoLogin { return vo.NewFail("该账号需要重新登录!loginState == MMLoginStateNoLogin ") } else if !connect.CheckOnLineStatus() { return vo.NewFail("账号离线,自动上线失败!loginState == " + strconv.Itoa(int(wxAccount.GetLoginState()))) } if m.Url == "" || m.Url == "undefined" || m.Url == "null" { return vo.NewFail("url不能为空") } reqInvoker := connect.GetWXReqInvoker() resp, err := reqInvoker.SendScanQrcodeEventReportRequest(m.Url) if err != nil { return vo.NewFail("扫码事件上报失败: " + err.Error()) } return vo.NewSuccessObj(resp, "") }) }