Files
wechat_ipad_pro/api/service/appletService.go
2026-02-17 13:06:23 +08:00

920 lines
35 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, "")
})
}