Files
2026-02-17 13:06:23 +08:00

920 lines
35 KiB
Go

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, "")
})
}