first commit

This commit is contained in:
2026-02-17 13:06:23 +08:00
commit 7cbd3d061d
349 changed files with 126558 additions and 0 deletions

27
srv/defines/enums.go Normal file
View File

@@ -0,0 +1,27 @@
package defines
const (
// EGroupTaskStateIdel 群聊任务状态:空闲
EGroupTaskStateIdel uint32 = 0
// EGroupTaskStateSave 群聊任务状态:保存群聊
EGroupTaskStateSave uint32 = 1
// EGroupTaskStateUnSave 群聊任务状态:取消保存群聊
EGroupTaskStateUnSave uint32 = 2
// EGroupTaskStateDownQrcode 群聊任务状态:下载群二维码
EGroupTaskStateDownQrcode uint32 = 3
)
const (
// MTaskTypeFavTrans 收藏转发
MTaskTypeFavTrans uint32 = 0
// MTaskTypeSyncTrans 同步转发
MTaskTypeSyncTrans uint32 = 0
)
const (
// MFavTransShieldLabelName 收藏转发屏蔽标签名
MFavTransShieldLabelName string = "收藏转发屏蔽"
// MSyncTransShieldLabelName 同步转发屏蔽标签名
MSyncTransShieldLabelName string = "同步转发屏蔽"
)

View File

@@ -0,0 +1,151 @@
package srvconfig
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"regexp"
"strings"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"github.com/gogf/gf/database/gredis"
)
// GlobalSetting 全局设置
var GlobalSetting Setting
// TaskExecWaitTimes 任务执行间隔时间 500毫秒
var TaskExecWaitTimes = uint32(500)
// Setting 设置
type Setting struct {
Debug bool `json:"debug"`
Host string `json:"host"`
Port string `json:"port"`
ApiVersion string `json:"apiVersion"`
OpenClearMsg bool `json:"openClearMsg"`
SycnTargetIpUrl string `json:"sycnTargetIpUrl"` // 同步外网IP地址的url
WorkerPoolSize uint32 `json:"workerpoolsize"`
MaxWorkerTaskLen uint32 `json:"maxworkertasklen"`
WebDomain string `json:"webdomain"`
WebDomainNum string `json:"webdomainnum"`
SyncOnlineCount string `json:"syncOnlineCount"`
WebTaskName string `json:"webtaskname"`
WebTaskAppNumber string `json:"webtaskappnumber"`
//Redis
RedisConfig gredis.Config `json:"redisConfig"`
//消息同步 是否按微信id 发布消息
NewsSynWxId bool `json:"newsSynWxId"`
Dt bool `json:"dt"`
MysqlConnectStr string `json:"mySqlConnectStr"`
InitializeMySQLTables bool `json:"initializeMySQLTables"`
UserInputKey string `json:"userInputKey"`
Version string `json:"version"`
HttpSyncMsg bool `json:"httpSyncMsg"`
IsInitOld bool `json:"isInitOld"`
WsSyncMsg bool `json:"wsSyncMsg"`
SyncHisyory bool `json:"syncHisyory"`
LoginBeforeRequestUrl string `json:"loginBeforeRequestUrl"` // 登录前会请求该 url二维码和唤醒
// 当前服务器外网IP地址
TargetIp string `json:"targetIp"`
// 当前 HTTP-Api 服务的本地地址 基址
LocalApiUrl string
// 推广关注自己的微信公众号(新用户首次登录时关注)
GhWxid string `json:"ghWxid"`
// 管理接口授权 ADMIN_KEY
AdminKey string `json:"adminKey"`
// 是否开启 mac2ipad
Mac2Ipad bool `json:"mac2ipad"`
// 是否开启 car2ipad
Car2Ipad bool `json:"car2ipad"`
}
// getExternal 请求获取外网ip
func getExternal() string {
resp, err := http.Get(GlobalSetting.SycnTargetIpUrl)
if err != nil {
fmt.Printf("获取 ip 失败 -> 退出程序!---->")
os.Exit(1)
// return fmt.Sprintf("%s:%s", GlobalSetting.Host, GlobalSetting.Port)
}
defer resp.Body.Close()
content, _ := ioutil.ReadAll(resp.Body)
if content == nil {
fmt.Printf("获取 ip 失败 -> 退出程序!---->")
os.Exit(1)
// return fmt.Sprintf("%s:%s", GlobalSetting.Host, GlobalSetting.Port)
}
// 校验一下是不是 ip
if net.ParseIP(string(content)) == nil {
fmt.Printf("获取 ip 失败 -> 退出程序!---->")
os.Exit(1)
}
fmt.Printf("获取 ip 成功 -> %s ----> %s\n", content, GlobalSetting.SycnTargetIpUrl)
return fmt.Sprintf("%s:%s", content, GlobalSetting.Port)
}
func isValidWeChatID(wxid string) bool {
if strings.TrimSpace(wxid) == "" {
GlobalSetting.GhWxid = ""
return true
}
regex := `^[a-zA-Z][a-zA-Z0-9_-]{5,19}$`
r := regexp.MustCompile(regex)
return r.MatchString(wxid)
}
func ConfigSetUp() {
tmpHomeDir, err := os.Getwd()
if err != nil {
baseutils.PrintLog(err.Error())
return
}
baseinfo.HomeDIR = tmpHomeDir
// 读取配置文件
configData, err := baseutils.ReadFile(baseinfo.HomeDIR + "/assets/setting.json")
if err != nil {
return
}
err = json.Unmarshal(configData, &GlobalSetting)
if err != nil {
fmt.Println(err)
return
}
if len(strings.TrimSpace(GlobalSetting.AdminKey)) == 0 {
// 默认授权admin_key
GlobalSetting.AdminKey = "df704bd7012548049e68db25aff0507f"
}
fmt.Println("======== ADMIN_KEY ===", GlobalSetting.AdminKey, "========")
if !isValidWeChatID(GlobalSetting.GhWxid) {
fmt.Println("引流公众号(assets/setting.json ghWxid)不合法:", GlobalSetting.GhWxid, ";已重设为空")
GlobalSetting.GhWxid = ""
}
// 确保API版本格式正确以斜杠开头
if GlobalSetting.ApiVersion != "" && !strings.HasPrefix(GlobalSetting.ApiVersion, "/") {
GlobalSetting.ApiVersion = "/" + GlobalSetting.ApiVersion
}
if GlobalSetting.ApiVersion == "/" {
GlobalSetting.ApiVersion = ""
}
// 如果ApiVersion不为空且不以斜杠结尾则添加斜杠
if GlobalSetting.ApiVersion != "" && !strings.HasSuffix(GlobalSetting.ApiVersion, "/") {
GlobalSetting.ApiVersion += "/"
}
GlobalSetting.LocalApiUrl = fmt.Sprintf("http://127.0.0.1:%s%s", GlobalSetting.Port, GlobalSetting.ApiVersion)
// 获取当前服务器外网IP地址
// 如果没有指定 TargetIp, 同时 SycnTargetIpUrl 不为空, 则请求 SycnTargetIpUrl 获取外网 IP
if GlobalSetting.TargetIp == "" && GlobalSetting.SycnTargetIpUrl != "" {
GlobalSetting.TargetIp = getExternal()
}
}

48
srv/websrv/define.go Normal file
View File

@@ -0,0 +1,48 @@
package websrv
import "xiawan/wx/clientsdk/baseinfo"
const (
// TaskStateLoginSuccess 登录成功状态
TaskStateLoginSuccess = string("2")
// TaskStateLogout 退出登录
TaskStateLogout = string("6")
// TaskStateCheckLoginQrcodeSuccess 检测状态
TaskStateCheckLoginQrcodeSuccess = string("1")
// TaskStateCheckLoginQrcodeStartLogin 用户点击了登录
TaskStateCheckLoginQrcodeStartLogin = string("7")
// TaskStateMismatching 账号不匹配
TaskStateMismatching = string("9")
// TaskTypeUploadStatus 上传状态
TaskTypeUploadStatus uint32 = 1
// TaskTypeReportHeart 发送心跳包
TaskTypeReportHeart uint32 = 2
)
// TaskInfo 任务信息
type TaskInfo struct {
UUID string
TaskID string
Name string
AppNumber string
Account string
SignKey string
}
// UploadTaskStatusResp 上传状态响应
type UploadTaskStatusResp struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data string `json:"data"`
}
// WebTask web任务
type WebTask struct {
UserInfo *baseinfo.UserInfo
TaskInfo *TaskInfo
ConnectInfo map[string]interface{}
Status string
Type uint32
Count uint32
}

51
srv/websrv/httptool.go Normal file
View File

@@ -0,0 +1,51 @@
package websrv
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)
// TaskPost 短连接发送数据
func TaskPost(uri string, data []byte) ([]byte, error) {
client := &http.Client{}
request, err := http.NewRequest("POST", uri, bytes.NewReader(data))
if err != nil {
return nil, err
}
request.Header.Add("UserAgent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)")
request.Header.Add("Accept", "*/*")
request.Header.Add("content-type", "application/x-www-form-urlencoded")
resp, err := client.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return []byte(body), nil
}
// TaskPost 短连接发送数据
func TaskPostJson(uri string, data []byte) ([]byte, error) {
client := &http.Client{}
request, err := http.NewRequest("POST", uri, bytes.NewReader(data))
if err != nil {
return nil, err
}
request.Header.Add("content-type", "application/json")
resp, err := client.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
fmt.Println("短连接发送数据", uri,string(data),string(body))
return []byte(body), nil
}

112
srv/websrv/taskmgr.go Normal file
View File

@@ -0,0 +1,112 @@
package websrv
import (
"errors"
"sync"
"xiawan/wx/clientsdk/baseinfo"
)
// WebTaskMgr web任务管理器
type WebTaskMgr struct {
// wxConn wxface.IWXConnect
taskChan chan *WebTask
endChan chan bool
isStart bool
}
// 全局只能定义一个
var WebTaskMgrInit *WebTaskMgr = &WebTaskMgr{
taskChan: make(chan *WebTask, 100),
endChan: make(chan bool, 1),
isStart: false,
}
var globalLock sync.Mutex = sync.Mutex{}
// NewWebTaskMgr 新建web任务管理器
func NewWebTaskMgr() *WebTaskMgr {
globalLock.Lock()
defer globalLock.Unlock()
return WebTaskMgrInit
}
// Start 开启
func (wtm *WebTaskMgr) Start() {
globalLock.Lock()
defer globalLock.Unlock()
if wtm.isStart {
return
}
wtm.isStart = true
go wtm.queryAndExecuteTask()
}
// Stop 关闭
func (wtm *WebTaskMgr) Stop() {
wtm.isStart = false
wtm.endChan <- true
}
// AddWebTask 新增Web任务
func (wtm *WebTaskMgr) retSetWebTask(webTask *WebTask) {
// 最多三次
if webTask.Count >= 2 {
return
}
webTask.Count++
wtm.taskChan <- webTask
}
// AddWebTask 新增Web任务
func (wtm *WebTaskMgr) AddWebTask(userInfo *baseinfo.UserInfo, taskInfo *TaskInfo, status string, tmpType uint32, connectInfo map[string]interface{}) {
if !wtm.isStart {
return
}
globalLock.Lock()
defer globalLock.Unlock()
newWebTask := &WebTask{}
newWebTask.ConnectInfo = connectInfo
newWebTask.TaskInfo = taskInfo
newWebTask.UserInfo = userInfo
newWebTask.Status = status
newWebTask.Type = tmpType
newWebTask.Count = 0
wtm.taskChan <- newWebTask
}
func (wtm *WebTaskMgr) queryAndExecuteTask() {
for {
select {
case webTask := <-wtm.taskChan:
if webTask.Type == TaskTypeUploadStatus {
err := errors.New("")
// err := UploadTaskStatus(webTask)
// if err != nil && webTask.Status != TaskStateCheckLoginQrcodeSuccess {
// wtm.retSetWebTask(webTask)
// continue
// }
// 登陆成功上报
if webTask.Status == TaskStateLoginSuccess {
err = ReportWechatStatus(webTask, "1")
if err != nil {
wtm.retSetWebTask(webTask)
continue
}
}
// 退出登陆上报
if webTask.Status == TaskStateLogout {
err = ReportWechatStatus(webTask, "0")
if err != nil {
wtm.retSetWebTask(webTask)
continue
}
}
} else if webTask.Type == TaskTypeReportHeart {
// 上报心跳包
}
continue
case <-wtm.endChan:
return
}
}
}

126
srv/websrv/tasktool.go Normal file
View File

@@ -0,0 +1,126 @@
package websrv
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"time"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/srv/srvconfig"
)
func createParamData(paramMap map[string]string) []byte {
retString := ""
for key, value := range paramMap {
retString = retString + key + "=" + value + "&"
}
retBytes := []byte(retString)
count := len(retBytes)
retBytes = retBytes[0 : count-1]
return retBytes
}
func getBaseParamMap(taskInfo *TaskInfo) map[string]string {
retMap := make(map[string]string)
curTime := strconv.Itoa(int(time.Now().UnixNano() / 1000000000))
head := curTime + taskInfo.SignKey
hsWebTime := curTime + baseutils.RandomBigHexString(20)
apiKey := baseutils.Md5Value(head)
retMap["name"] = taskInfo.Name
retMap["app_number"] = taskInfo.AppNumber
retMap["apikey"] = apiKey
retMap["hswebtime"] = hsWebTime
retMap["time"] = curTime
return retMap
}
// UploadTaskStatus 上传任务状态
func UploadTaskStatus(webTask *WebTask) error {
baseParamMap := getBaseParamMap(webTask.TaskInfo)
baseParamMap["name"] = webTask.UserInfo.NickName
baseParamMap["task_id"] = webTask.TaskInfo.TaskID
baseParamMap["account"] = webTask.UserInfo.WxId
baseParamMap["headimg"] = webTask.UserInfo.HeadURL
baseParamMap["status"] = webTask.Status
if webTask.Status == TaskStateLoginSuccess {
tmpToken := base64.StdEncoding.EncodeToString(webTask.UserInfo.AutoAuthKey)
baseParamMap["token"] = tmpToken
baseParamMap["data"] = ""
baseParamMap["autoauth"] = "data=" + "token=" + tmpToken
}
paramBytes := createParamData(baseParamMap)
respData, err := TaskPost("http://"+srvconfig.GlobalSetting.WebDomain+"/statistic/Wechat/upd.html", paramBytes)
if err != nil {
baseutils.PrintLog(err.Error())
return err
}
resp := &UploadTaskStatusResp{}
err = json.Unmarshal(respData, resp)
if err != nil {
return err
}
// 如果显示44
if resp.Code == 44 {
return nil
}
return nil
}
// ReportWechatStatus 上报状态
func ReportWechatStatus(webTask *WebTask, tmpType string) error {
baseParamMap := getBaseParamMap(webTask.TaskInfo)
baseParamMap["name"] = webTask.UserInfo.NickName
baseParamMap["uuid"] = webTask.UserInfo.UUID
baseParamMap["task_id"] = webTask.TaskInfo.TaskID
baseParamMap["account"] = webTask.UserInfo.WxId
baseParamMap["task_type"] = "2"
baseParamMap["service"] = srvconfig.GlobalSetting.WebDomainNum
baseParamMap["type"] = tmpType
connectInfo := map[string]interface{}{}
connectInfo["info"] = baseParamMap
// 复制所有能够被序列化的字段
for key, value := range webTask.ConnectInfo {
switch v := value.(type) {
case func(): // 这里检查并排除不支持的字段
fmt.Println("Skipping function:", key)
default:
// 判断是否是基本类型
if reflect.TypeOf(v).Kind() == reflect.String || reflect.TypeOf(v).Kind() == reflect.Int || reflect.TypeOf(v).Kind() == reflect.Bool {
connectInfo[key] = v
}
}
}
// 将参数转换为 JSON 格式
paramBytes, err := json.Marshal(connectInfo)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return err
}
if srvconfig.GlobalSetting.WebDomain == "" {
return nil
}
respData, err := TaskPostJson("http://"+srvconfig.GlobalSetting.WebDomain+"/statistic/Wechat/reportStatus", paramBytes)
if err != nil {
baseutils.PrintLog(err.Error())
return err
}
resp := &UploadTaskStatusResp{}
err = json.Unmarshal(respData, resp)
if err != nil {
return err
}
// 上报错误
if resp.Code == 44 {
return errors.New("上报错误")
}
return nil
}

319
srv/wxaccount.go Normal file
View File

@@ -0,0 +1,319 @@
package srv
import (
"strconv"
"strings"
"sync"
"xiawan/wx/srv/websrv"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/proxynet"
"xiawan/wx/protobuf/wechat"
)
// WXAccount 代表微信帐号
type WXAccount struct {
userInfo *baseinfo.UserInfo
// 好友列表
FriendMap map[string]*wechat.ModContact
// 公众号列表
ghMap map[string]*wechat.ModContact
// 群map
groupMap map[string]*wechat.ModContact
// 标签列表
labelMap map[uint32]*wechat.LabelPair
// 配置文件信息
userProfile *wechat.GetProfileResponse
// 登录状态
loginState uint32
// 刚刚扫码登陆要初始化
bInitializing bool
// 任务信息
taskInfo *websrv.TaskInfo
friendMapLock sync.RWMutex //读写连接的读写锁
ghMapLock sync.RWMutex //读写连接的读写锁
groupMapLock sync.RWMutex //读写连接的读写锁
labelMapLock sync.RWMutex //读写连接的读写锁
}
// NewWXAccount 生成一个新的账户
func NewWXAccount(taskInfo *websrv.TaskInfo, proxyInfo *proxynet.WXProxyInfo, ClientVersion uint32, dbUserInfo *baseinfo.UserInfo) *WXAccount {
if !(int(ClientVersion) > 0) {
ClientVersion = baseinfo.ClientVersion
}
if ClientVersion == 319293451 {
wxAccount := &WXAccount{
userInfo: clientsdk.NewUserInfo_Mac(taskInfo.UUID, proxyInfo, ClientVersion, dbUserInfo),
FriendMap: make(map[string]*wechat.ModContact),
ghMap: make(map[string]*wechat.ModContact),
groupMap: make(map[string]*wechat.ModContact),
labelMap: make(map[uint32]*wechat.LabelPair),
userProfile: nil,
loginState: baseinfo.MMLoginStateNoLogin,
taskInfo: taskInfo,
}
return wxAccount
} else {
wxAccount := &WXAccount{
userInfo: clientsdk.NewUserInfo(taskInfo.UUID, proxyInfo, ClientVersion, dbUserInfo),
FriendMap: make(map[string]*wechat.ModContact),
ghMap: make(map[string]*wechat.ModContact),
groupMap: make(map[string]*wechat.ModContact),
labelMap: make(map[uint32]*wechat.LabelPair),
userProfile: nil,
loginState: baseinfo.MMLoginStateNoLogin,
taskInfo: taskInfo,
}
return wxAccount
}
}
// GetUserInfo 获取UserInfo
func (wxAccount *WXAccount) GetUserInfo() *baseinfo.UserInfo {
return wxAccount.userInfo
}
// SetUserInfo 设置用户信息
func (wxAccount *WXAccount) SetUserInfo(info *baseinfo.UserInfo) {
// 默认重启状态为 false
info.IsServerRestart = false
wxAccount.userInfo = info
}
// GetTaskInfo 获取任务信息
func (wxAccount *WXAccount) GetTaskInfo() *websrv.TaskInfo {
return wxAccount.taskInfo
}
// SetUserProfile 设置用户配置信息
func (wxAccount *WXAccount) SetUserProfile(userProfile *wechat.GetProfileResponse) {
wxAccount.userProfile = userProfile
}
// GetUserProfile 获取帐号信息
func (wxAccount *WXAccount) GetUserProfile() *wechat.GetProfileResponse {
return wxAccount.userProfile
}
// GetLoginState 获取登录状态
func (wxAccount *WXAccount) GetLoginState() uint32 {
//return wxAccount.loginState
return wxAccount.GetUserInfo().GetLoginState()
}
// SetLoginState 设置登录状态
func (wxAccount *WXAccount) SetLoginState(loginState uint32) {
wxAccount.loginState = loginState
wxAccount.userInfo.SetLoginState(loginState)
}
// AddWXFriendContact 增加微信好友ID
func (wxAccount *WXAccount) AddWXFriendContact(modContact *wechat.ModContact) {
wxAccount.friendMapLock.Lock()
defer wxAccount.friendMapLock.Unlock()
// 不存在-添加, 存在-更新
wxAccount.FriendMap[modContact.GetUserName().GetStr()] = modContact
}
// RemoveWXFriendID 删除信好友ID
func (wxAccount *WXAccount) RemoveWXFriendID(friendWXID string) bool {
wxAccount.friendMapLock.Lock()
defer wxAccount.friendMapLock.Unlock()
// 如果不存在则退出
_, ok := wxAccount.FriendMap[friendWXID]
if !ok {
return false
}
delete(wxAccount.FriendMap, friendWXID)
return true
}
// GetWXFriendList 获取微信好友ID列表
func (wxAccount *WXAccount) GetWXFriendList() []*wechat.ModContact {
wxAccount.friendMapLock.Lock()
defer wxAccount.friendMapLock.Unlock()
retList := make([]*wechat.ModContact, 0)
for _, modContact := range wxAccount.FriendMap {
retList = append(retList, modContact)
}
return retList
}
// GetWXFriendList 根据 id 获取好友
func (wxAccount *WXAccount) GetWXFriendByWxid(wxid string) wechat.ModContact {
wxAccount.friendMapLock.Lock()
defer wxAccount.friendMapLock.Unlock()
for _, modContact := range wxAccount.FriendMap {
if modContact.GetUserName().GetStr() == wxid {
return *modContact
}
}
return wechat.ModContact{}
}
// GetWXFriendList 获取微信好友ID列表
func (wxAccount *WXAccount) getWXFriendListByLabelID(labelID uint32) []string {
wxAccount.friendMapLock.Lock()
defer wxAccount.friendMapLock.Unlock()
retList := make([]string, 0)
// 遍历好友 获取对应标签的好友列表
for userName, modContact := range wxAccount.FriendMap {
if wxAccount.ContainsLabel(modContact.GetLabelIdlist(), labelID) {
retList = append(retList, userName)
}
}
return retList
}
// AddWXGhContact 增加微信公众号ID
func (wxAccount *WXAccount) AddWXGhContact(modContact *wechat.ModContact) {
wxAccount.ghMapLock.Lock()
defer wxAccount.ghMapLock.Unlock()
// 不存在-添加, 存在-更新
wxAccount.ghMap[modContact.GetUserName().GetStr()] = modContact
}
// RemoveWXGhID 删除微信公众号ID
func (wxAccount *WXAccount) RemoveWXGhID(ghWXID string) bool {
wxAccount.ghMapLock.Lock()
defer wxAccount.ghMapLock.Unlock()
// 如果不存在则退出
_, ok := wxAccount.ghMap[ghWXID]
if !ok {
return false
}
delete(wxAccount.ghMap, ghWXID)
return true
}
// GetWXGhList 获取微信公众号ID列表
func (wxAccount *WXAccount) GetWXGhList() []*wechat.ModContact {
wxAccount.ghMapLock.Lock()
defer wxAccount.ghMapLock.Unlock()
retList := make([]*wechat.ModContact, 0)
for _, modContact := range wxAccount.ghMap {
retList = append(retList, modContact)
}
return retList
}
// AddWXGroup 增加群聊ID
func (wxAccount *WXAccount) AddWXGroup(modContact *wechat.ModContact) {
wxAccount.groupMapLock.Lock()
defer wxAccount.groupMapLock.Unlock()
// 不存在-添加,存在-更新
userName := modContact.GetUserName().GetStr()
wxAccount.groupMap[userName] = modContact
}
// RemoveWXGroup 删除群聊ID
func (wxAccount *WXAccount) RemoveWXGroup(groupWxID string) bool {
wxAccount.groupMapLock.Lock()
defer wxAccount.groupMapLock.Unlock()
// 如果不存在则退出
_, ok := wxAccount.groupMap[groupWxID]
if !ok {
return false
}
delete(wxAccount.groupMap, groupWxID)
return true
}
// GetWXGroupList 获取微信群聊ID列表
func (wxAccount *WXAccount) GetWXGroupList() []*wechat.ModContact {
wxAccount.groupMapLock.Lock()
defer wxAccount.groupMapLock.Unlock()
retList := make([]*wechat.ModContact, 0)
for _, modContact := range wxAccount.groupMap {
retList = append(retList, modContact)
}
return retList
}
// SetLabelPairList 设置标签列表
func (wxAccount *WXAccount) SetLabelPairList(labelList []*wechat.LabelPair) {
wxAccount.labelMapLock.Lock()
defer wxAccount.labelMapLock.Unlock()
labelCount := len(labelList)
wxAccount.labelMap = make(map[uint32]*wechat.LabelPair)
for index := 0; index < labelCount; index++ {
tmpLabelPair := labelList[index]
wxAccount.labelMap[tmpLabelPair.GetLabelId()] = tmpLabelPair
}
}
// 获取对应名称的LabelPair
func (wxAccount *WXAccount) getLabelPairByName(labelName string) *wechat.LabelPair {
for _, labelPair := range wxAccount.labelMap {
if labelPair.GetLabelName() == labelName {
return labelPair
}
}
return nil
}
// GetUserListByLabel 根据Label名称获取好友列表
func (wxAccount *WXAccount) GetUserListByLabel(labelName string) []string {
wxAccount.labelMapLock.Lock()
defer wxAccount.labelMapLock.Unlock()
// 先获取LabelPair
tmpLabelPair := wxAccount.getLabelPairByName(labelName)
if tmpLabelPair == nil {
return []string{}
}
return wxAccount.getWXFriendListByLabelID(tmpLabelPair.GetLabelId())
}
// ContainsLabel 判断好友是否属于某个标签
func (wxAccount *WXAccount) ContainsLabel(strLabelList string, tmpLabel uint32) bool {
if len(strLabelList) <= 0 {
return false
}
// 判断是否属于标签
labelList := strings.Split(strLabelList, ",")
tmpStrLabel := strconv.Itoa(int(tmpLabel))
tmpCount := len(labelList)
for index := 0; index < tmpCount; index++ {
if tmpStrLabel == labelList[index] {
return true
}
}
return false
}
// GetBindQueryNewReq GetBindQueryNewReq
func (wxAccount *WXAccount) GetBindQueryNewReq() (string, error) {
tmpBindQueryNewReq := &baseinfo.BindQueryNewReq{}
tmpBindQueryNewReq.BalanceVersion = wxAccount.userInfo.BalanceVersion
tmpBindQueryNewReq.BindQueryScen = 1
tmpBindQueryNewReq.BindTypeCond = "all_type"
tmpBindQueryNewReq.ISRoot = 0
tmpBindQueryNewReq.City = wxAccount.userProfile.GetUserInfo().GetCity()
tmpBindQueryNewReq.Province = wxAccount.userProfile.GetUserInfo().GetProvince()
tmpString := "balance_version=" + strconv.Itoa(int(tmpBindQueryNewReq.BalanceVersion))
tmpString = tmpString + "&bind_query_scene=" + strconv.Itoa(int(tmpBindQueryNewReq.BindQueryScen))
tmpString = tmpString + "&bind_type_cond=" + tmpBindQueryNewReq.BindTypeCond
tmpString = tmpString + "&city=" + tmpBindQueryNewReq.City
tmpString = tmpString + "&is_device_open_touch=" + strconv.Itoa(int(tmpBindQueryNewReq.ISDeviceOpenTouch))
tmpString = tmpString + "&is_root=" + strconv.Itoa(int(tmpBindQueryNewReq.ISRoot))
tmpString = tmpString + "&province=" + tmpBindQueryNewReq.Province
wcPaySign, err := clientsdk.TenPaySignDes3(tmpString, "%^&*Tenpay!@#$")
if err != nil {
return "", err
}
tmpString = tmpString + "&WCPaySign=" + wcPaySign
return tmpString, nil
}

View File

@@ -0,0 +1,21 @@
package wxcore
import "xiawan/wx/srv/wxface"
// WXBaseRouter 实现router时先嵌入这个基类然后根据需要对这个基类的方法进行重写
type WXBaseRouter struct{}
// PreHandle 在处理conn业务之前的钩子方法
func (wxbr *WXBaseRouter) PreHandle(response wxface.IWXResponse) error {
return nil
}
// Handle 处理conn业务的方法
func (wxbr *WXBaseRouter) Handle(response wxface.IWXResponse) error {
return nil
}
// PostHandle 处理conn业务之后的钩子方法
func (wxbr *WXBaseRouter) PostHandle(response wxface.IWXResponse) error {
return nil
}

478
srv/wxcore/wxcache.go Normal file
View File

@@ -0,0 +1,478 @@
package wxcore
import (
"encoding/json"
"os"
"strconv"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/db"
// "xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXCache 缓存
type WXCache struct {
wxConn wxface.IWXConnect
// 二维码缓存信息
qrCodeInfoCache *baseinfo.QrcodeInfo
// 收藏缓存信息
favorInfoCache *baseinfo.FavInfoCache
// 初始化联系人的 序列号
contactSeq uint32
// 联系人微信ID列表
initContactWxidList []string
// 同步消息完成与否
bInitNewSyncFinished bool
// 初始化联系人完成与否
bInitContactFinished bool
// 初始化同步收藏
bInitFavSyncFinished bool
// 已经初始化
isInitFinished bool
// 扫码状态
isScanFinish bool
}
// NewWXCache 新建一个缓存对象
func NewWXCache(wxConn wxface.IWXConnect) wxface.IWXCache {
return &WXCache{
qrCodeInfoCache: new(baseinfo.QrcodeInfo),
favorInfoCache: new(baseinfo.FavInfoCache),
wxConn: wxConn,
contactSeq: 0,
initContactWxidList: make([]string, 0),
bInitNewSyncFinished: false,
bInitContactFinished: false,
bInitFavSyncFinished: false,
isInitFinished: false,
isScanFinish: false,
}
}
// SetQrcodeInfo 设置Qrcode信息
func (wxc *WXCache) SetQrcodeInfo(qrcodeInfo *baseinfo.QrcodeInfo) {
wxc.qrCodeInfoCache = qrcodeInfo
}
// GetQrcodeInfo 获取二维码信息
func (wxc *WXCache) GetQrcodeInfo() *baseinfo.QrcodeInfo {
return wxc.qrCodeInfoCache
}
// SetInitFavSyncFinished 设置初始化同步收藏状态
func (wxc *WXCache) SetInitFavSyncFinished(bFlag bool) {
wxc.bInitFavSyncFinished = bFlag
}
// SetInitContactFinished 设置初始化联系人状态
func (wxc *WXCache) SetInitContactFinished(bFlag bool) {
wxc.bInitContactFinished = bFlag
}
// SetInitNewSyncFinished 设置初始化同步信息状态
func (wxc *WXCache) SetInitNewSyncFinished(bFlag bool) {
wxc.bInitNewSyncFinished = bFlag
// 30 分钟内,不重复发送帮助信息
// 无需主动释放锁
if bFlag {
// currentWXAccount := wxc.wxConn.GetWXAccount()
// uuid := currentWXAccount.GetUserInfo().UUID
// lockKey := "SendUsage_lock_" + uuid
// lockAcquired, _ := db.AcquireLock(lockKey, time.Minute*30)
// if lockAcquired {
// // wxc.SendUsage()
// }
// 如果未完成扫码则发送指令缓存
if !wxc.isScanFinish {
// wxc.InitCmds(lockAcquired)
// wxc.SendUsage()
}
}
}
// 扫码完成
func (wxc *WXCache) IsScanFinish() bool {
return wxc.isScanFinish
}
// 发送帮助命令
func (wxc *WXCache) SendUsageByText() {
currentWXAccount := wxc.wxConn.GetWXAccount()
uuid := currentWXAccount.GetUserInfo().UUID
lockKey := "SendUsage_lock_" + uuid
// 释放
currentWXAccount.GetUserInfo().SetIsServerRestart(false)
db.ReleaseLock(lockKey)
wxc.SendUsage()
}
// 设置扫码状态
func (wxc *WXCache) SetScanFinish(bFlag bool) {
wxc.isScanFinish = bFlag
if bFlag {
// currentWXAccount := wxc.wxConn.GetWXAccount()
// uuid := currentWXAccount.GetUserInfo().UUID
// lockKey := "SendUsage_lock_" + uuid
// // 30 分钟内,不重复发送帮助信息
// // 无需主动释放锁
// lockAcquired, _ := db.AcquireLock(lockKey, time.Minute*30)
// if lockAcquired {
// // 发送【帮助】
// wxc.SendUsage()
// }
// 初始化信息未完成则发送指令缓存
if !wxc.bInitNewSyncFinished {
// wxc.InitCmds(lockAcquired)
// wxc.SendUsage()
}
}
}
func (wxc *WXCache) IsMsgSyncAndContact() bool {
wxc.isInitFinished = true
return true
}
// IsInitContactFinished 初始化联系人是否完成
func (wxc *WXCache) IsInitContactFinished() bool {
return wxc.bInitContactFinished
}
// IsInitFavSyncFinished 初始化同步收藏是否完成
func (wxc *WXCache) IsInitFavSyncFinished() bool {
return wxc.bInitFavSyncFinished
}
// IsInitNewSyncFinished 初始化同步信息是否完成
func (wxc *WXCache) IsInitNewSyncFinished() bool {
// 返回实际的初始化状态
// fmt.Printf("IsInitNewSyncFinished() 被调用,返回 %v\n", wxc.bInitNewSyncFinished)
return wxc.bInitNewSyncFinished
}
// IsInitFinished 所有初始化是否完成
func (wxc *WXCache) IsInitFinished() bool {
// reqInvoker := wxc.wxConn.GetWXReqInvoker()
// ghWxid := srvconfig.GlobalSetting.GhWxid
// if len(ghWxid) > 0 { // 引流关注公众号
// // 注意: 这里的微信公众号的 wxid 一定要填对
// reqInvoker.VerifyUserRequest(1, "", 0, ghWxid, ghWxid, "")
// }
return wxc.bInitNewSyncFinished && wxc.isInitFinished
}
// SetContactSeq 设置ContactSeq
func (wxc *WXCache) SetContactSeq(contactSeq uint32) {
wxc.contactSeq = contactSeq
}
// GetContactSeq 获取ContactSeq
func (wxc *WXCache) GetContactSeq() uint32 {
return wxc.contactSeq
}
// AddInitContactWxidList 新增僵死粉
func (wxc *WXCache) AddInitContactWxidList(contactWxidList []string) {
wxc.initContactWxidList = append(wxc.initContactWxidList, contactWxidList...)
}
// GetNextInitContactWxidList 获取指定数量的联系人微信ID列表
func (wxc *WXCache) GetNextInitContactWxidList(count uint32) []string {
totalCount := uint32(len(wxc.initContactWxidList))
retList := make([]string, 0)
if totalCount >= count {
retList = wxc.initContactWxidList[:count]
wxc.initContactWxidList = wxc.initContactWxidList[count:]
} else {
retList = wxc.initContactWxidList
wxc.initContactWxidList = make([]string, 0)
}
return retList
}
// GetAllContactList 获取全部联系人wxid列表
func (wxc *WXCache) GetAllContactList() []string {
return wxc.initContactWxidList
}
// Clear 清空缓存
func (wxc *WXCache) Clear() {
wxc.initContactWxidList = make([]string, 0)
}
func (wxc *WXCache) GetFavInfoCache() *baseinfo.FavInfoCache {
return wxc.favorInfoCache
}
func (wxc *WXCache) SetIsInitFinished(bFlag bool) {
wxc.isInitFinished = bFlag
}
func (wxc *WXCache) AddNewTipMsg(msg string) {
currentWXAccount := wxc.wxConn.GetWXAccount()
currentWXFileHelperMgr := wxc.wxConn.GetWXFileHelperMgr()
isServerRestart := currentWXAccount.GetUserInfo().GetIsServerRestart()
if !isServerRestart {
currentWXFileHelperMgr.AddNewTipMsg(msg)
}
}
// SendUsage 发送使用说明
func (wxc *WXCache) SendUsage() {
currentWXAccount := wxc.wxConn.GetWXAccount()
// 命令 start --------
currentTaskMgr := wxc.wxConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
currentGrapHBTask := taskMgr.GetGrabHBTask()
currentSnsTransTask := taskMgr.GetSnsTransTask()
currentSnsTask := taskMgr.GetSnsTask()
currentVerifyTask := taskMgr.GetVerifyTask()
currentRevokeTask := taskMgr.GetRevokeTask()
uuid := currentWXAccount.GetUserInfo().UUID
commandInfo, _ := db.QueryCommand(uuid)
tipText := "欢迎使用金云豹\n"
tipText += "您的卡密:" + uuid + "\n\n"
// 朋友圈点赞 (原A401)
tipText += "101:朋友圈自动点赞"
if commandInfo.A401 == 1 {
tipText += "【开启】"
currentSnsTask.SetAutoThumbUP(true)
} else {
tipText += "【关闭】"
currentSnsTask.SetAutoThumbUP(false)
}
tipText += "\n"
// 朋友圈评论 (原B001)
tipText += "102:朋友圈自动评论"
if commandInfo.B001 == 1 {
tipText += "【开启】"
currentSnsTask.SetAutoComment(true)
} else {
tipText += "【关闭】"
currentSnsTask.SetAutoComment(false)
}
tipText += "\n102#"
if len(commandInfo.B001Str) > 0 {
tipText += commandInfo.B001Str
currentSnsTask.SetCommentContent(commandInfo.B001Str)
} else {
tipText += "回复内容"
}
tipText += "\n"
// 自动入群 (原A811)
tipText += "103:自动入群邀请"
if commandInfo.A811 == 1 {
tipText += "【开启】"
currentVerifyTask.SetAutoJoinGroup(true)
} else {
tipText += "【关闭】"
currentVerifyTask.SetAutoJoinGroup(false)
}
tipText += "\n"
// 通过好友验证 (原A801)
tipText += "104:通过好友验证"
if commandInfo.A801 == 1 {
tipText += "【开启】"
currentVerifyTask.SetNeedVerify(true)
} else {
tipText += "【关闭】"
currentVerifyTask.SetNeedVerify(false)
}
tipText += "\n104#"
if len(commandInfo.A301Str) > 0 {
tipText += commandInfo.A301Str
currentSnsTransTask.SetAddFriendAutoMsg(commandInfo.A301Str)
} else {
tipText += "通过后回复内容"
}
tipText += "\n"
// 消息防撤回 (原A601)
tipText += "105:消息防止撤回"
if commandInfo.A601 == 1 {
tipText += "【开启】"
currentRevokeTask.SetAvoidRevoke(true)
} else {
tipText += "【关闭】"
currentRevokeTask.SetAvoidRevoke(false)
}
tipText += "\n"
// 自动领取红包 (原A101)
tipText += "106:自动领取红包"
if commandInfo.A101 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetAutoGrap(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetAutoGrap(false)
}
tipText += "\n"
// 延迟领取红包 (原A103)
tipText += "107:延迟领取红包"
if commandInfo.A103 > 0 {
tipText += "【开启】"
} else {
tipText += "【关闭】"
}
tipText += "\n107#" + strconv.Itoa(commandInfo.A103)
tipText += "\n"
// 领取红包后感谢 (原A104)
tipText += "108:领取红包后感谢"
if commandInfo.A104 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetAutoReply(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetAutoReply(false)
}
tipText += "\n108#"
if len(commandInfo.A104Str) > 0 {
tipText += commandInfo.A104Str
currentGrapHBTask.SetAutoReplyContent(commandInfo.A104Str)
} else {
tipText += "谢谢,谢谢老板"
currentGrapHBTask.SetAutoReplyContent("谢谢,谢谢老板")
}
tipText += "\n"
// 领取个人红包 (原A105)
tipText += "109:领取个人红包"
if commandInfo.A105 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetAutoPerson(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetAutoPerson(false)
}
tipText += "\n"
// 置顶群组红包 (原A111)
tipText += "110:置顶群组红包"
if commandInfo.A111 == 1 {
tipText += "【仅抢】"
currentGrapHBTask.SetNotTopGroup(true)
} else {
tipText += "【不抢】"
currentGrapHBTask.SetNotTopGroup(false)
}
tipText += "\n"
// 自动接收转账 (原A106)
tipText += "111:自动接收转账"
if commandInfo.A106 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetAutoTransfer(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetAutoTransfer(false)
}
tipText += "\n111#"
if len(commandInfo.A116Str) > 0 {
tipText += commandInfo.A116Str
currentGrapHBTask.SetAutoTransferReplyContent(commandInfo.A116Str)
} else {
tipText += "回复内容"
currentGrapHBTask.SetAutoTransferReplyContent("谢谢,谢谢老板")
}
tipText += "\n"
// 红包统计功能 (原A107)
tipText += "112:红包统计功能"
if commandInfo.A107 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetHBStat(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetHBStat(false)
}
tipText += "\n"
// 红包统计清零 (原A108)
tipText += "113:红包统计清零\n"
// 收款统计功能 (原A109)
tipText += "114:收款统计功能"
if commandInfo.A109 == 1 {
tipText += "【开启】"
currentGrapHBTask.SetTransStat(true)
} else {
tipText += "【关闭】"
currentGrapHBTask.SetTransStat(false)
}
tipText += "\n"
// 收款统计清零 (原A110)
tipText += "115:收款统计清零\n"
// 查询置顶群聊 (原200)
tipText += "116:查询置顶的群聊\n"
// 朋友圈收藏转发 (原A403)
tipText += "117:朋友圈收藏转发"
if commandInfo.A403 == 1 {
tipText += "【开启】"
currentSnsTransTask.SetAutoRelay(true)
} else {
tipText += "【关闭】"
currentSnsTransTask.SetAutoRelay(false)
}
tipText += "\n"
// 朋友圈跟随转发 (原A402)
tipText += "118:朋友圈跟随转发"
if commandInfo.A402 == 1 {
tipText += "【开启】"
currentSnsTransTask.SetSyncTrans(true)
} else {
tipText += "【关闭】"
currentSnsTransTask.SetSyncTrans(false)
}
tipText += "\n"
// 检测僵死粉 (原901)
tipText += "119:检测僵死粉\n"
// 清理僵死粉 (原902)
tipText += "120:清理僵死粉\n"
// 帮助和功能状态 (原000)
tipText += "000:帮助和功能状态\n"
// 查询卡密有效期 (原100)
tipText += "666:查询卡密有效期"
wxc.AddNewTipMsg(tipText)
}
func GetOwner() map[string]int {
tmpHomeDir, err := os.Getwd()
if err != nil {
baseutils.PrintLog(err.Error())
return nil
}
file, err := os.Open(tmpHomeDir + "/assets/owner.json")
if err != nil {
return nil
}
defer file.Close()
// 解码 JSON 数据
var data map[string]int
decoder := json.NewDecoder(file)
if err := decoder.Decode(&data); err != nil {
return nil
}
return data
}

1206
srv/wxcore/wxconnect.go Normal file

File diff suppressed because it is too large Load Diff

222
srv/wxcore/wxconnectmgr.go Normal file
View File

@@ -0,0 +1,222 @@
package wxcore
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"sync"
"time"
"xiawan/wx/srv/srvconfig"
"xiawan/wx/srv/websrv"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/wxface"
)
// WXConnectMgr 微信链接管理器
type WXConnectMgr struct {
canUseConnIDList []uint32 // 删掉/回收后的connID
currentWxConnID uint32
wxConnectMap map[string]wxface.IWXConnect //管理的连接信息
wxConnLock sync.RWMutex //读写连接的读写锁
wxConnLockShowConnects sync.RWMutex //读写连接的读写锁
}
// NewWXConnManager 创建一个WX链接管理
func NewWXConnManager() wxface.IWXConnectMgr {
return &WXConnectMgr{
canUseConnIDList: make([]uint32, 0),
currentWxConnID: 0,
wxConnectMap: make(map[string]wxface.IWXConnect),
}
}
// Add 添加链接
func (wm *WXConnectMgr) Add(wxConnect wxface.IWXConnect) {
wm.wxConnLock.Lock()
defer wm.wxConnLock.Unlock()
// newConnID := uint32(0)
// if len(wm.canUseConnIDList) > 0 {
// newConnID = wm.canUseConnIDList[0]
// wm.canUseConnIDList = wm.canUseConnIDList[1:]
// } else {
// newConnID = wm.currentWxConnID
// wm.currentWxConnID++
// }
newConnID := wm.currentWxConnID
wm.currentWxConnID++
wxConnect.SetWXConnID(newConnID)
wm.wxConnectMap[wxConnect.GetWXAccount().GetUserInfo().UUID] = wxConnect
// 打印链接数量
wm.ShowConnectInfo()
}
// GetWXConnectByUserInfoUUID 根据UserInfoUUID获取微信链接
func (wm *WXConnectMgr) GetWXConnectByUserInfoUUID(userInfoUUID string) wxface.IWXConnect {
wm.wxConnLock.Lock()
defer wm.wxConnLock.Unlock()
wxConn, ok := wm.wxConnectMap[userInfoUUID]
if ok {
fmt.Println(fmt.Sprintf("GET Connection locfree success by %s", userInfoUUID))
return wxConn
}
fmt.Println(fmt.Sprintf("GET Connection locfree Failed by %s abandon the conntection get !", userInfoUUID))
return nil
}
// GetWXConnectByWXID 根据WXID获取微信链接
func (wm *WXConnectMgr) GetWXConnectByWXID(wxid string) wxface.IWXConnect {
for _, tryCoon := range wm.wxConnectMap {
tmpUserInfo := tryCoon.GetWXAccount().GetUserInfo()
if tmpUserInfo == nil || strings.Compare(tmpUserInfo.WxId, wxid) != 0 {
continue
}
return tryCoon
}
//保护共享资源Map 加读锁
wm.wxConnLock.RLock()
defer wm.wxConnLock.RUnlock()
//根据WXID获取微信链接
for _, wxConn := range wm.wxConnectMap {
tmpUserInfo := wxConn.GetWXAccount().GetUserInfo()
if tmpUserInfo == nil || strings.Compare(tmpUserInfo.WxId, wxid) != 0 {
continue
}
return wxConn
}
return nil
}
// Remove 删除连接
func (wm *WXConnectMgr) Remove(wxconn wxface.IWXConnect) {
wm.wxConnLock.Lock()
defer wm.wxConnLock.Unlock()
// 在删除连接前确保清理WebSocket连接
currentUserInfo := wxconn.GetWXAccount().GetUserInfo()
if currentUserInfo != nil {
currentTaskMgr := wxconn.GetWXTaskMgr()
if taskMgr, ok := currentTaskMgr.(*WXTaskMgr); ok {
wsTask := taskMgr.SocketMsgTask
userUUID := currentUserInfo.UUID
existingConn := wsTask.GetWebSocket(userUUID)
if existingConn != nil {
fmt.Println("Remove()时清理WebSocket连接:", userUUID)
existingConn.Close()
wsTask.DeleteWebSocket(userUUID)
}
// 禁用WebSocket功能
wsTask.SetWebSocketEnabled(false)
}
}
//删除
delete(wm.wxConnectMap, currentUserInfo.UUID)
//wm.canUseConnIDList = append(wm.canUseConnIDList, wxconn.GetWXConnID())
currentUserInfo = nil
// 打印链接数量
wm.ShowConnectInfo()
}
// Len 获取当前连接
func (wm *WXConnectMgr) Len() int {
return len(wm.wxConnectMap)
}
// ClearWXConn 删除并停止所有链接
func (wm *WXConnectMgr) ClearWXConn() {
//保护共享资源Map 加写锁
wm.wxConnLock.Lock()
defer wm.wxConnLock.Unlock()
//停止并删除全部的连接信息
for uuid, wxConn := range wm.wxConnectMap {
//停止
wxConn.Stop()
//删除
delete(wm.wxConnectMap, uuid)
}
// 打印链接数量
wm.ShowConnectInfo()
}
// ShowConnectInfo 打印链接情况
func (wm *WXConnectMgr) ShowConnectInfo() string {
wm.wxConnLockShowConnects.Lock()
defer wm.wxConnLockShowConnects.Unlock()
totalNum := wm.Len()
noLoginNum := uint32(0)
onlineNum := uint32(0)
offlineNum := uint32(0)
for _, wxConn := range wm.wxConnectMap {
loginState := wxConn.GetWXAccount().GetLoginState()
if loginState == baseinfo.MMLoginStateNoLogin {
noLoginNum = noLoginNum + 1
} else if loginState == baseinfo.MMLoginStateOnLine {
onlineNum = onlineNum + 1
} else if loginState == baseinfo.MMLoginStateOffLine {
offlineNum = offlineNum + 1
}
}
showText := time.Now().Format("2006-01-02 15:04:05")
showText = showText + " 总链接数量: " + strconv.Itoa(totalNum) + " 未登录数量:" + strconv.Itoa(int(noLoginNum))
showText = showText + " 在线数量: " + strconv.Itoa(int(onlineNum)) + " 离线数量: " + strconv.Itoa(int(offlineNum))
fmt.Println(showText)
return showText
}
func (wm *WXConnectMgr) GetConnectInfo() map[string]interface{} {
wm.wxConnLock.Lock()
defer wm.wxConnLock.Unlock()
totalNum := wm.Len()
noLoginNum := int(0)
onlineNum := int(0)
offlineNum := int(0)
var connections = make([]map[string]interface{}, 0)
for _, wxConn := range wm.wxConnectMap {
connections = append(connections, map[string]interface{}{
"loginState": wxConn.GetWXAccount().GetLoginState(),
"userInfo": wxConn.GetWXAccount().GetUserInfo(),
})
loginState := wxConn.GetWXAccount().GetLoginState()
if loginState == baseinfo.MMLoginStateNoLogin {
noLoginNum = noLoginNum + 1
} else if loginState == baseinfo.MMLoginStateOnLine {
onlineNum = onlineNum + 1
} else if loginState == baseinfo.MMLoginStateOffLine {
offlineNum = offlineNum + 1
}
}
return map[string]interface{}{
"time": time.Now().Format("2006-01-02 15:04:05"),
"totalNum": totalNum,
"noLoginNum": noLoginNum,
"onlineNum": onlineNum,
"offlineNum": offlineNum,
"connections": connections,
}
}
// 上报到后端 syncOnlineCount
func updateOnlineCount(count int32) error {
connectInfo := map[string]interface{}{}
connectInfo["ip"] = srvconfig.GlobalSetting.TargetIp
connectInfo["count"] = count
// 将参数转换为 JSON 格式
paramBytes, err := json.Marshal(connectInfo)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return err
}
if srvconfig.GlobalSetting.SyncOnlineCount == "" {
return nil
}
// 发送 POST 请求
fmt.Printf("上报在线数量:%d\n", count)
_, _ = websrv.TaskPostJson("http://"+srvconfig.GlobalSetting.SyncOnlineCount, paramBytes)
return nil
}

View File

@@ -0,0 +1,165 @@
package wxcore
import (
"encoding/xml"
"time"
"xiawan/wx/api/req"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/wxface"
"github.com/lunny/log"
)
// WXFileHelperMgr 文件传输助手消息管理器
type WXFileHelperMgr struct {
wxConn wxface.IWXConnect
msgList chan *MsgItem
endChan chan bool
isStarted bool
}
// MsgItem MsgItem
type MsgItem struct {
TextMsg string
ImageData []byte
MsgType uint32
}
// NewWXFileHelperMgr 新建文件传输助手管理器
func NewWXFileHelperMgr(wxConn wxface.IWXConnect) *WXFileHelperMgr {
return &WXFileHelperMgr{
wxConn: wxConn,
msgList: make(chan *MsgItem, 100),
endChan: make(chan bool, 1),
isStarted: false,
}
}
// Start 开启
func (wxfhm *WXFileHelperMgr) Start() {
if wxfhm.isStarted {
return
}
wxfhm.isStarted = true
go wxfhm.startDealMsg()
}
// Stop 关闭
func (wxfhm *WXFileHelperMgr) Stop() {
wxfhm.isStarted = false
wxfhm.endChan <- true
wxfhm.isStarted = false
}
// AddNewTipMsg 新增提示
func (wxfhm *WXFileHelperMgr) AddNewTipMsg(newMsg string) {
if wxfhm.isStarted {
newMsgItem := &MsgItem{}
newMsgItem.MsgType = 1
newMsgItem.TextMsg = newMsg
wxfhm.msgList <- newMsgItem
}
}
// 新增名片消息 AddNewCardMsg
func (wxfhm *WXFileHelperMgr) AddNewCardMsg(newMsg string) {
if wxfhm.isStarted {
newMsgItem := &MsgItem{}
newMsgItem.MsgType = 42
newMsgItem.TextMsg = newMsg
wxfhm.msgList <- newMsgItem
}
}
// AddImageMsg 新增图片消息
func (wxfhm *WXFileHelperMgr) AddImageMsg(imgData []byte) {
if wxfhm.isStarted {
newMsgItem := &MsgItem{}
newMsgItem.MsgType = 2
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 转发图片
func (wxfhm *WXFileHelperMgr) ForwardImageMsg(imgData []byte) {
if wxfhm.isStarted {
newMsgItem := &MsgItem{}
newMsgItem.MsgType = 3
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 转发表情
func (wxfhm *WXFileHelperMgr) ForwardEmoticonMsg(imgData []byte) {
if wxfhm.isStarted {
newMsgItem := &MsgItem{}
newMsgItem.MsgType = 47
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 处理消息
func (wxfhm *WXFileHelperMgr) startDealMsg() {
defer TryE("(wxfhm *WXFileHelperMgr) startDealMsg()")
currentReqInvoker := wxfhm.wxConn.GetWXReqInvoker()
for {
// 最少1秒发送一次
time.Sleep(1 * time.Second)
select {
case newMsgItem := <-wxfhm.msgList:
if newMsgItem.MsgType == 1 {
// 文字
currentReqInvoker.SendTextMsgToFileHelperRequest(newMsgItem.TextMsg)
} else if newMsgItem.MsgType == 2 {
// 图片
currentReqInvoker.SendImageToFileHelper(newMsgItem.ImageData)
} else if newMsgItem.MsgType == 42 {
// 名片
// 发送消息
currentReqInvoker.SendCardMsgToFileHelperRequest(newMsgItem.TextMsg)
// currentReqInvoker.SendCardMsgToFileHelper(newMsgItem.TextMsg)
} else if newMsgItem.MsgType == 3 {
// 图片
var msg baseinfo.Msg3
err := xml.Unmarshal(newMsgItem.ImageData, &msg)
if err != nil {
log.Fatalf("Error parsing XML: %v", err)
return
}
if msg.Img.CdnThumbURL != "" {
sendMsg := baseinfo.ForwardImageItem{
ToUserName: baseinfo.FileHelperWXID,
AesKey: msg.Img.AESKey,
CdnMidImgUrl: msg.Img.CdnThumbURL,
CdnMidImgSize: msg.Img.CdnThumbLength,
CdnThumbImgSize: msg.Img.Length,
}
_, _ = currentReqInvoker.ForwardCdnImageRequest(sendMsg)
}
} else if newMsgItem.MsgType == 47 {
// 表情
var msg baseinfo.Msg47
err := xml.Unmarshal(newMsgItem.ImageData, &msg)
if err != nil {
log.Fatalf("Error parsing XML: %v", err)
return
}
if msg.Emoji.MD5 != "" {
sendMsg := req.SendEmojiItem{
ToUserName: baseinfo.FileHelperWXID,
EmojiMd5: msg.Emoji.MD5,
EmojiSize: msg.Emoji.Len,
}
_, _ = currentReqInvoker.ForwardEmojiRequest(sendMsg.EmojiMd5, sendMsg.ToUserName, sendMsg.EmojiSize)
}
}
case <-wxfhm.endChan:
return
}
}
}

View File

@@ -0,0 +1,17 @@
package wxcore
// WXLongRequest 微信长链接请求
type WXLongRequest struct {
OpCode uint32
Data []byte
}
// GetOpcode 获取Opcode
func (wxlq *WXLongRequest) GetOpcode() uint32 {
return wxlq.OpCode
}
// GetData 获取数据
func (wxlq *WXLongRequest) GetData() []byte {
return wxlq.Data
}

101
srv/wxcore/wxmsghandler.go Normal file
View File

@@ -0,0 +1,101 @@
package wxcore
import (
"fmt"
"xiawan/wx/srv/srvconfig"
"xiawan/wx/srv/wxface"
)
// WXMsgHandler 微信响应管理器
type WXMsgHandler struct {
wxRouterMap map[uint32]wxface.IWXRouter //存放每个MsgId 所对应的处理方法的map属性
wxWorkerPoolSize uint32 //业务工作Worker池的数量
wxTaskQueue []chan wxface.IWXResponse //Worker负责取任务的消息队列
}
// NewWXMsgHandler 新建微信消息处理器
func NewWXMsgHandler() *WXMsgHandler {
return &WXMsgHandler{
wxRouterMap: make(map[uint32]wxface.IWXRouter),
// 一个worker对应一个queue
wxWorkerPoolSize: srvconfig.GlobalSetting.WorkerPoolSize * 2,
wxTaskQueue: make([]chan wxface.IWXResponse, srvconfig.GlobalSetting.WorkerPoolSize*2),
}
}
// AddRouter 增加微信消息路由
func (wxmh *WXMsgHandler) AddRouter(respID uint32, wxRouter wxface.IWXRouter) {
//1 判断当前msg绑定的API处理方法是否已经存在
if _, ok := wxmh.wxRouterMap[respID]; ok {
return
}
//2 添加msg与api的绑定关系
wxmh.wxRouterMap[respID] = wxRouter
}
// GetRouterByRespID 根据响应ID获取对应的路由
func (wxmh *WXMsgHandler) GetRouterByRespID(urlID uint32) wxface.IWXRouter {
handler, ok := wxmh.wxRouterMap[urlID]
if !ok {
return nil
}
return handler
}
// doMsgHandler 马上以非阻塞方式处理消息
func (wxmh *WXMsgHandler) doMsgHandler(response wxface.IWXResponse) {
//xiaoyue处理消息
// fmt.Println("xiaoyue------doMsgHandler")
// defer TryE(response.GetWXConncet().GetWXAccount().GetUserInfo().GetUserName())
handler, ok := wxmh.wxRouterMap[response.GetPackHeader().URLID]
// fmt.Println("[URLID]", response.GetPackHeader().URLID)
if !ok {
return
}
//执行对应处理方法
handler.PreHandle(response)
handler.Handle(response)
handler.PostHandle(response)
}
// startOneWorker 启动一个Worker工作流程
func (wxmh *WXMsgHandler) startOneWorker(workerID int, taskQueue chan wxface.IWXResponse) {
// fmt.Println("xiaoyue------startOneWorker")
defer func() {
if r := recover(); r != nil {
fmt.Printf("startOneWorker recovered from panic: %v\n", r)
// 可以在这里记录日志或执行其他紧急恢复处理措施
}
}()
//不断的等待队列中的消息
for {
select {
//有消息则取出队列的Request并执行绑定的业务方法
case request := <-taskQueue:
wxmh.doMsgHandler(request)
}
}
}
// StartWorkerPool 启动worker工作池
func (wxmh *WXMsgHandler) StartWorkerPool() {
defer TryE("")
// fmt.Println("xiaoyue------StartWorkerPool")
//遍历需要启动worker的数量依此启动
for i := 0; i < int(wxmh.wxWorkerPoolSize); i++ {
//一个worker被启动
//给当前worker对应的任务队列开辟空间
wxmh.wxTaskQueue[i] = make(chan wxface.IWXResponse, srvconfig.GlobalSetting.MaxWorkerTaskLen*2)
//启动当前Worker阻塞的等待对应的任务队列是否有消息传递进来
go wxmh.startOneWorker(i, wxmh.wxTaskQueue[i])
}
}
// SendWXRespToTaskQueue 将消息交给TaskQueue,由worker进行处理
func (wxmh *WXMsgHandler) SendWXRespToTaskQueue(response wxface.IWXResponse) {
//得到需要处理此条连接的workerID
workerID := response.GetWXConncet().GetWXConnID() % wxmh.wxWorkerPoolSize
//将请求消息发送给任务队列
wxmh.wxTaskQueue[workerID] <- response
}

6502
srv/wxcore/wxreqinvoker.go Normal file

File diff suppressed because it is too large Load Diff

32
srv/wxcore/wxresponse.go Normal file
View File

@@ -0,0 +1,32 @@
package wxcore
import (
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/wxface"
)
// WXResponse 微信响应
type WXResponse struct {
wxConn wxface.IWXConnect
packHeader *baseinfo.PackHeader
//业务日志Id
LogUUID string
}
// NewWXResponse 新建WXResponse
func NewWXResponse(wxConn wxface.IWXConnect, packHeader *baseinfo.PackHeader) wxface.IWXResponse {
return &WXResponse{
wxConn: wxConn,
packHeader: packHeader,
}
}
// GetPackHeader 获取响应数据
func (resp *WXResponse) GetPackHeader() *baseinfo.PackHeader {
return resp.packHeader
}
// GetWXConncet 获取WXConncet
func (resp *WXResponse) GetWXConncet() wxface.IWXConnect {
return resp.wxConn
}

78
srv/wxcore/wxserver.go Normal file
View File

@@ -0,0 +1,78 @@
package wxcore
import (
// "fmt"
"xiawan/wx/srv"
"xiawan/wx/srv/wxface"
)
// WXServer 微信服务器,处理与微信的交互
type WXServer struct {
wxMsgHandler wxface.IWXMsgHandler
wxConnectMgr wxface.IWXConnectMgr
wxFileMgr *srv.WXFileMgr
}
// NewWXServer 新建微信服务对象
func NewWXServer() wxface.IWXServer {
return &WXServer{
wxMsgHandler: NewWXMsgHandler(),
wxConnectMgr: NewWXConnManager(),
wxFileMgr: srv.NewWXFileMgr(),
}
}
// Start 开启微信服务
func (wxs *WXServer) Start() {
defer TryE("(wxtm *WXServer) Start()")
// fmt.Println("xiaoyue--------start开启微信服务")
// 开启微信消息线程池
wxs.wxMsgHandler.StartWorkerPool()
//wxs.wxFileMgr.Start()
}
// GetWXMsgHandler 获取微信消息管理器
func (wxs *WXServer) GetWXMsgHandler() wxface.IWXMsgHandler {
return wxs.wxMsgHandler
}
// GetWXConnectMgr 获取微信链接管理器
func (wxs *WXServer) GetWXConnectMgr() wxface.IWXConnectMgr {
return wxs.wxConnectMgr
}
// GetWXFileMgr 获取微信文件管理器
func (wxs *WXServer) GetWXFileMgr() *srv.WXFileMgr {
return wxs.wxFileMgr
}
// AddWXRouter 添加微信消息路由
func (wxs *WXServer) AddWXRouter(funcID uint32, wxRouter wxface.IWXRouter) {
wxs.wxMsgHandler.AddRouter(funcID, wxRouter)
}
// UpdateExpiryDate 更新授权码过期时间
func (wxs *WXServer) UpdateExpiryDate(key, expiryDate string) {
connMgr := wxs.GetWXConnectMgr()
wxConn := connMgr.GetWXConnectByUserInfoUUID(key)
if wxConn == nil {
return
}
currentTaskMgr := wxConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
currentSnsTransTask := taskMgr.GetSnsTransTask()
currentSnsTransTask.SetExpiryDate(expiryDate)
}
// UpdateDisable 更新授权码禁用状态
func (wxs *WXServer) UpdateDisable(key string, disable int) {
connMgr := wxs.GetWXConnectMgr()
wxConn := connMgr.GetWXConnectByUserInfoUUID(key)
if wxConn == nil {
return
}
currentTaskMgr := wxConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
currentSnsTransTask := taskMgr.GetSnsTransTask()
currentSnsTransTask.SetDisable(disable)
}

110
srv/wxcore/wxsyncmgr.go Normal file
View File

@@ -0,0 +1,110 @@
package wxcore
import (
"time"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/wxface"
)
// WXSyncMgr 同步管理器(同步消息,同步收藏等等)
type WXSyncMgr struct {
wxConn wxface.IWXConnect
newSyncIDList chan uint32
favSyncIDList chan uint32
newInitIDList chan uint32
endNewChan chan bool
endFavChan chan bool
endInitChan chan bool
isStart bool
}
// NewWXSyncMgr 新建同步管理器
func NewWXSyncMgr(wxConn wxface.IWXConnect) wxface.IWXSyncMgr {
return &WXSyncMgr{
wxConn: wxConn,
newSyncIDList: make(chan uint32, 100),
favSyncIDList: make(chan uint32, 100),
newInitIDList: make(chan uint32, 200),
endNewChan: make(chan bool, 1),
endFavChan: make(chan bool, 1),
endInitChan: make(chan bool, 1),
isStart: false,
}
}
// Start 开启管理器
func (wxsm *WXSyncMgr) Start() {
if wxsm.isStart {
return
}
wxsm.isStart = true
go wxsm.startNewSyncListener()
go wxsm.startFavSyncListener()
// go wxsm.startInitSyncListener()
}
// Stop 关闭管理器
func (wxsm *WXSyncMgr) Stop() {
wxsm.isStart = false
wxsm.endNewChan <- true
wxsm.endFavChan <- true
wxsm.endInitChan <- true
}
// SendNewSyncRequest 发送同步消息请求
func (wxsm *WXSyncMgr) SendNewSyncRequest() {
wxsm.newSyncIDList <- 1
}
// SendFavSyncRequest 发送同步收藏请求
func (wxsm *WXSyncMgr) SendFavSyncRequest() {
wxsm.favSyncIDList <- 1
}
func (wxsm *WXSyncMgr) SendSyncInitRequest() {
wxsm.newInitIDList <- 1
}
func (wxsm *WXSyncMgr) startNewSyncListener() {
//处理异常
defer TryE(wxsm.wxConn.GetWXAccount().GetUserInfo().GetUserName())
currentReqInvoker := wxsm.wxConn.GetWXReqInvoker()
for {
select {
case <-wxsm.newSyncIDList:
// 同步消息
currentReqInvoker.SendNewSyncRequest(baseinfo.MMSyncSceneTypeNeed)
case <-wxsm.endNewChan:
return
}
}
}
func (wxsm *WXSyncMgr) startFavSyncListener() {
currentReqInvoker := wxsm.wxConn.GetWXReqInvoker()
for {
// 3秒执行一次
time.Sleep(1 * time.Second)
select {
case <-wxsm.favSyncIDList:
// 同步收藏
currentReqInvoker.SendFavSyncRequest()
case <-wxsm.endFavChan:
return
}
}
}
func (wxsm *WXSyncMgr) startInitSyncListener() {
currentReqInvoker := wxsm.wxConn.GetWXReqInvoker()
for {
select {
case <-wxsm.newInitIDList:
// 同步收藏
_ = currentReqInvoker.SendNewInitSyncRequest()
case <-wxsm.endInitChan:
return
}
}
}

108
srv/wxcore/wxtaskmgr.go Normal file
View File

@@ -0,0 +1,108 @@
package wxcore
import (
"fmt"
"xiawan/wx/srv/wxface"
"xiawan/wx/srv/wxtask"
)
// WXTaskMgr 任务管理器
type WXTaskMgr struct {
start bool
wxConn wxface.IWXConnect
friendTask *wxtask.WXFriendTask
groupTask *wxtask.WXGroupTask
grabHBTask *wxtask.WXGrabHBTask
snsTransTask *wxtask.WXSnsTransTask
revokeTask *wxtask.WXRevokeTask
snsTask *wxtask.WXSnsTask
verifyTask *wxtask.WXVerifyTask
SocketMsgTask *wxtask.WXSocketMsgTask
}
// NewWXTaskMgr 新建一个微信管理器
func NewWXTaskMgr(wxConn wxface.IWXConnect) wxface.IWXTaskMgr {
return &WXTaskMgr{
wxConn: wxConn,
friendTask: wxtask.NewWXFriendTask(wxConn),
groupTask: wxtask.NewWXGroupTask(wxConn),
grabHBTask: wxtask.NewWXGrabHBTask(wxConn),
snsTransTask: wxtask.NewWXSnsTransTask(wxConn),
revokeTask: wxtask.NewWXRevokeTask(wxConn),
snsTask: wxtask.NewWXSnsTask(wxConn),
verifyTask: wxtask.NewWXVerifyTask(wxConn),
SocketMsgTask: wxtask.NewWXSocketMsgTask(wxConn),
}
}
// Start 启动
func (wxtm *WXTaskMgr) Start() {
fmt.Println("启动微信任务管理器")
//处理异常
// defer TryE("(wxtm *WXTaskMgr) Start()")
if wxtm.start {
return
}
wxtm.start = true
// wxtm.grabHBTask.Start()
wxtm.snsTransTask.Start()
// wxtm.revokeTask.Start()
wxtm.snsTask.Start()
wxtm.SocketMsgTask.Start()
}
// Stop 关闭
func (wxtm *WXTaskMgr) Stop() {
if !wxtm.start {
return
}
wxtm.start = false
// wxtm.grabHBTask.Stop()
wxtm.snsTransTask.Stop()
// wxtm.revokeTask.Stop()
wxtm.snsTask.Stop()
wxtm.SocketMsgTask.Stop()
}
func (wxtm *WXTaskMgr) GetTaskStatus() bool {
return wxtm.start
}
// GetGroupTask 获取群任务管理器
func (wxtm *WXTaskMgr) GetGroupTask() *wxtask.WXGroupTask {
return wxtm.groupTask
}
// GetFriendTask 获取好友任务管理器
func (wxtm *WXTaskMgr) GetFriendTask() *wxtask.WXFriendTask {
return wxtm.friendTask
}
// GetGrabHBTask 获取红包任务管理器
func (wxtm *WXTaskMgr) GetGrabHBTask() *wxtask.WXGrabHBTask {
return wxtm.grabHBTask
}
func (wxtm *WXTaskMgr) GetSocketMsgTask() *wxtask.WXSocketMsgTask {
return wxtm.SocketMsgTask
}
// GetSnsTransTask 获取朋友圈转发任务管理器
func (wxtm *WXTaskMgr) GetSnsTransTask() *wxtask.WXSnsTransTask {
return wxtm.snsTransTask
}
// GetRevokeTask 获取防撤回消息任务管理器
func (wxtm *WXTaskMgr) GetRevokeTask() *wxtask.WXRevokeTask {
return wxtm.revokeTask
}
// GetSnsTask 获取朋友圈任务管理器
func (wxtm *WXTaskMgr) GetSnsTask() *wxtask.WXSnsTask {
return wxtm.snsTask
}
// GetVerifyTask 获取朋友圈任务管理器
func (wxtm *WXTaskMgr) GetVerifyTask() *wxtask.WXVerifyTask {
return wxtm.verifyTask
}

18
srv/wxcore/wxtry.go Normal file
View File

@@ -0,0 +1,18 @@
package wxcore
import (
"fmt"
"time"
)
// 异常处理
func TryE(userName string) {
errs := recover()
if errs == nil {
return
}
now := time.Now() //获取当前时间
timeFormat := now.Format("2006-01-02 15:04:05") //设定时间格式
fileName := fmt.Sprintf("[%s]-%s-%vrn", userName, timeFormat, errs) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒)
fmt.Println("error: ", fileName)
}

150
srv/wxcore/wxusermsgmgr.go Normal file
View File

@@ -0,0 +1,150 @@
package wxcore
import (
"encoding/xml"
"time"
"xiawan/wx/api/req"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/wxface"
"github.com/lunny/log"
)
// WXUSerMsgMgr WX用户消息管理器
type WXUSerMsgMgr struct {
wxConn wxface.IWXConnect
msgList chan *USerMsgItem
endChan chan bool
isStarted bool
}
// USerMsgItem MsgItem
type USerMsgItem struct {
TextMsg string
ImageData []byte
MsgType uint32
TOUserName string
}
// NewWXFileHelperMgr 新建用户消息管理器
func NewWXUSerMsgMgr(wxConn wxface.IWXConnect) *WXUSerMsgMgr {
return &WXUSerMsgMgr{
wxConn: wxConn,
msgList: make(chan *USerMsgItem, 100),
endChan: make(chan bool, 1),
isStarted: false,
}
}
// Start 开启
func (wxfhm *WXUSerMsgMgr) Start() {
if wxfhm.isStarted {
return
}
wxfhm.isStarted = true
go wxfhm.startDealMsg()
}
// Stop 关闭
func (wxfhm *WXUSerMsgMgr) Stop() {
wxfhm.endChan <- true
wxfhm.isStarted = false
}
// AddNewMsg 新增提示
func (wxfhm *WXUSerMsgMgr) AddNewTextMsg(newMsg, toUSerName string) {
if wxfhm.isStarted {
newMsgItem := &USerMsgItem{}
newMsgItem.TOUserName = toUSerName
newMsgItem.MsgType = 1
newMsgItem.TextMsg = newMsg
wxfhm.msgList <- newMsgItem
}
}
// AddImageMsg 新增图片消息
func (wxfhm *WXUSerMsgMgr) AddImageMsg(imgData []byte, toUSerName string) {
if wxfhm.isStarted {
newMsgItem := &USerMsgItem{}
newMsgItem.TOUserName = toUSerName
newMsgItem.MsgType = 2
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 转发图片
func (wxfhm *WXUSerMsgMgr) ForwardImageMsg(imgData []byte, toUSerName string) {
if wxfhm.isStarted {
newMsgItem := &USerMsgItem{}
newMsgItem.TOUserName = toUSerName
newMsgItem.MsgType = 3
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 转发表情
func (wxfhm *WXUSerMsgMgr) ForwardEmoticonMsg(imgData []byte, toUSerName string) {
if wxfhm.isStarted {
newMsgItem := &USerMsgItem{}
newMsgItem.TOUserName = toUSerName
newMsgItem.MsgType = 47
newMsgItem.ImageData = imgData
wxfhm.msgList <- newMsgItem
}
}
// 处理消息
func (wxfhm *WXUSerMsgMgr) startDealMsg() {
//处理异常
defer TryE("(wxfhm *WXUSerMsgMgr) startDealMsg()")
currentReqInvoker := wxfhm.wxConn.GetWXReqInvoker()
for {
// 最少1秒发送一次
time.Sleep(1 * time.Second)
select {
case newMsgItem := <-wxfhm.msgList:
if newMsgItem.MsgType == 1 {
// 文字
_, _ = currentReqInvoker.SendTextMsgRequest(newMsgItem.TOUserName, newMsgItem.TextMsg, []string{}, 1)
} else if newMsgItem.MsgType == 2 {
// 图片
_, _ = currentReqInvoker.SendCdnUploadImageReuqest(newMsgItem.ImageData, newMsgItem.TOUserName)
} else if newMsgItem.MsgType == 3 {
// 图片
var msg baseinfo.Msg3
err := xml.Unmarshal(newMsgItem.ImageData, &msg)
if err != nil {
log.Fatalf("Error parsing XML: %v", err)
return
}
sendMsg := baseinfo.ForwardImageItem{
ToUserName: newMsgItem.TOUserName,
AesKey: msg.Img.AESKey,
CdnMidImgUrl: msg.Img.CdnThumbURL,
CdnMidImgSize: msg.Img.CdnThumbLength,
CdnThumbImgSize: msg.Img.Length,
}
_, _ = currentReqInvoker.ForwardCdnImageRequest(sendMsg)
} else if newMsgItem.MsgType == 47 {
// 表情
var msg baseinfo.Msg47
err := xml.Unmarshal(newMsgItem.ImageData, &msg)
if err != nil {
log.Fatalf("Error parsing XML: %v", err)
return
}
sendMsg := req.SendEmojiItem{
ToUserName: newMsgItem.TOUserName,
EmojiMd5: msg.Emoji.MD5,
EmojiSize: msg.Emoji.Len,
}
_, _ = currentReqInvoker.ForwardEmojiRequest(sendMsg.EmojiMd5, sendMsg.ToUserName, sendMsg.EmojiSize)
}
case <-wxfhm.endChan:
return
}
}
}

49
srv/wxface/iwxcache.go Normal file
View File

@@ -0,0 +1,49 @@
package wxface
import (
"xiawan/wx/clientsdk/baseinfo"
)
// IWXCache 缓存接口
type IWXCache interface {
// 设置Qrcode信息
SetQrcodeInfo(qrcodeInfo *baseinfo.QrcodeInfo)
// 获取二维码信息
GetQrcodeInfo() *baseinfo.QrcodeInfo
// 获取收藏缓存
GetFavInfoCache() *baseinfo.FavInfoCache
// 设置初始化联系人状态
SetInitContactFinished(bFlag bool)
// 设置初始化同步信息状态
SetInitNewSyncFinished(bFlag bool)
// 设置初始化收藏同步信息状态
SetInitFavSyncFinished(bFlag bool)
// 初始化联系人是否完成
IsInitContactFinished() bool
// 初始化同步信息是否完成
IsInitNewSyncFinished() bool
// 初始化同步信息是否完成
IsInitFavSyncFinished() bool
// 所有初始化是否完成
IsInitFinished() bool
// 设置发送使用说明初始化状态
SetIsInitFinished(bFlag bool)
// 使用说明命令
SendUsageByText()
// 发送使用说明
SendUsage()
// 扫码完成
SetScanFinish(bFlag bool)
// 设置初始化通讯录的标志
SetContactSeq(contactSeq uint32)
// 获取初始化通讯录的标志
GetContactSeq() uint32
// 新增联系人WXID列表
AddInitContactWxidList(contactWxidList []string)
// 获取指定数量的联系人微信ID列表
GetNextInitContactWxidList(count uint32) []string
// 获取全部联系人Wxid
GetAllContactList() []string
// 清空缓存
Clear()
}

60
srv/wxface/iwxconnect.go Normal file
View File

@@ -0,0 +1,60 @@
package wxface
import (
"xiawan/wx/srv"
)
// IWXConnect 微信链接接口
type IWXConnect interface {
// 开启
Start() error
// 关闭
Stop()
// 设置微信链接ID
SetWXConnID(wxConnID uint32)
// 获取WX链接ID
GetWXConnID() uint32
// 获取微信服务器
GetWXServer() IWXServer
// 获取微信帐号信息
GetWXAccount() *srv.WXAccount
// 获取缓存器
GetWXCache() IWXCache
// 获取调用器
GetWXReqInvoker() IWXReqInvoker
// 获取任务管理器
GetWXTaskMgr() IWXTaskMgr
// 获取同步管理器
GetWXSyncMgr() IWXSyncMgr
// 获取文件助手消息管理器
GetWXFileHelperMgr() IWXFileHelperMgr
// 获取好友消息管理器
GetWXFriendMsgMgr() IWXUserMsgMgr
// 判断是否处于链接状态
IsConnected() bool
// 发送给消息队列去处理
SendToWXMsgHandler(wxResp IWXResponse)
// 添加到长链接请求队列
SendToWXLongReqQueue(wxLongReq IWXLongRequest)
// 添加到长链接请求队列
SendWebTask(status string, taskType uint32)
// 等待 waitTimes后发送心跳包
SendHeartBeatWaitingSeconds(seconds uint32)
// 等待 waitTimes后发送二次登录包
SendAutoAuthWaitingMinutes(minutes uint32)
// 添加群二维码
AddGroupQrcodeData(fileData []byte, fileName string)
// 开始下载群二维码
DumpGroupQrcode()
// CheckOnLineStatus 检查在线状态
CheckOnLineStatus() bool
CheckOnLineStatusLogin() bool
// 获取上次心跳时间
GetHeartBeatTime() int64
// 设置上次心跳时间
SetHeartBeatTime(time int64)
// 关闭
StopShortReader()
}

View File

@@ -0,0 +1,13 @@
package wxface
// IWXConnectMgr 微信链接管理器
type IWXConnectMgr interface {
Add(wxConnect IWXConnect) // 添加链接
GetWXConnectByUserInfoUUID(userInfoUUID string) IWXConnect // 根据UserInfoUUID获取微信链接
GetWXConnectByWXID(wxid string) IWXConnect // 根据WXID获取微信链接
Remove(wxconn IWXConnect) // 删除连接
Len() int // 获取当前连接
ClearWXConn() // 删除并停止所有链接
ShowConnectInfo() string // 打印链接数量
GetConnectInfo() map[string]interface{} // 打印链接信息
}

View File

@@ -0,0 +1,14 @@
package wxface
// IWXFileHelperMgr 文件传输助手消息管理器
type IWXFileHelperMgr interface {
Start()
Stop()
AddNewTipMsg(newMsg string)
AddNewCardMsg(newMsg string)
AddImageMsg(imgData []byte)
// 转发图片
ForwardImageMsg(imgData []byte)
// 转发表情包
ForwardEmoticonMsg(imgData []byte)
}

View File

@@ -0,0 +1,29 @@
package wxface
import "xiawan/wx/clientsdk/baseinfo"
// IWXGrabHBMgr 自动抢红包管理器
type IWXGrabHBMgr interface {
// 开始抢红包协程
Start()
// 结束抢红包协程
Stop()
// 抢下一个红包
GrapNext()
// 添加红包项
AddHBItem(hbItem *baseinfo.HongBaoItem)
// 获取当前正在抢的红包项
GetCurrentHBItem() *baseinfo.HongBaoItem
// 是否开启自动抢红包功能
IsAutoGrap() bool
// 设置自动抢红包开关
SetAutoGrap(bFlag bool)
//设置抢红包自动回复
SetAutoReply(bFlag bool)
//设置延迟抢红包时间
SetDelayOpen(bFlag int)
//是否抢红包自动回复
IsAutoReply()
//获取延迟时间
GetDelayOpen()
}

View File

@@ -0,0 +1,7 @@
package wxface
// IWXLongRequest 微信请求
type IWXLongRequest interface {
GetOpcode() uint32
GetData() []byte
}

View File

@@ -0,0 +1,9 @@
package wxface
// IWXMsgHandler 微信响应处理器
type IWXMsgHandler interface {
AddRouter(respID uint32, wxRouter IWXRouter) // 为消息添加具体的处理逻辑
GetRouterByRespID(urlID uint32) IWXRouter // 获取对应的路由处理器
StartWorkerPool() // 启动worker工作池
SendWXRespToTaskQueue(response IWXResponse) // 将消息交给TaskQueue,由worker进行处理
}

344
srv/wxface/iwxreqinvoker.go Normal file
View File

@@ -0,0 +1,344 @@
package wxface
import (
"xiawan/wx/api/req"
"xiawan/wx/api/vo"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/protobuf/wechat"
)
// IWXReqInvoker 微信请求调用器
type IWXReqInvoker interface {
// 发送登录短信
SendWxBindOpMobileForRequest(OpCode int64, PhoneNumber string, VerifyCode string) (*wechat.BindOpMobileForRegResponse, error)
// 获取登录二维码
SendGetLoginQrcodeRequest() (*wechat.LoginQRCodeResponse, error)
// 获取登录二维码(直登)
SendGetLoginQrcodeRequestDirect() (*wechat.LoginQRCodeResponse, error)
// 获取登录二维码(绕过验证码)
SendGetLoginQrcodeRequestX() (*wechat.LoginQRCodeResponse, error)
// 检测登录二维码状态
SendCheckLoginQrcodeRequest(qrcodeUUID string, qrcodeKey []byte) error
// SendHybridManualAutoRequest
SendHybridManualAutoRequest(newPass string, wxID string, ver byte) error
// 发送登陆请求
SendManualAuthRequest(newPass string, wxID string) error
// 发送登录请求根据设备Id进行登录
SendManualAuthByDeviceIdRequest() error
// 发送获取deviceToken请求
SendDeviceTokenRequest() (*wechat.TrustResp, error)
// 获取设备
SendGetSafetyInfoRequest() (*wechat.GetSafetyInfoResponse, error)
// 删除设备
SendDelSafeDeviceRequest(deviceUUID string) (*wechat.DelSafeDeviceResponse, error)
// 获取硬件设备情况
SendGetBoundHardDeviceRequest() (*wechat.GetBoundHardDevicesResponse, error)
// 检测微信登录环境
SendCheckCanSetAliasRequest() (*wechat.CheckCanSetAliasResp, error)
// 发送心跳包
SendHeartBeatRequest() error
// 发送短链接心跳
SendHeartBeatShortRequest() (*wechat.HeartBeatResponse, int32, error)
// 发送同步请求; 真正的同步消息接口; 新消息都需要从此接口同步
SendNewSyncRequest(scene uint32) error
// 获取微信账号配置信息
SendGetProfileRequest() error
SendGetProfileNewRequest() (*wechat.GetProfileResponse, error)
// 同步消息
SendWxSyncMsg(key string) (*wechat.NewSyncResponse, error)
SendNewInitSyncRequest() error
// 会重新链接服务器 发送Token登陆请求
SendAutoAuthRequest() error
// 发送二维码二次登录; 扫码唤醒登录
SendPushQrLoginNotice() (*wechat.PushLoginURLResponse, error)
// 发送初始化联系人请求
SendInitContactRequest(contactSeq uint32) error
// 分页获取联系人
SendGetContactListPageRequest(CurrentWxcontactSeq uint32, CurrentChatRoomContactSeq uint32) (*wechat.InitContactResp, error)
// 批量获取联系人详情
SendBatchGetContactBriefInfoReq(userWxidList []string) error
// 获取联系人信息列表
SendGetContactRequest(userInfoList []string, antisPanTicketList []string, chatRoomWxidList []string, needResp bool) (*wechat.GetContactResponse, error)
// 获取联系人信息列表(优先本地缓存)
SendGetContactRequestForCache(userWxid string, isCache bool) (*wechat.ModContact, *wechat.VerifyUserValidTicket, error)
// 获取联系人信息列表
SendGetContactRequestForHB(userWxid string) (*wechat.GetContactResponse, error)
// 获取联系人信息列表List
SendGetContactRequestForList(userInfoList []string, roomWxIDList []string) (*wechat.GetContactResponse, error)
// 获取好友关系状态
SendGetFriendRelationRequest(userName string) (*wechat.MMBizJsApiGetUserOpenIdResponse, error)
// 接收红包
SendReceiveWxHBRequest(hbItem *baseinfo.HongBaoItem) error
// 打开红包
SendOpenWxHBRequest(hbItem *baseinfo.HongBaoItem, timingIdentifier string) error
// 拆红包
SendOpenRedEnvelopesRequest(hbItem *baseinfo.HongBaoItem) (*wechat.HongBaoRes, error)
// 接收转账
SendReceiveZZ(transferItem *baseinfo.TenPayReqItem, wcPayInfo *baseinfo.WCPayInfo2) error
// 创建红包
SendWXCreateRedPacketRequest(hbItem *baseinfo.RedPacket) (*wechat.HongBaoRes, error)
// 查看红包详情
SendRedEnvelopesDetailRequest(hbItem *baseinfo.HongBaoItem) (*wechat.HongBaoRes, error)
// 查看红包列表
SendGetRedPacketListRequest(hbItem *baseinfo.GetRedPacketList) (*wechat.HongBaoRes, error)
// 创建转账
SendCreatePreTransferRequest(transferItem *req.CreatePreTransfer) (*baseinfo.PreTransferResp, error)
// 确认转账
SendConfirmPreTransferRequest(transferItem *req.ConfirmPreTransfer) (*wechat.TenPayResponse, error)
// 发送消息
SendTextMsgRequest(toUserName string, content string, atWxIDList []string, ContentType int) (*wechat.NewSendMsgResponse, error)
// 发送消息给文件助手
SendTextMsgToFileHelperRequest(content string) error
// 发送名片给文件助手
SendCardMsgToFileHelperRequest(content string) error
// 发送Oplog请求
SendOplogRequest(modifyItems []*baseinfo.ModifyItem) error
// 发送企业Oplog请求
SendQWOpLogRequest(cmdId int64, value []byte) error
// 获取群/个人二维码(封号;限制功能)
SendGetQRCodeRequest(userName string) error
// 退出登陆
SendLogoutRequest() error
// 同步收藏
SendFavSyncRequest() error
SendFavSyncRequestResult() (*wechat.FavSyncResponse, error)
SendFavSyncListRequestResult(keyBuf string) (*wechat.SyncResponse, error)
// 获取收藏信息
SendGetFavInfoRequest() error
SendGetFavInfoRequestResult() (*wechat.GetFavInfoResponse, error)
// 获取收藏项详情
SendBatchGetFavItemRequest(favID uint32) error
SendBatchGetFavItemRequestResult(favID uint32) (*wechat.BatchGetFavItemResponse, error)
// 删除收藏
SendBatchDelFavItemRequest(favID uint32) error
SendBatchDelFavItemRequestResult(favID uint32) (*wechat.BatchDelFavItemResponse, error)
// 获取CdnDns信息
SendGetCDNDnsRequest() error
// 发送朋友圈
SendSnsPostRequest(postItem *baseinfo.SnsPostItem) error
// 发送朋友圈
SendSnsPostRequestNew(postItem *baseinfo.SnsPostItem) (*wechat.SnsPostResponse, error)
// 设置朋友圈可见天数
SetFriendCircleDays(postItem *req.SetFriendCircleDaysModel) error
// 操作朋友圈
SendSnsObjectOpRequest(opItems []*baseinfo.SnsObjectOpItem) (*wechat.SnsObjectOpResponse, error)
// 上传文件
UploadAppAttach(fileData []byte) (*wechat.UploadAppAttachResponse, error)
// 获取指定好友朋友圈
SendSnsUserPageRequest(userName string, firstPageMd5 string, maxID uint64, needResp bool) (*wechat.SnsUserPageResponseNew, error)
// 同步转发朋友圈
SendSnsPostRequestByXML(timeLineObj *baseinfo.TimelineObject, blackList []string) error
// 获取指定的朋友圈详情
SendSnsObjectDetailRequest(snsID uint64) (*wechat.SnsObject, error)
// 获取朋友圈首页
SendSnsTimeLineRequest(firstPageMD5 string, maxID uint64) error
SendSnsTimeLineRequestResult(firstPageMD5 string, maxID uint64) (*wechat.SnsTimeLineResponse, error)
// 发送评论/点赞请求
SendSnsCommentRequest(commentItem *baseinfo.SnsCommentItem) error
// 同步朋友圈
SendSnsSyncRequest(toHandler bool) (*wechat.SnsSyncResponse, error)
// 获取联系人标签列表
SendGetContactLabelListRequest(needResp bool) (*wechat.GetContactLabelListResponse, error)
// 添加标签
SendAddContactLabelRequest(newLabelList []string, needResp bool) (*wechat.AddContactLabelResponse, error)
// 删除标签
SendDelContactLabelRequest(labelId string) (*wechat.DelContactLabelResponse, error)
// 修改标签
SendModifyLabelRequest(userLabelList []baseinfo.UserLabelInfoItem) (*wechat.ModifyContactLabelListResponse, error)
// 查询钱包信息
SendBindQueryNewRequest(reqItem *baseinfo.TenPayReqItem) error
// 获取余额以及银行卡信息
SendBandCardRequest(reqItem *baseinfo.TenPayReqItem) (*wechat.TenPayResponse, error)
// 支付方法
SendTenPayRequest(reqItem *baseinfo.TenPayReqItem) (*wechat.TenPayResponse, error)
// 下载请求
SendCdnDownloadReuqest(downItem *baseinfo.DownMediaItem) (*baseinfo.CdnDownloadResponse, error)
// 下载图片
GetMsgBigImg(m req.DownloadParam) (*wechat.GetMsgImgResponse, error)
// 下载视频数据
GetMsgVideo(m req.DownloadParam) (*wechat.DownloadVideoResponse, error)
// Cdn上传高清图片
SendCdnSnsUploadImageReuqest(imgData []byte) (*baseinfo.CdnSnsImageUploadResponse, error)
// 发送CDN朋友圈视频下载请求
SendCdnSnsVideoDownloadReuqest(encKey uint64, tmpURL string) ([]byte, error)
// 发送CDN朋友圈上传视频请求
SendCdnSnsVideoUploadReuqest(videoData []byte, thumbData []byte) (*baseinfo.CdnSnsVideoUploadResponse, error)
// Cdn发送图片给好友
SendCdnUploadImageReuqest(imgData []byte, toUserName string) (bool, error)
// Cdn发送图片给好友并返回详细信息
SendCdnUploadImageReuqestWithDetails(imgData []byte, toUserName string) (*baseinfo.CdnImageUploadResponse, string, error)
// Cdn发送图片给好友并返回详细信息调试
SendCdnUploadImageReuqestWithSource(imgData []byte, toUserName string, sourceValue uint32) (*baseinfo.CdnImageUploadResponse, string, error)
// 发送图片
SendUploadImageNewRequest(imgData []byte, toUserName string) (*wechat.UploadMsgImgResponse, error)
// cdn发送视频
SendCdnUploadVideoRequest(toUserName string, imgData []byte, videoData []byte) (*baseinfo.CdnMsgVideoUploadResponse, error)
// Cdn发送图片给文件传输助手
SendImageToFileHelper(imgData []byte) (bool, error)
// 转发图片
ForwardCdnImageRequest(item baseinfo.ForwardImageItem) (*wechat.UploadMsgImgResponse, error)
// 转发视频
ForwardCdnVideoRequest(item baseinfo.ForwardVideoItem) (*wechat.UploadVideoResponse, error)
// 发送app消息
SendAppMessage(msgXml, toUSerName string, contentType uint32) (*wechat.SendAppMsgResponse, error)
// SendEmojiRequest 发送表情
SendEmojiRequest(md5 string, toUSerName string, length int32) (*wechat.SendAppMsgResponse, error)
// 发送表情new 动图
ForwardEmojiRequest(md5 string, toUSerName string, length int32) (*wechat.UploadEmojiResponse, error)
// 群发文字
SendGroupMassMsgTextRequest(toUserName []string, content string) (*wechat.MassSendResponse, error)
// 群发图片
SendGroupMassMsgImageRequest(toUserName []string, ImageBase64 []byte) (*wechat.MassSendResponse, error)
// 群拍一拍
SendSendPatRequest(chatRoomName string, toUserName string, scene int64) (*wechat.SendPatResponse, error)
// 下载语音
SendGetMsgVoiceRequest(toUserName, newMsgId, bufid string, length int) (*vo.DownloadVoiceData, error)
// 群发
// 设置群公告
SetChatRoomAnnouncementRequest(roomId, content string) (*wechat.SetChatRoomAnnouncementResponse, error)
// 获取群成员详细
GetChatroomMemberDetailRequest(roomId string) (*wechat.GetChatroomMemberDetailResponse, error)
// 同意进入群聊
ConsentToJoinGroupRequest(url string) (*wechat.JoinChatRoomResponse, error)
// 获取群详细
SetGetChatRoomInfoDetailRequest(roomId string) (*wechat.GetChatRoomInfoDetailRequest, error)
// 退出群聊
GetQuitChatroomRequest(chatRoomName string) error
// 创建群
SendCreateChatRoomRequest(topIc string, userList []string) (*wechat.CreateChatRoomResponse, error)
// 邀请群成员
SendInviteChatroomMembersRequest(chatRoomName string, userList []string) (*wechat.CreateChatRoomResponse, error)
// 添加好友进群
SendAddChatRoomMemberRequest(chatRoomName string, userList []string) (*wechat.AddChatRoomMemberResponse, error)
// 删除群成员
SendDelDelChatRoomMemberRequest(chatRoomName string, delUserList []string) (*wechat.DelChatRoomMemberResponse, error)
// 转让群
SendTransferGroupOwnerRequest(chatRoomName, newOwnerUserName string) (*wechat.TransferChatRoomOwnerResponse, error)
// 添加群管理
SendAddChatroomAdminRequest(chatRoomName string, userList []string) (*wechat.AddChatRoomAdminResponse, error)
// 删除群管理
SendDelChatroomAdminRequest(chatRoomName string, userList []string) (*wechat.DelChatRoomAdminResponse, error)
// 获取群列表
SendWXSyncContactRequest() (*vo.GroupData, error)
// 链接授权
GetA8KeyRequest(opCode, scene uint32, reqUrl string, getType baseinfo.GetA8KeyType) (*wechat.GetA8KeyResp, error)
// 小程序授权
JSLoginRequest(appId string) (*wechat.JSLoginResponse, error)
// 小程序云函数操作
JSOperateWxDataRequest(appId string, opt int32, data string) (*wechat.JSOperateWxDataResponse, error)
// APP 授权
SdkOauthAuthorizeRequest(appId string, sdkName string, packageName string) (*wechat.SdkOauthAuthorizeConfirmNewResp, error)
// 搜索好友
SendSearchContactRequest(opCode, fromScene, searchScene uint32, userName string) (*wechat.SearchContactResponse, error)
// 好友验证/加好友/关注公众号
VerifyUserRequest(opCode uint32, verifyContent string, scene int, V3, V4, ChatRoomUserName string) (*wechat.VerifyUserResponse, error)
// 上传手机通讯录好友
UploadMContact(mobile string, mobileList []string, opcode int32) (*wechat.UploadMContactResponse, error)
// 获取手机通讯录好友
GetMFriend() (*wechat.GetMFriendResponse, error)
// 获取证书
SendCertRequest() (*wechat.GetCertResponse, error)
// 发送二维码授权请求
SendQRConnectAuthorize(qrUrl string) (*wechat.QRConnectAuthorizeResp, error)
// 发送二维码授权请求确认
SendQRConnectAuthorizeConfirm(qrUrl string) (*wechat.SdkOauthAuthorizeConfirmNewResp, error)
// 授权链接
SendGetMpA8Request(url string, opcode uint32) (*wechat.GetA8KeyResp, error)
// 获取登录设备信息
SendOnlineInfo() (*wechat.GetOnlineInfoResponse, error)
// 获取(群聊/个人)二维码
SendGetQrCodeRequest(id string, style uint32) (*wechat.GetQRCodeResponse, error)
// 查看附近的人
SendGetPeopleNearbyResultRequest(longitude float32, latitude float32) (*wechat.LbsResponse, error)
// 撤销消息
SendRevokeMsgRequest(newMsgId string, clientMsgId uint64, toUserName string) (*wechat.RevokeMsgResponse, error)
// 撤回消息New
SendRevokeMsgRequestNew(m req.RevokeMsgModel) (*wechat.RevokeMsgResponse, error)
// 删除好友
SendDelContactRequest(userName string) error
// 修改资料
SendModifyUserInfoRequest(city, country, nickName, province, signature string, sex uint32, initFlag uint32) error
// 修改昵称
SendUpdateNickNameRequest(cmd uint32, val string) error
// 设置姓名
SetNickNameService(cmd uint32, val string) error
// 设置性别
SetSexService(val uint32, country string, city string, province string) error
// 修改加好友需要验证属性
UpdateAutopassRequest(SwitchType uint32) error
// 修改头像
UploadHeadImage(base64 string) (*wechat.UploadHDHeadImgResponse, error)
// 修改密码
SendChangePwdRequest(oldPwd, NewPwd string, OpCode uint32) (*wechat.BaseResponse, error)
// 修改备注
SendModifyRemarkRequest(userName string, remarkName string) error
// 发送语音
SendUploadVoiceRequest(toUserName string, voiceData string, voiceSecond, voiceFormat int32) (*wechat.UploadVoiceResponse, error)
// 设置微信号
SetWechatRequest(alisa string) (*wechat.GeneralSetResponse, error)
// 设置微信步数
UpdateStepNumberRequest(number uint64) (*wechat.UploadDeviceStepResponse, error)
// 获取步数列表
SendGetUserRankLikeCountRequest(rankId string) (*wechat.GetUserRankLikeCountResponse, error)
// 提取企业 WX 详情
SendQWContactRequest(openIm, chatRoom, t string) (*wechat.GetQYContactResponse, error)
// 提取全部的企业通寻录
SendQWSyncContactRequest() (*wechat.GetQYContactResponse, error)
// 备注企业
SendQWRemarkRequest(toUserName string, name string) error
// 创建企业群
SendQWCreateChatRoomRequest(userList []string) (*wechat.CreateQYChatRoomResponese, error)
// 搜手机或企业对外名片链接提取验证
SendQWSearchContactRequest(tg string, fromScene uint64, userName string) (*wechat.SearchQYContactResponse, error)
// 向企业微信打招呼
SendQWApplyAddContactRequest(toUserName, v1, Content string) error
// 单向加企业微信
SendQWAddContactRequest(toUserName, v1, Content string) error
// 拉取企业微信群
SendQWSyncChatRoomRequest(key string) (*vo.QYChatroomContactVo, error)
// 转让企业群
SendQWChatRoomTransferOwnerRequest(chatRoomName string, toUserName string) (*wechat.BaseResponse, error)
// 直接拉好友进群
SendQWAddChatRoomMemberRequest(chatRoomName string, toUserName []string) (*wechat.QYAddChatRoomMemberResponse, error)
// 发送群邀请链接
SendQWInviteChatRoomMemberRequest(chatRoomName string, toUserName []string) (*wechat.BaseResponse, error)
// 删除企业群群成员
SendQWDelChatRoomMemberRequest(chatRoomName string, toUserName []string) (*wechat.QYDelChatRoomMemberResponse, error)
// 提取企业群全部成员
SendQWGetChatRoomMemberRequest(chatRoomName string) (*wechat.GetQYChatroomMemberDetailResponse, error)
// 提取企业群名称公告设定等信息
SendQWGetChatroomInfoRequest(chatRoomName string) (*wechat.QYChatroomContactResponse, error)
// 提取企业群二维码
SendQWGetChatRoomQRRequest(chatRoomName string) (*wechat.QYGetQRCodeResponse, error)
// 增加企业管理员
SendQWAppointChatRoomAdminRequest(chatRoomName string, toUserName []string) (*wechat.TransferChatRoomOwnerResponse, error)
// 移除企业群管理员
SendQWDelChatRoomAdminRequest(chatRoomName string, toUserName []string) (*wechat.TransferChatRoomOwnerResponse, error)
// 同意进企业群
SendQWAcceptChatRoomRequest(link string, opcode uint32) (*wechat.GetA8KeyResp, error)
// 设定企业群管理审核进群
SendQWAdminAcceptJoinChatRoomSetRequest(chatRoomName string, p int64) (*wechat.TransferChatRoomOwnerResponse, error)
// 群管理批准进企业群 1
SendQWAdminAcceptJoinChatRoomRequest(chatRoomName, key, toUserName string, toUserNames []string) (*wechat.TransferChatRoomOwnerResponse, error)
// 修改企业群名称
SendQWModChatRoomNameRequest(chatRoomName, name string) (*wechat.TransferChatRoomOwnerResponse, error)
// 修改成员在群中呢称
SendQWModChatRoomMemberNickRequest(chatRoomName, name string) (*wechat.TransferChatRoomOwnerResponse, error)
// 发布企业群公告
SendQWChatRoomAnnounceRequest(chatRoomName, Announcement string) (*wechat.TransferChatRoomOwnerResponse, error)
// 删除企业群
SendQWDelChatRoomRequest(chatRoomName string) (*wechat.TransferChatRoomOwnerResponse, error)
// 视频号搜索
SendGetFinderSearchRequest(Index uint32, Userver int32, UserKey string, Uuid string) (*wechat.FinderSearchResponse, error)
// 视频号个人中心
SendFinderUserPrepareRequest(uServer int32) (*wechat.FinderUserPrepareResponse, error)
// 视频号关注
SendFinderFollowRequest(FinderUserName string, OpType int32, RefObjectId string, Cook string, Userver int32, PosterUsername string) (*wechat.FinderFollowResponse, error)
// 扫码事件上报 (CGI 8887)
SendScanQrcodeEventReportRequest(loginUrl string) (*wechat.ScanQrcodeEventReportRequest, error)
// 微信小店获取确认信息 (CGI 971)
SendExtDeviceLoginConfirmGetRequest(loginUrl string) (*wechat.ExtDeviceLoginConfirmGetResponse, error)
// 微信小店确认登录 (CGI 972)
SendExtDeviceLoginConfirmOkRequest(loginUrl string) (*wechat.ExtDeviceLoginConfirmOkResponse, error)
}

11
srv/wxface/iwxresponse.go Normal file
View File

@@ -0,0 +1,11 @@
package wxface
import "xiawan/wx/clientsdk/baseinfo"
// IWXResponse 微信请求接口
type IWXResponse interface {
// 获取请求数据
GetPackHeader() *baseinfo.PackHeader
// 获取WXConncet
GetWXConncet() IWXConnect
}

8
srv/wxface/iwxrouter.go Normal file
View File

@@ -0,0 +1,8 @@
package wxface
// IWXRouter 微信消息处理路由
type IWXRouter interface {
PreHandle(response IWXResponse) error //在处理conn业务之前的钩子方法
Handle(response IWXResponse) error //处理conn业务的方法
PostHandle(response IWXResponse) error //处理conn业务之后的钩子方法
}

17
srv/wxface/iwxserver.go Normal file
View File

@@ -0,0 +1,17 @@
package wxface
import "xiawan/wx/srv"
// IWXServer 微信服务
type IWXServer interface {
Start()
GetWXMsgHandler() IWXMsgHandler
GetWXConnectMgr() IWXConnectMgr
GetWXFileMgr() *srv.WXFileMgr
AddWXRouter(funcID uint32, wxRouter IWXRouter)
// UpdateExpiryDate 更新授权码过期时间
UpdateExpiryDate(key, expiryDate string)
// 更新禁用状态
UpdateDisable(key string, disable int)
}

12
srv/wxface/iwxsyncmgr.go Normal file
View File

@@ -0,0 +1,12 @@
package wxface
// IWXSyncMgr 消息同步管理器
type IWXSyncMgr interface {
Start()
Stop()
// 发送同步请求
SendNewSyncRequest()
SendFavSyncRequest()
SendSyncInitRequest()
}

7
srv/wxface/iwxtaskmgr.go Normal file
View File

@@ -0,0 +1,7 @@
package wxface
// IWXTaskMgr 任务管理器
type IWXTaskMgr interface {
Start()
Stop()
}

View File

@@ -0,0 +1,8 @@
package wxface
type IWXUserMsgMgr interface {
Start()
Stop()
AddNewTextMsg(newMsg, toUSerName string)
AddImageMsg(imgData []byte, toUSerName string)
}

83
srv/wxfilemgr.go Normal file
View File

@@ -0,0 +1,83 @@
package srv
import (
"os"
)
var baseDIR = string("/.source")
// FileInfo 文件信息
type FileInfo struct {
FileData []byte
FileName string
}
// WXFileMgr 文件管理器
type WXFileMgr struct {
fileInfoChan chan *FileInfo
currentDirName string
endChan chan bool
}
// NewWXFileMgr 新建文件管理器
func NewWXFileMgr() *WXFileMgr {
// 先创建文件夹
os.Mkdir(baseDIR, os.ModePerm)
return &WXFileMgr{
fileInfoChan: make(chan *FileInfo, 10000),
currentDirName: "",
endChan: make(chan bool, 1),
}
}
// Start 开启
func (wxfg *WXFileMgr) Start() {
// go wxfg.doDeal()
}
// Stop 关闭
func (wxfg *WXFileMgr) Stop() {
// wxfg.endChan <- true
}
// AddGroupQrcodeData 添加群二维码
func (wxfg *WXFileMgr) AddGroupQrcodeData(fileData []byte, fileName string) {
// newFileInfo := &FileInfo{}
// newFileInfo.FileData = fileData
// newFileInfo.FileName = fileName
// wxfg.fileInfoChan <- newFileInfo
}
func (wxfg *WXFileMgr) makeSureDir() {
// if wxfg.currentDirName == time.Now().Format("2006-01-02") {
// return
// }
// wxfg.currentDirName = time.Now().Format("2006-01-02")
// subDirPath := baseDIR + "/" + wxfg.currentDirName
// os.Mkdir(subDirPath, os.ModePerm)
}
func (wxfg *WXFileMgr) doDeal() {
// for {
// select {
// case fileInfo := <-wxfg.fileInfoChan:
// wxfg.writeToFile(fileInfo)
// case <-wxfg.endChan:
// return
// }
// }
}
// writeToFile 写入文件
func (wxfg *WXFileMgr) writeToFile(fileInfo *FileInfo) {
// // 确保目录存在
// wxfg.makeSureDir()
// tmpFilePath := baseDIR + "/" + wxfg.currentDirName + "/" + fileInfo.FileName
// // 如果文件存在
// _, err := os.Lstat(tmpFilePath)
// if err == nil {
// return
// }
// baseutils.WriteToFile(fileInfo.FileData, tmpFilePath)
}

View File

@@ -0,0 +1,34 @@
package wxrouter
import (
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXAddContactLabelRouter 心跳包响应路由
type WXAddContactLabelRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXAddContactLabelRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
// 解析退出登陆响应包
var addLabelResp wechat.AddContactLabelResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &addLabelResp)
if err != nil {
// 请求出问题了,应该关闭链接
currentWXConn.Stop()
return err
}
// 跟新标签列表
_, _ = currentReqInvoker.SendGetContactLabelListRequest(false)
return nil
}

146
srv/wxrouter/wxautoauth.go Normal file
View File

@@ -0,0 +1,146 @@
package wxrouter
import (
"errors"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXAutoAuthRouter token登陆响应路由
type WXAutoAuthRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (glqr *WXAutoAuthRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("(glqr *WXAutoAuthRouter) Handle(wxResp wxface.IWXResponse)")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
//currentInvoker := currentWXConn.GetWXReqInvoker()
// 解析 token 登陆响应
var manualResponse wechat.ManualAuthResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &manualResponse)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
retCode := manualResponse.GetBaseResponse().GetRet()
currentUserInfo.UpdateLastAuthTime() // 更新上次自动登录的二次登录时间
// Mysql 提交登录日志
db.SetLoginLog("AutoAuth", currentWXAccount, manualResponse.GetBaseResponse().GetErrMsg().GetStr(), retCode)
switch retCode {
case baseinfo.MMOk: //success
WXAutoAuthSuccess(&manualResponse, currentWXConn)
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
currentWXAccount.SetLoginState(baseinfo.MMLoginStateOnLine)
return nil
case baseinfo.MMErrDropped: //出现用户主动退出获取被T下线在线状态不存在需要调用Push
// -2023 登录出现错误可重新登录
/*if strings.Contains(manualResponse.GetBaseResponse().GetErrMsg().GetStr(), "登录出现错误") {
currentWXConn.SendAutoAuthWaitingMinutes(5)
return nil
}*/
//fmt.Println(hex.EncodeToString(currentUserInfo.AutoAuthKey))
// 登录状态改为登录后退出
currentWXAccount.SetLoginState(baseinfo.MMLoginStateLogout)
// 保存登录状态到数据库
db.UpdateLoginStatus(currentUserInfo.WxId, int32(currentWXAccount.GetLoginState()), "你已退出微信")
// 关闭重新启动,再次发送登陆请求
currentWXConn.Stop()
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
return errors.New("WXAutoAuthRouter retCode = - 2023,err: " + manualResponse.GetBaseResponse().GetErrMsg().GetStr())
case -100:
// 关闭重新启动,再次发送登陆请求
currentWXConn.Stop()
// 登录状态改为未登录
currentWXAccount.SetLoginState(baseinfo.MMLoginStateNoLogin)
errMsg := manualResponse.GetBaseResponse().GetErrMsg().GetStr()
// 保存登录状态到数据库
db.UpdateLoginStatus(currentUserInfo.GetUserName(), int32(currentUserInfo.GetLoginState()), errMsg)
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
return errors.New("WXAutoAuthRouter retCode = - 100,err: " + manualResponse.GetBaseResponse().GetErrMsg().GetStr())
case -6:
// 登录状态改为未登录
currentWXAccount.SetLoginState(baseinfo.MMLoginStateNoLogin)
// 关闭重新启动,再次发送登陆请求
currentWXConn.Stop()
// 保存登录状态到数据库
errMsg := manualResponse.GetBaseResponse().GetErrMsg().GetStr()
db.UpdateLoginStatus(currentUserInfo.WxId, int32(currentUserInfo.GetLoginState()), errMsg)
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
return errors.New("WXAutoAuthRouter err: " + errMsg)
default:
currentWXAccount.SetLoginState(baseinfo.MMLoginStateNoLogin)
// 关闭重新启动,再次发送登陆请求
currentWXConn.Stop()
// 保存登录状态到数据库
errMsg := manualResponse.GetBaseResponse().GetErrMsg().GetStr()
db.UpdateLoginStatus(currentUserInfo.WxId, int32(currentUserInfo.GetLoginState()), errMsg)
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
return errors.New("WXAutoAuthRouter err: " + errMsg)
}
}
func WXAutoAuthSuccess(manualResponse *wechat.ManualAuthResponse, connect wxface.IWXConnect) {
account := connect.GetWXAccount()
userInfo := account.GetUserInfo()
reqInvoker := connect.GetWXReqInvoker()
// 获取aesKey
userInfo.ConsultSessionKey(manualResponse.AuthParam.EcdhKey.Key.GetBuffer(), manualResponse.AuthParam.SessionKey.Key)
// SetAutoKey
userInfo.SetAutoKey(manualResponse.AuthParam.AutoAuthKey.Buffer)
// SetNetworkSect
userInfo.SetNetworkSect(manualResponse.DnsInfo)
// 登录成功可与服务器重新建立长链接
isConnected := connect.IsConnected()
if !isConnected {
connect.Start()
}
// 是否需要初始化cdn信息
if userInfo.CheckCdn() {
// 获取CDNDns信息
_ = reqInvoker.SendGetCDNDnsRequest()
}
// 获取账号的wxProfile
_ = reqInvoker.SendGetProfileRequest()
// 登录状态改为在线
account.SetLoginState(baseinfo.MMLoginStateOnLine)
// 发送心跳
GetHeartBeatTime := connect.GetHeartBeatTime()
nowTime := time.Now().Unix()
HeartBeatWaiting := 175
if GetHeartBeatTime == 0 {
HeartBeatWaiting = 1
} else if nowTime-GetHeartBeatTime >= 60 {
HeartBeatWaiting = 1
} else {
HeartBeatWaiting = 175 - int(nowTime-GetHeartBeatTime)
}
connect.SendHeartBeatWaitingSeconds(uint32(HeartBeatWaiting))
// 等待发送二次登录30分钟
connect.SendAutoAuthWaitingMinutes(60)
// 保存UserInfo
db.UpdateUserInfo(userInfo)
// 更新状态到数据库
db.UpdateLoginStatus(userInfo.GetUserName(), int32(account.GetLoginState()), "登录成功!")
}

View File

@@ -0,0 +1,97 @@
package wxrouter
import (
"strings"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXBatchGetContactBriefInfoReqRouter 批量获取联系人信息
type WXBatchGetContactBriefInfoReqRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (wxbgcbirr *WXBatchGetContactBriefInfoReqRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentWXCache := currentWXConn.GetWXCache()
currentUserInfo := currentWXAccount.GetUserInfo()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
uuid := currentWXAccount.GetUserInfo().UUID
// 解析批量获取联系人信息响应包
var briefInfoResp wechat.BatchGetContactBriefInfoResp
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &briefInfoResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// fmt.Println("批量获取联系人信息响应包:")
tmpContactList := briefInfoResp.GetContactList()
tmpCount := len(tmpContactList)
for index := 0; index < tmpCount; index++ {
tmpContactBriefInfo := tmpContactList[index]
userName := tmpContactBriefInfo.GetUsername()
tmpModContact := tmpContactBriefInfo.GetContact()
nickName := tmpModContact.GetNickName().GetStr()
// 公众号
hasExternalInfo := false
if len(tmpModContact.GetCustomizedInfo().GetExternalInfo()) > 0 {
hasExternalInfo = true
}
if strings.HasPrefix(userName, "gh_") || hasExternalInfo {
if len(nickName) <= 0 {
continue
}
// currentWXAccount.AddWXGhContact(tmpModContact)
continue
}
// 群
if strings.HasSuffix(userName, "@chatroom") {
// currentWXAccount.AddWXGroup(tmpModContact)
db.SaveOrUpdateContact(tmpModContact, uuid)
continue
}
// 好友
// currentWXAccount.AddWXFriendContact(tmpModContact)
if !isSystemWXID(userName) {
db.SaveOrUpdateContact(tmpModContact, uuid)
}
}
// 获取剩余20个微信ID列表
nextWxidList := currentWXCache.GetNextInitContactWxidList(20)
if len(nextWxidList) > 0 {
currentReqInvoker.SendBatchGetContactBriefInfoReq(nextWxidList)
} else {
// 初始化完成
db.UpdateInitContactStatus(uuid, 1)
// if !currentWXCache.IsInitContactFinished() {
// currentWXCache.SetInitContactFinished(true)
// }
}
return nil
}
func isSystemWXID(userName string) bool {
if userName == "qqmail" ||
userName == "qmessage" ||
userName == "mphelper" ||
userName == "filehelper" ||
userName == "weixin" ||
userName == "floatbottle" ||
userName == "fmessage" ||
userName == "medianote" {
return true
}
return false
}

View File

@@ -0,0 +1,69 @@
package wxrouter
import (
"encoding/xml"
"time"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"github.com/lunny/log"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXBatchGetFavItemRouter 获取单条收藏详情响应路由
type WXBatchGetFavItemRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXBatchGetFavItemRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
currentSnsTransTask := taskMgr.GetSnsTransTask()
// 解析 获取单条收藏响应包
var batchGetFavItemResp wechat.BatchGetFavItemResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &batchGetFavItemResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 解析单条收藏响应详情
count := batchGetFavItemResp.GetCount()
objectList := batchGetFavItemResp.GetObjectList()
for index := uint32(0); index < count; index++ {
tmpFavObject := objectList[index]
// 反序列化
objStr := tmpFavObject.GetObject()
if len(objStr) <= 0 {
continue
}
favItem := &baseinfo.FavItem{}
err := xml.Unmarshal([]byte(objStr), favItem)
if err != nil {
log.Error("xml 解析error!")
continue
}
if tmpFavObject == nil {
continue
}
// 自动转发
favItem.FavItemID = tmpFavObject.GetFavId()
currentSnsTransTask.AddFavItem(favItem)
//收藏发送mq
if favItem != nil {
log.Info("----收藏id=", favItem.FavItemID)
go db.PublishFavItem(currentWXAccount, favItem)
}
}
return nil
}

View File

@@ -0,0 +1,156 @@
package wxrouter
import (
"errors"
"strings"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"github.com/gogo/protobuf/proto"
)
// WXCheckQrcodeRouter 检测二维码状态响应路由
type WXCheckQrcodeRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (cqr *WXCheckQrcodeRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXCheckQrcodeRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentAccount := currentWXConn.GetWXAccount()
currentInvoker := currentWXConn.GetWXReqInvoker()
currentCache := currentWXConn.GetWXCache()
currentUserInfo := currentAccount.GetUserInfo()
// 解析检测二维码响应
var checkResp wechat.CheckLoginQRCodeResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &checkResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
currentWXConn.Stop()
return err
}
// 解密出现问题,说明协议出现了问题
qrcodeInfo := currentCache.GetQrcodeInfo()
retBytes, err := baseutils.AesDecryptByteKey(checkResp.LoginQrcodeNotifyPkg.NotifyData.Buffer, qrcodeInfo.QrCodeKey)
if err != nil {
// 请求出问题了,应该关闭链接
currentWXConn.Stop()
return err
}
lgQrNotify := &wechat.LoginQRCodeNotify{}
err = proto.Unmarshal(retBytes, lgQrNotify)
if err != nil {
// 请求出问题了,应该关闭链接
currentWXConn.Stop()
return err
}
errMsg := ""
if lgQrNotify.GetState() == 2 {
// 每秒查一次数据库太频繁了,这里改为仅在扫码成功后查一次
bind, _ := db.IsLicenseBind(currentUserInfo.UUID)
if bind != nil && len(lgQrNotify.GetWxid()) > 0 && lgQrNotify.GetWxid() != bind.WxId {
errMsg = "该授权码(KEY)已绑定其他微信号, 请换一个KEY"
}
}
// 添加扫码状态缓存
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: lgQrNotify,
Msg: errMsg,
})
if len(errMsg) != 0 {
// 当前 License 已绑定其他微信号, 需要更换 License
currentWXConn.Stop()
return errors.New(errMsg)
}
str := byteArrayToString(retBytes)
ticketValue := extractTicketValue(str)
if ticketValue != "" {
// 赋值ticketValue
currentUserInfo.Ticket = ticketValue
currentUserInfo.LoginDataInfo.Ticket = ticketValue
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: lgQrNotify,
Msg: "请提交验证码后登录",
Ret: int32(0),
})
}
if lgQrNotify.GetState() == 2 {
// 扫码成功发送登录包
err := currentInvoker.SendManualAuthRequest(lgQrNotify.GetWxnewpass(), lgQrNotify.GetWxid())
if err != nil {
// 在停止连接前,确保扫码状态被正确持久化保存
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: lgQrNotify,
Msg: "登录包发送失败:" + err.Error(),
Ret: int32(300),
})
// 确保状态被保存后再停止连接
time.Sleep(time.Millisecond * 200)
currentWXConn.Stop()
return err
}
go func() {
// 3 秒后发送指令
time.Sleep(3 * time.Second)
currentCache.SetScanFinish(true)
// 扫码完成后再次更新状态缓存,确保登录成功的状态被持久化
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: lgQrNotify,
Msg: "登录成功",
Ret: int32(0),
})
}()
} else if lgQrNotify.GetState() == 4 {
// 在停止连接前,确保状态被正确保存
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: lgQrNotify,
Msg: "二维码失效",
Ret: int32(301),
})
// 确保状态被保存后再停止连接
time.Sleep(time.Millisecond * 200)
currentWXConn.Stop()
return errors.New("WXCheckQrcodeRouter err: 二维码失效")
} else {
currentUserInfo.HeadURL = lgQrNotify.GetHeadImgUrl()
err := currentWXConn.GetWXReqInvoker().SendCheckLoginQrcodeRequest(qrcodeInfo.QrCodeUUID, qrcodeInfo.QrCodeKey)
if err != nil {
currentWXConn.Stop()
return err
}
time.Sleep(time.Second)
}
return nil
}
func byteArrayToString(data []byte) string {
return string(data)
}
func extractTicketValue(str string) string {
index := strings.Index(str, "ticket=")
if index == -1 {
return ""
}
return str[index+len("ticket="):]
}

92
srv/wxrouter/wxfavsync.go Normal file
View File

@@ -0,0 +1,92 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"github.com/gogo/protobuf/proto"
)
// WXFavSyncRouter 同步收藏响应路由
type WXFavSyncRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXFavSyncRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
currentWXCache := currentWXConn.GetWXCache()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
// currentSyncMgr := currentWXConn.GetWXSyncMgr()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
currentSnsTransTask := taskMgr.GetSnsTransTask()
// 解析同步收藏响应包
var favSyncResp wechat.FavSyncResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &favSyncResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 保存同步key下次使用
// 解析同步收藏列表
cmdList := favSyncResp.GetCmdList()
tmpCount := cmdList.GetCount()
tmpKeyBuf := favSyncResp.KeyBuf.GetBuffer()
if len(tmpKeyBuf) <= 0 {
currentReqInvoker.SendGetFavInfoRequest()
return nil
}
currentUserInfo.FavSyncKey = tmpKeyBuf
if tmpCount < 1024 {
if !currentWXCache.IsInitFavSyncFinished() {
// 同步完成
currentWXCache.SetInitFavSyncFinished(true)
}
}
currentReqInvoker.SendGetFavInfoRequest()
// 如果还没开启转发收藏
if !currentSnsTransTask.IsAutoRelay() {
return nil
}
itemList := cmdList.GetItemList()
// 获取收藏缓存
favInfoCache := currentWXCache.GetFavInfoCache()
for index := uint32(0); index < tmpCount; index++ {
tmpItem := itemList[index]
// 判断是否是新增的收藏
tmpCmdID := tmpItem.GetCmdId()
if tmpCmdID != baseinfo.MMFavSyncCmdAddItem {
return nil
}
// 处理新增的收藏
addItem := &wechat.AddFavItem{}
err := proto.Unmarshal(tmpItem.GetCmdBuf().GetData(), addItem)
if err != nil {
return err
}
// 判断是否重复
if favInfoCache.LastFavID() == addItem.GetFavId() && favInfoCache.LastFavUpdateTime() == addItem.GetUpdateTime() {
continue
}
favInfoCache.SetLastFavID(addItem.GetFavId())
favInfoCache.SetLastFavUpdateTime(addItem.GetUpdateTime())
// 获取收藏详情
return currentReqInvoker.SendBatchGetFavItemRequest(favInfoCache.LastFavID())
}
return nil
}

View File

@@ -0,0 +1,39 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXGetCDNDnsRouter 获取CDN dns信息
type WXGetCDNDnsRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXGetCDNDnsRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXGetCDNDnsRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析获取CdnDns信息响应包
var getCdnDNSResp wechat.GetCDNDnsResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &getCdnDNSResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 跟新DNS信息
currentUserInfo.APPDnsInfo = getCdnDNSResp.GetAppDnsInfo()
currentUserInfo.FAKEDnsInfo = getCdnDNSResp.GetFakeDnsInfo()
currentUserInfo.SNSDnsInfo = getCdnDNSResp.GetSnsDnsInfo()
currentUserInfo.DNSInfo = getCdnDNSResp.GetDnsInfo()
return nil
}

View File

@@ -0,0 +1,65 @@
package wxrouter
import (
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/defines"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXGetContactLabelListRouter 获取联系人标签路由
type WXGetContactLabelListRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXGetContactLabelListRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
// 解析退出登陆响应包
var getContableListResp wechat.GetContactLabelListResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &getContableListResp)
if err != nil {
// 请求出问题了,应该关闭链接
currentWXConn.Stop()
return err
}
// 先判断是否有 收藏转发屏蔽,同步转发屏蔽
hasFavLabel := false
hasSyncLabel := false
labelCount := getContableListResp.GetLabelCount()
labelPairList := getContableListResp.GetLabelPairList()
// 跟新标签列表
currentWXAccount.SetLabelPairList(labelPairList)
// 判断是否有收藏转发屏蔽组、同步转发屏蔽,如果没有就添加
for index := uint32(0); index < labelCount; index++ {
tmpLabelPair := labelPairList[index]
if tmpLabelPair.GetLabelName() == defines.MFavTransShieldLabelName {
hasFavLabel = true
continue
}
if tmpLabelPair.GetLabelName() == defines.MSyncTransShieldLabelName {
hasSyncLabel = true
continue
}
}
// 创建 屏蔽组
newLabelNameList := []string{}
if !hasFavLabel {
newLabelNameList = append(newLabelNameList, defines.MFavTransShieldLabelName)
}
if !hasSyncLabel {
newLabelNameList = append(newLabelNameList, defines.MSyncTransShieldLabelName)
}
if len(newLabelNameList) > 0 {
currentReqInvoker.SendAddContactLabelRequest(newLabelNameList, false)
}
return nil
}

View File

@@ -0,0 +1,42 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXGetFavInfoRouter 心跳包响应路由
type WXGetFavInfoRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXGetFavInfoRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXGetFavInfoRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentWXCache := currentWXConn.GetWXCache()
currentSyncMgr := currentWXConn.GetWXSyncMgr()
// 解析退出登陆响应包
var logoutResp wechat.GetFavInfoResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &logoutResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 如果没有初始化完成,则继续初始化
if !currentWXCache.IsInitFavSyncFinished() {
currentSyncMgr.SendFavSyncRequest()
return nil
}
return nil
}

View File

@@ -0,0 +1,42 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXGetProfileRouter 获取帐号信息路由
type WXGetProfileRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXGetProfileRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXGetProfileRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析账号Profile响应
userProfileResp := new(wechat.GetProfileResponse)
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), userProfileResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 设置昵称和头像
currentUserInfo.NickName = userProfileResp.GetUserInfo().GetNickName().GetStr()
currentUserInfo.HeadURL = userProfileResp.GetUserInfoExt().GetSmallHeadImgUrl()
// 设置UserProfile信息
currentWXAccount.SetUserProfile(userProfileResp)
// 更新用户信息
db.UpdateUserInfoByPhone(userProfileResp.GetUserInfo())
return nil
}

View File

@@ -0,0 +1,51 @@
package wxrouter
import (
"strings"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXGetQrcodeRouter 获取个人/群二维码响应
type WXGetQrcodeRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXGetQrcodeRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXGetQrcodeRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析获取二维码响应包
var getQrcodeResp wechat.GetQRCodeResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &getQrcodeResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 如果获取失败
if getQrcodeResp.GetBaseResponse().GetRet() != 0 {
return nil
}
// 判断是否是群聊
tmpUserName := string(wxResp.GetPackHeader().ReqData)
if !strings.HasSuffix(tmpUserName, "@chatroom") {
return nil
}
// 保存群二维码
retList := strings.Split(tmpUserName, "@")
qrcodeFileName := retList[0] + ".png"
currentWXConn.AddGroupQrcodeData(getQrcodeResp.Qrcode.GetBuffer(), qrcodeFileName)
return nil
}

View File

@@ -0,0 +1,45 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"github.com/lunny/log"
)
// WXHeartBeatRouter 心跳包响应路由
type WXHeartBeatRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXHeartBeatRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXHeartBeatRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析心跳包响应
var hearBeatResp wechat.HeartBeatResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &hearBeatResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
if hearBeatResp.GetBaseResponse().GetRet() == 0 {
log.Printf("[%s],[%s] HeartBeatSuccess \n", currentUserInfo.GetUserName(), currentUserInfo.NickName)
currentWXConn.StopShortReader()
// 获取当前时间 秒
nowTime := time.Now().Unix()
currentWXConn.SetHeartBeatTime(nowTime)
}
//fmt.Println("心跳--->", hearBeatResp.GetBaseResponse().GetRet())
// 等待 NextTime后再次发送心跳包
currentWXConn.SendHeartBeatWaitingSeconds(hearBeatResp.GetNextTime())
return nil
}

View File

@@ -0,0 +1,49 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXInitContactRouter 初始化联系人响应路由
type WXInitContactRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXInitContactRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentWXCache := currentWXConn.GetWXCache()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
// 解析初始化通讯录响应
var initContactResp wechat.InitContactResp
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &initContactResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 设置ContactSeq
newContactSeq := initContactResp.GetCurrentWxcontactSeq()
currentWXCache.SetContactSeq(newContactSeq)
// 判断 联系人数量
contactUserNameList := initContactResp.GetContactUsernameList()
currentWXCache.AddInitContactWxidList(contactUserNameList)
if len(contactUserNameList) >= 100 {
currentReqInvoker.SendInitContactRequest(newContactSeq)
} else {
// 初始化通讯录
tmpWxidList := currentWXCache.GetNextInitContactWxidList(20)
currentReqInvoker.SendBatchGetContactBriefInfoReq(tmpWxidList)
}
return nil
}

39
srv/wxrouter/wxlogout.go Normal file
View File

@@ -0,0 +1,39 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXLogoutRouter 心跳包响应路由
type WXLogoutRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXLogoutRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXLogoutRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析退出登陆响应包
var logoutResp wechat.LogOutResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &logoutResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
currentUserInfo.SetLoginState(baseinfo.MMLoginQrcodeStateNone)
db.UpdateLoginStatus(currentUserInfo.GetUserName(), int32(currentUserInfo.GetLoginState()), "LogOut")
// 退出登陆成功
currentWXConn.Stop()
return nil
}

View File

@@ -0,0 +1,148 @@
package wxrouter
import (
"errors"
"fmt"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXManualAuthRouter 扫码登陆响应路由
type WXManualAuthRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (glqr *WXManualAuthRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXManualAuthRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentCache := currentWXConn.GetWXCache()
currentInvoker := currentWXConn.GetWXReqInvoker()
currentUserInfo := currentWXAccount.GetUserInfo()
currentPackHeader := wxResp.GetPackHeader()
currentWXSyncMgr := currentWXConn.GetWXSyncMgr()
//currentWXFileHelperMgr := currentWXConn.GetWXFileHelperMgr()
// 解析扫码登陆响应
var manualResponse wechat.ManualAuthResponse
err := clientsdk.ParseResponseData(currentUserInfo, currentPackHeader, &manualResponse)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
retCode := manualResponse.GetBaseResponse().GetRet()
// 重定向
switch retCode {
case baseinfo.MMErrIdcRedirect:
currentUserInfo.ShortHost = manualResponse.GetDnsInfo().GetNewHostList().GetList()[1].GetSubstitute()
currentUserInfo.LongHost = manualResponse.GetDnsInfo().GetNewHostList().GetList()[0].GetSubstitute()
//提交登录日志
db.SetLoginLog("ManualAuth", currentWXAccount, fmt.Sprintf("重定向登录 ShortHost :%s,LongHost:%s", currentUserInfo.ShortHost, currentUserInfo.LongHost), retCode)
// 关闭重新启动,再次发送登陆请求
currentWXConn.Stop()
time.Sleep(2 * time.Second)
_ = currentWXConn.Start()
qrcodeInfo := currentCache.GetQrcodeInfo()
if qrcodeInfo.QrCodePassword == "" {
return currentInvoker.SendManualAuthByDeviceIdRequest()
} else {
return currentInvoker.SendManualAuthRequest(qrcodeInfo.QrCodePassword, qrcodeInfo.QrCodeWxId)
}
case baseinfo.MMOk:
//取基本信息
accountInfo := manualResponse.GetAccountInfo()
currentUserInfo.SetWxId(accountInfo.GetWxid())
currentUserInfo.NickName = accountInfo.GetNickName()
// 协商密钥
currentUserInfo.ConsultSessionKey(manualResponse.GetAuthParam().GetEcdhKey().GetKey().GetBuffer(), manualResponse.AuthParam.SessionKey.Key)
// AutoAuthKey
currentUserInfo.SetAutoKey(manualResponse.AuthParam.AutoAuthKey.Buffer)
// 随机一个字符红包AesKey
currentUserInfo.GenHBKey()
// 如果数据库有存储这个微信号的信息
if len(currentUserInfo.FavSyncKey) <= 0 {
// 刷新收藏同步Key
oldUserInfo := db.GetUserInfoByWXID(currentUserInfo.WxId)
if oldUserInfo != nil {
currentUserInfo.FavSyncKey = oldUserInfo.FavSyncKey
}
}
// 开始发送心跳包
currentWXConn.SendHeartBeatWaitingSeconds(10)
// 设置登陆状态,发送提示
currentWXAccount.SetLoginState(baseinfo.MMLoginStateOnLine)
go func() {
// 如果开了切号, 不管登录什么, 数据库里面都是iPad的设备信息
db.UpdateLoginStatus(currentUserInfo.GetUserName(), int32(currentWXAccount.GetLoginState()), "登录成功!")
// 获取账号的wxProfile
currentInvoker.SendGetProfileRequest()
// 获取联系人标签列表
currentInvoker.SendGetContactLabelListRequest(false)
// 获取CDNDns信息
currentInvoker.SendGetCDNDnsRequest()
// 开始发送二次登录包
currentWXConn.SendAutoAuthWaitingMinutes(60)
// 初始化通讯录
// contactSeq := currentCache.GetContactSeq()
// currentInvoker.SendInitContactRequest(contactSeq)
currentCache.SetInitContactFinished(true)
// 同步消息
currentWXSyncMgr.SendNewSyncRequest()
// 同步收藏
// currentWXSyncMgr.SendFavSyncRequest()
// 打印当前链接状态
currentWXConn.GetWXServer().GetWXConnectMgr().ShowConnectInfo()
//currentWXFileHelperMgr.AddNewTipMsg("上线成功!")
//currentWXFileHelperMgr.AddNewTipMsg("系统正在初始化...")
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
//Mysql 提交登录日志
currentUserInfo.UpdateLastLoginTime() // 更新上次手动登录时间
// 手动登录成功: 扫码登录、62登录、16登录等等
db.SetLoginLog("ManualAuth", currentWXAccount, "登录成功!", retCode)
fmt.Println("currentUserInfo扫码登录响应路由", currentUserInfo.DeviceInfo)
db.SaveUserInfo(currentUserInfo)
/*time.Sleep(time.Second * 10)
currentWXConn.Stop()*/
}()
default:
// 登陆失败
errMsg := manualResponse.GetBaseResponse().GetErrMsg().GetStr()
baseutils.PrintLog(errMsg)
// 将登录失败的错误信息保存到状态缓存中这样前端就能获取到验证码URL等错误信息
db.AddCheckStatusCache(currentUserInfo.UUID, &baseinfo.CheckLoginQrCodeResult{
LoginQRCodeNotify: nil,
Ret: retCode,
Msg: "登录失败:" + errMsg,
})
//Mysql 提交登录日志
//db.SetLoginLog("ManualAuth", currentWXAccount, errMsg, retCode)
//Mysql 保存用户信息
//db.SaveUserInfo(currentUserInfo)
//Mysql 保存登录状态
//db.UpdateLoginStatus(currentUserInfo.GetUserName(), retCode, errMsg)
currentWXConn.Stop()
//redis 发布消息 发布登录状态
//db.PublishLoginState(currentWXAccount.GetUserInfo().UUID, currentWXAccount.GetLoginState())
fmt.Println("登录失败:" + errMsg)
return errors.New("login failed" + errMsg)
}
return nil
}

121
srv/wxrouter/wxnewinit.go Normal file
View File

@@ -0,0 +1,121 @@
package wxrouter
import (
"bytes"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/db"
"xiawan/wx/db/table"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"github.com/gogo/protobuf/proto"
"github.com/lunny/log"
)
// WXNewInitRouter 获取二维码响应路由
type WXNewInitRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXNewInitRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentWXCache := currentWXConn.GetWXCache()
// currentSyncMgr := currentWXConn.GetWXSyncMgr()
// 同步响应
var initResp wechat.NewInitResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &initResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 跟新同步Key
syncKey := initResp.GetCurrentSyncKey().GetBuffer()
syncKeyMgr := currentUserInfo.SyncKeyMgr()
syncKeyMgr.SetMaxKey(initResp.GetMaxSyncKey())
syncKeyMgr.SetMaxKey(initResp.GetCurrentSyncKey())
//保存SyncKey
if len(currentUserInfo.SyncKey) <= 0 || !bytes.Equal(currentUserInfo.SyncKey, syncKey) {
currentUserInfo.SyncKey = syncKey
db.UpdateUserInfo(currentUserInfo)
}
if initResp.GetContinueFlag() <= 0 {
if !currentWXCache.IsInitNewSyncFinished() {
// 禁用历史消息推送,不设置初始化完成标志
currentWXCache.SetInitNewSyncFinished(true)
}
}
// 如果没有同步到数据则返回
cmdList := initResp.GetCmdList()
syncCount := initResp.GetCmdCount()
//log.Info(initResp.GetContinueFlag())
//log.Info(initResp.GetContinueFlag(), syncCount)
//redis 发布结构体
messageResp := new(table.SyncMessageResponse)
// 遍历同步的信息和群
itemList := cmdList
for index := uint32(0); index < syncCount; index++ {
item := itemList[index]
itemID := item.GetCmdId()
// 同步到消息
if itemID == baseinfo.CmdIDAddMsg {
print(currentWXCache.IsInitNewSyncFinished(), 111)
if !currentWXCache.IsInitNewSyncFinished() {
// fmt.Println("跳过初始化历史消息 - IsInitNewSyncFinished=false")
continue
}
// fmt.Println("处理初始化消息 - IsInitNewSyncFinished=true")
addMsg := &wechat.AddMsg{}
err := proto.Unmarshal(item.CmdBuf.Data, addMsg)
if err != nil {
baseutils.PrintLog(err.Error())
continue
}
go dealAddMsgRun(currentWXConn, addMsg, "init")
}
// 同步到联系人
if itemID == baseinfo.CmdIDModContact {
contact := new(wechat.ModContact)
err := proto.Unmarshal(item.GetCmdBuf().GetData(), contact)
if err != nil {
continue
}
log.Info("dealModContact")
// dealModContact(currentWXConn, contact)
}
// 删除联系人
if itemID == baseinfo.CmdIDDelContact {
delContact := &wechat.DelContact{}
err := proto.Unmarshal(item.CmdBuf.Data, delContact)
if err != nil {
continue
}
// go dealDelContact(currentWXConn, delContact.GetUserName().GetStr())
}
messageResp.SetMessage(item.GetCmdBuf().GetData(), int32(itemID))
}
//发布同步信息消息
_ = db.PublishSyncMsgWxMessage(currentWXAccount, *messageResp)
// 如果数量超过10条则继续同步
if !currentWXCache.IsInitNewSyncFinished() {
// currentSyncMgr.SendSyncInitRequest()
}
return nil
}

1662
srv/wxrouter/wxnewsync.go Normal file

File diff suppressed because it is too large Load Diff

55
srv/wxrouter/wxopenhb.go Normal file
View File

@@ -0,0 +1,55 @@
package wxrouter
import (
"encoding/json"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXOpenHBRouter 接收红包响应处理器
type WXOpenHBRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXOpenHBRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXOpenHBRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
currentGrapHBMgr := taskMgr.GetGrabHBTask()
// 红包响应
var hongbaoResp wechat.HongBaoRes
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &hongbaoResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
defer currentGrapHBMgr.GrapNext()
// 如果 返回错误码
if hongbaoResp.GetErrorType() != 0 {
baseutils.PrintLog("WXReceiveHBRouter err: " + hongbaoResp.GetErrorMsg())
return nil
}
// 解析
retHongBaoOpenResp := &baseinfo.HongBaoOpenResp{}
err = json.Unmarshal(hongbaoResp.GetRetText().GetBuffer(), retHongBaoOpenResp)
if err != nil {
return err
}
// 抢红包成功 可以增加一条记录
return nil
}

View File

@@ -0,0 +1,48 @@
package wxrouter
import (
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXPushQrCodeLoginRouter 二维码二次登录
type WXPushQrCodeLoginRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXPushQrCodeLoginRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXPushQrCodeLoginRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析获取二维码响应包
var getQrcodeResp wechat.PushLoginURLResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &getQrcodeResp)
if err != nil {
currentWXConn.Stop()
return err
}
//第一次push 成功后没有点击确认 第二次push 会报 -2017 需要重新扫码登录
if getQrcodeResp.GetBaseResponse().GetRet() == -2017 {
// 更改登录状态为未登录状态
currentWXAccount.SetLoginState(baseinfo.MMLoginStateNoLogin)
// Mysql 更新状态
db.UpdateLoginStatus(currentUserInfo.GetUserName(), int32(baseinfo.MMLoginQrcodeStateNone), "需要重新登录pushLoginResp.RetCode -2017")
return nil
}
// 如果获取失败
if getQrcodeResp.GetBaseResponse().GetRet() != 0 {
return nil
}
return nil
}

View File

@@ -0,0 +1,69 @@
package wxrouter
import (
"encoding/json"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXReceiveHBRouter 接收红包响应处理器
type WXReceiveHBRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXReceiveHBRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXReceiveHBRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
currentGrabHBMgr := taskMgr.GetGrabHBTask()
// 心跳包响应
var hongbaoResp wechat.HongBaoRes
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &hongbaoResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 如果 返回错误码
if hongbaoResp.GetErrorType() != 0 {
baseutils.PrintLog("WXReceiveHBRouter err: " + hongbaoResp.GetErrorMsg())
// 抢下一个红包
currentGrabHBMgr.GrapNext()
return nil
}
// 解析
retHongBaoReceiveResp := &baseinfo.HongBaoReceiverResp{}
err = json.Unmarshal(hongbaoResp.GetRetText().GetBuffer(), retHongBaoReceiveResp)
baseutils.PrintLog("WXReceiveHBRouter retHongBaoReceiveResp: " + retHongBaoReceiveResp.TimingIdentifier)
if err != nil {
// 抢下一个红包
currentGrabHBMgr.GrapNext()
return err
}
// 如果都没问题,就打开红包
currentHBItem := currentGrabHBMgr.GetCurrentHBItem()
err = currentReqInvoker.SendOpenWxHBRequest(currentHBItem, retHongBaoReceiveResp.TimingIdentifier)
if err != nil {
// 抢下一个红包
currentGrabHBMgr.GrapNext()
return err
}
return nil
}

View File

@@ -0,0 +1,34 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXSnsCommentRouter 发送朋友圈响应路由
type WXSnsCommentRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXSnsCommentRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXSnsCommentRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析发送朋友圈响应包
var snsCommentResp wechat.SnsCommentResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &snsCommentResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
return nil
}

33
srv/wxrouter/wxsnspost.go Normal file
View File

@@ -0,0 +1,33 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXSnsPostRouter 发送朋友圈响应路由
type WXSnsPostRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXSnsPostRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
// 解析发送朋友圈响应包
var snsPostResp wechat.SnsPostResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &snsPostResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
return nil
}

57
srv/wxrouter/wxsnssync.go Normal file
View File

@@ -0,0 +1,57 @@
package wxrouter
import (
"errors"
"strconv"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXSnsSyncRouter 同步朋友圈
type WXSnsSyncRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (ssr *WXSnsSyncRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXSnsSyncRouter Handle")
currentWXConn := wxResp.GetWXConncet()
//currentTaskMgr := currentWXConn.GetWXTaskMgr()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
//taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
//currentSnsTransTask := taskMgr.GetSnsTransTask()
currentReqInvoker := currentWXConn.GetWXReqInvoker()
// 解析 同步朋友圈响应包
var snsSyncResp wechat.SnsSyncResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &snsSyncResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 如果请求失败
retCode := snsSyncResp.GetBaseResponse().GetRet()
errMsg := snsSyncResp.GetBaseResponse().GetErrMsg().GetStr()
if retCode != baseinfo.MMOk {
return errors.New("WXSnsSyncRouter err:" + strconv.Itoa(int(retCode)) + " msg = " + errMsg)
}
// 跟新key
currentUserInfo.SnsSyncKey = snsSyncResp.GetKeyBuf().GetBuffer()
// 遍历同步到的朋友圈
tmpCmdList := snsSyncResp.GetCmdList()
tmpCount := tmpCmdList.GetCount()
if tmpCount > 0 {
_, _ = currentReqInvoker.SendSnsSyncRequest(true)
}
return nil
}

View File

@@ -0,0 +1,67 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
)
// WXSnsTimeLineRouter 心跳包响应路由
type WXSnsTimeLineRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (oplog *WXSnsTimeLineRouter) Handle(wxResp wxface.IWXResponse) error {
defer wxcore.TryE("WXSnsTimeLineRouter Handle")
currentWXConn := wxResp.GetWXConncet()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
snsTask := taskMgr.GetSnsTask()
// 解析退出登陆响应包
var timeLineResp wechat.SnsTimeLineResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &timeLineResp)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
currentTime := snsTask.GetCurrentCreateTime()
tmpObjList := timeLineResp.GetObjectList()
tmpCount := len(tmpObjList)
// 判断是否有初始化
if currentTime == 0 {
snsTask.SetCurrentCreateTime(1)
if tmpCount > 0 {
snsTask.SetCurrentCreateTime(tmpObjList[0].GetCreateTime())
}
return nil
}
// 自动添加评论
for index := 0; index < tmpCount; index++ {
tmpSnsObj := tmpObjList[index]
if tmpSnsObj.GetCreateTime() <= currentTime {
break
}
if snsTask.IsAutoThumbUP() {
snsTask.AddCommentItemObj(tmpSnsObj)
}
if snsTask.IsAutoComment() {
snsTask.AddCommentItemObjcomment(tmpSnsObj)
}
}
// 设置最新的朋友圈创建时间
if tmpCount > 0 {
snsTask.SetCurrentCreateTime(tmpObjList[0].GetCreateTime())
}
return nil
}

View File

@@ -0,0 +1,77 @@
package wxrouter
import (
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"github.com/gogo/protobuf/proto"
)
// WXSnsUserPageRouter 获取好友朋友圈路由
type WXSnsUserPageRouter struct {
wxcore.WXBaseRouter
}
// Handle 处理conn业务的方法
func (hbr *WXSnsUserPageRouter) Handle(wxResp wxface.IWXResponse) error {
currentWXConn := wxResp.GetWXConncet()
currentTaskMgr := currentWXConn.GetWXTaskMgr()
currentWXAccount := currentWXConn.GetWXAccount()
currentUserInfo := currentWXAccount.GetUserInfo()
taskMgr, _ := currentTaskMgr.(*wxcore.WXTaskMgr)
currentSnsTransTask := taskMgr.GetSnsTransTask()
// 解析 获取好友朋友圈响应
var userPageResponse wechat.SnsUserPageResponse
err := clientsdk.ParseResponseData(currentUserInfo, wxResp.GetPackHeader(), &userPageResponse)
if err != nil {
// 请求出问题了,判断是否掉线,并重连
time.Sleep(1 * time.Second)
go currentWXConn.CheckOnLineStatusLogin()
return err
}
// 关联
tmpRequest := &wechat.SnsUserPageRequest{}
proto.Unmarshal(wxResp.GetPackHeader().ReqData, tmpRequest)
transItem := currentSnsTransTask.GetTransFriendItem(tmpRequest.GetUsername())
if transItem == nil {
return nil
}
// 初始化
if !transItem.IsInited {
// 初始化ID
transItem.FirstPageMd5 = userPageResponse.GetFirstPageMd5()
objectCount := userPageResponse.GetObjectCount()
if objectCount > 0 {
objectList := userPageResponse.GetObjectList()
transItem.CreateTime = objectList[0].GetCreateTime()
}
transItem.IsInited = true
return nil
}
// 转发新的朋友圈
objectCount := userPageResponse.GetObjectCount()
objectList := userPageResponse.GetObjectList()
for index := uint32(0); index < objectCount; index++ {
tmpObject := objectList[index]
if tmpObject.GetCreateTime() > transItem.CreateTime {
// 转发
currentSnsTransTask.AddSyncItem(tmpObject)
continue
}
break
}
// 跟新设置
if objectCount > 0 {
transItem.FirstPageMd5 = userPageResponse.GetFirstPageMd5()
transItem.CreateTime = objectList[0].GetCreateTime()
}
return nil
}

View File

@@ -0,0 +1,21 @@
package wxtask
import (
"xiawan/wx/protobuf/wechat"
)
// MessageWrapper 消息包装结构体用于WebSocket和callback消息同步时包含UUID信息
type MessageWrapper struct {
Key string `json:"key"` // UUID标识用于区分是哪个微信收到的消息
Message *wechat.AddMsg `json:"message"` // 原始消息内容
Type string `json:"type"` // 消息类型,如"message"、"contact_sync"等
}
// NewMessageWrapper 创建新的消息包装器
func NewMessageWrapper(key string, message *wechat.AddMsg, msgType string) *MessageWrapper {
return &MessageWrapper{
Key: key,
Message: message,
Type: msgType,
}
}

469
srv/wxtask/wxfriendtask.go Normal file
View File

@@ -0,0 +1,469 @@
package wxtask
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxface"
)
// WXFriendTask 微信好友任务
type WXFriendTask struct {
wxConn wxface.IWXConnect
friendForList []*wechat.ModContact
friendForZombieFansList []*wechat.ModContact
labels []*wechat.LabelPair
waitTimes uint32 // 毫秒
isZombieFansDet bool // 检测僵死粉任务
isZombieFansDel bool // 删除僵死粉任务
}
// NewWXFriendTask 新建一个好友任务
func NewWXFriendTask(wxConn wxface.IWXConnect) *WXFriendTask {
return &WXFriendTask{
wxConn: wxConn,
friendForList: make([]*wechat.ModContact, 0),
friendForZombieFansList: make([]*wechat.ModContact, 0),
labels: make([]*wechat.LabelPair, 0),
isZombieFansDet: false,
isZombieFansDel: false,
waitTimes: uint32(1000), // 间隔 1000 毫秒
}
}
// 获取下一个待验证的好友
func (gpt *WXFriendTask) getNextFriendForContact() *wechat.ModContact {
if len(gpt.friendForList) <= 0 {
return nil
}
retContact := gpt.friendForList[0]
gpt.friendForList = gpt.friendForList[1:]
return retContact
}
// 获取 1500 - 2500 毫秒的随机数
func (gpt *WXFriendTask) getRandWaitTimes() int {
// 设置随机数种子
rand.Seed(time.Now().UnixNano())
// 生成 [1500, 2500] 之间的随机数
min := 1500
max := 2500
randomNumber := rand.Intn(max-min+1) + min
return randomNumber
}
// 获取 2000 - 4000 毫秒的随机数
func (gpt *WXFriendTask) getRandWaitTimes2() int {
// 设置随机数种子
rand.Seed(time.Now().UnixNano())
// 生成 [2000, 4000] 之间的随机数
min := 2000
max := 4000
randomNumber := rand.Intn(max-min+1) + min
return randomNumber
}
// 获取 5000 - 12000 毫秒的随机数5秒 - 12秒
func (gpt *WXFriendTask) getRandWaitTimes3() int {
// 设置随机数种子
rand.Seed(time.Now().UnixNano())
// 生成 [5000, 12000] 之间的随机数
min := 5000
max := 12000
randomNumber := rand.Intn(max-min+1) + min
return randomNumber
}
// 10 - 20秒 删除一个
// 获取 10000 - 20000 毫秒的随机数
func (gpt *WXFriendTask) getRandWaitTimes4() int {
// 设置随机数种子
rand.Seed(time.Now().UnixNano())
// 生成 [10000, 20000] 之间的随机数
min := 10000
max := 20000
randomNumber := rand.Intn(max-min+1) + min
return randomNumber
}
// 添加至标签 删除我的人 拉黑我的人
func (gpt *WXFriendTask) addTagForContact(labeName string, friendWxids []string) {
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
// 判断存在则删除,不存在则添加
labels := gpt.getTagForList()
// 当前标签
currentLabel := make([]*wechat.LabelPair, 0)
for _, label := range labels {
if label.GetLabelName() == labeName {
// 删除
currentWXReqInvoker.SendDelContactLabelRequest(strconv.FormatUint(uint64(label.GetLabelId()), 10))
break
}
}
if len(currentLabel) <= 0 {
// 添加标签
labelNames := make([]string, 0)
labelNames = append(labelNames, labeName)
resp, err := currentWXReqInvoker.SendAddContactLabelRequest(labelNames, true)
if err != nil {
return
}
currentLabel = resp.GetLabelPairList()
}
// 修改标签
if len(currentLabel) > 0 {
UserLabelList := make([]baseinfo.UserLabelInfoItem, 0)
for _, friendWxid := range friendWxids {
userLabel := baseinfo.UserLabelInfoItem{
UserName: friendWxid,
LabelIDList: strconv.FormatUint(uint64(currentLabel[0].GetLabelId()), 10),
}
UserLabelList = append(UserLabelList, userLabel)
}
currentWXReqInvoker.SendModifyLabelRequest(UserLabelList)
}
}
// 获取全部标签
func (gpt *WXFriendTask) getTagForList() []*wechat.LabelPair {
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
// 获取标签
resp, err := currentWXReqInvoker.SendGetContactLabelListRequest(true)
if err != nil {
return make([]*wechat.LabelPair, 0)
}
return resp.GetLabelPairList()
}
// 获取剩余僵死粉数量
// GetLessCount 获取剩余群聊数量
func (gpt *WXFriendTask) GetLessCount() uint32 {
return uint32(len(gpt.friendForZombieFansList))
}
// StartFriendTask 开启好友检测任务
func (gpt *WXFriendTask) StartFriendTask(taskType int) bool {
// currentWXAccount := gpt.wxConn.GetWXAccount()
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
currentWXFileHelperMgr := gpt.wxConn.GetWXFileHelperMgr()
// 如果正在执行执行任务,则返回
if gpt.isZombieFansDet {
tipText := "正在检测僵死粉,请稍后"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
return false
}
// 如果正在执行删除任务,则返回
if gpt.isZombieFansDel {
tipText := "正在清理僵死粉,请稍后"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
return false
}
if taskType == 1 {
gpt.isZombieFansDet = true
tmpTip := "执行检测僵死粉\n开始加载好友列表……"
currentWXFileHelperMgr.AddNewTipMsg(tmpTip)
}
if taskType == 2 {
gpt.isZombieFansDel = true
tmpTip := "执行清理僵死粉\n开始加载好友……"
currentWXFileHelperMgr.AddNewTipMsg(tmpTip)
}
gpt.friendForList = make([]*wechat.ModContact, 0)
gpt.friendForZombieFansList = make([]*wechat.ModContact, 0)
friendWxids := gpt.GetFriendWxidList()
// friendList := make([]*wechat.ModContact, 0)
go func() {
if len(friendWxids) <= 0 {
tipText := "好友检索完成,总数 0 个,无需操作"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
gpt.isZombieFansDel = false
gpt.isZombieFansDet = false
return
}
// 好友检索完成,好友总数:
tipText := "好友加载完成!!\n开始检测好友请耐心等待……"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
for i := 0; i < len(friendWxids); i += 1 {
// 获取详情
friend := new(wechat.ModContact)
friend.UserName = new(wechat.SKBuiltinString)
friend.Remark = new(wechat.SKBuiltinString)
friend.UserName.Str = new(string)
*friend.UserName.Str = friendWxids[i]
gpt.friendForList = append(gpt.friendForList, friend)
}
temFriendCount := 0
for {
// 先判断是否还处于链接当中
if !gpt.wxConn.IsConnected() {
gpt.isZombieFansDel = false
gpt.isZombieFansDet = false
return
}
// 获取下一个好友
tmpModContact := gpt.getNextFriendForContact()
temFriendCount++
if tmpModContact == nil {
zembieFansCount := len(gpt.friendForZombieFansList)
friendForZombieFansList := make([]*wechat.ModContact, 0)
friendNamesDel := ""
friendWxidsDel := make([]string, 0)
// 拉黑我的人
friendFroBlack := ""
friendWxidsBlack := make([]string, 0)
if zembieFansCount > 0 {
// jsonStr0, _ := json.Marshal(gpt.friendForList)
// fmt.Println(string(jsonStr0))
for i := 0; i < zembieFansCount; i += 20 {
end := i + 20
if end > len(gpt.friendForZombieFansList) {
end = len(gpt.friendForZombieFansList)
}
oldFriends := gpt.friendForZombieFansList[i:end]
friendWxids := make([]string, 0)
for _, tmpFriend := range oldFriends {
friendWxids = append(friendWxids, tmpFriend.GetUserName().GetStr())
}
// 获取详情
contactList, err := currentWXReqInvoker.SendGetContactRequestForList(friendWxids, nil)
if err != nil {
fmt.Println("获取好友详情失败:", err)
continue
}
// 先判断是否还处于链接当中
if !gpt.wxConn.IsConnected() {
gpt.isZombieFansDel = false
gpt.isZombieFansDet = false
return
}
// friends := contactList.GetContactList()
verifyUserList := contactList.GetVerifyUserValidTicketList()
friends := contactList.GetContactList()
// 转 json 并写入文件
// jsonStr, _ := json.Marshal(friends)
// fmt.Println(string(jsonStr))
// jsonStr2, _ := json.Marshal(verifyUserList)
// fmt.Println(string(jsonStr2))
count := len(friends)
for index := 0; index < count; index++ {
friendItem := friends[index]
verifyUserItem := verifyUserList[index]
CardWxId := friendItem.GetUserName().GetStr()
CardNickName := friendItem.GetNickName().GetStr()
// CardAlias := friendItem.GetAlias()
// 联系人
// 如果是僵死粉
if strings.HasPrefix(verifyUserItem.GetAntispamticket(), "v4_") && friendItem.GetHeadImgMd5() != "" {
if friendNamesDel != "" {
friendNamesDel += "\n"
}
friendItem.Remark = new(wechat.SKBuiltinString)
friendItem.Remark.Str = new(string)
*friendItem.Remark.Str = "删除我的人"
friendNamesDel += "删除我的人"
friendNamesDel += ("【" + CardNickName + "】")
friendWxidsDel = append(friendWxidsDel, CardWxId)
// baseutils.PrintLog("僵尸粉:" + tmpContact.GetNickName().GetStr())
} else {
// 拉黑
if friendFroBlack != "" {
friendFroBlack += "\n"
}
friendItem.Remark = new(wechat.SKBuiltinString)
friendItem.Remark.Str = new(string)
*friendItem.Remark.Str = "拉黑我的人"
friendFroBlack += "拉黑我的人"
friendFroBlack += ("【" + CardNickName + "】")
friendWxidsBlack = append(friendWxidsBlack, CardWxId)
}
friendForZombieFansList = append(friendForZombieFansList, friendItem)
}
// 暂停 1.5 - 2.5 秒
randWaitTime := gpt.getRandWaitTimes()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
}
}
// 添加到标签
// 发送名单列表
if friendNamesDel != "" {
gpt.addTagForContact("删除我的人", friendWxidsDel)
currentWXFileHelperMgr.AddNewTipMsg(friendNamesDel)
}
if friendFroBlack != "" {
gpt.addTagForContact("拉黑我的人", friendWxidsBlack)
currentWXFileHelperMgr.AddNewTipMsg(friendFroBlack)
}
tipText := "检测完成 \n"
tipText += "删除我的人 - 【" + strconv.Itoa(len(friendWxidsDel)) + "】个\n"
tipText += "拉黑我的人 - 【" + strconv.Itoa(len(friendWxidsBlack)) + "】个\n"
tipText += "僵死粉已标记到标签中,以便查看"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
gpt.isZombieFansDet = false
if gpt.isZombieFansDel {
tipText := "由于官方限制,删除好友所需时间较长,请耐心等待……,请勿退出 Ipad 登录!!"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
// 暂停 1 - 2 分钟
randWaitTime := gpt.getRandWaitTimes3()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
// 删除僵死粉
for _, friendWxid := range friendWxidsDel {
// 删除
err := currentWXReqInvoker.SendDelContactRequest(friendWxid)
if err != nil {
gpt.isZombieFansDet = false
gpt.isZombieFansDel = false
fmt.Printf("删除好友 del friend err %+v", err)
return
}
for _, friendRemak := range friendForZombieFansList {
if friendRemak.GetUserName().GetStr() == friendWxid {
tipText := "删除-删除我的人【" + friendRemak.GetNickName().GetStr() + "】"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
}
}
// 暂停 10s - 20s
randWaitTime := gpt.getRandWaitTimes4()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
}
for _, friendWxid := range friendWxidsBlack {
// 删除
err := currentWXReqInvoker.SendDelContactRequest(friendWxid)
if err != nil {
gpt.isZombieFansDet = false
gpt.isZombieFansDel = false
fmt.Printf("删除好友 del friend err %+v", err)
return
}
for _, friendRemak := range friendForZombieFansList {
if friendRemak.GetUserName().GetStr() == friendWxid {
tipText := "删除-拉黑我的人【" + friendRemak.GetNickName().GetStr() + "】"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
}
}
// 暂停 2 - 5 分钟
randWaitTime := gpt.getRandWaitTimes3()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
}
gpt.isZombieFansDet = false
gpt.isZombieFansDel = false
tipText = "清理完成【已清理】:" + strconv.Itoa(len(friendWxidsDel)+len(friendWxidsBlack))
currentWXFileHelperMgr.AddNewTipMsg(tipText)
}
return
}
// 判断每次超过 25 个好友,则返回%分比进度
if temFriendCount%25 == 0 {
// 取整 temFriendCount / len(friendWxids) * 100 去掉小数点
numCount := int(float64(temFriendCount) / float64(len(friendWxids)) * 100)
tipText := "已检测【" + strconv.Itoa(numCount) + "%】好友"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
}
// 获取好友关系请求
friendUserName := tmpModContact.GetUserName().GetStr()
// 微信团队跳过
if friendUserName == "Weixin" {
continue
}
// 判断是否是僵死粉
resp, err := currentWXReqInvoker.SendGetFriendRelationRequest(friendUserName)
if err != nil {
continue
}
if resp.GetFriendRelation() == 0 {
continue
}
// 1删除 4自己拉黑 5被拉黑
if resp.GetFriendRelation() == 1 {
tmpModContact.Remark.Str = new(string)
*tmpModContact.Remark.Str = "删除我的人"
// 添加到僵尸粉列表
gpt.friendForZombieFansList = append(gpt.friendForZombieFansList, tmpModContact)
}
if resp.GetFriendRelation() == 5 {
tmpModContact.Remark.Str = new(string)
*tmpModContact.Remark.Str = "拉黑我的人"
// 添加到僵尸粉列表
gpt.friendForZombieFansList = append(gpt.friendForZombieFansList, tmpModContact)
}
// 暂停 1.5 - 2.5 秒
randWaitTime := gpt.getRandWaitTimes()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
}
}()
return true
}
// 获取联系人
func (gpt *WXFriendTask) GetFriendWxidList() []string {
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
currentWXFileHelperMgr := gpt.wxConn.GetWXFileHelperMgr()
CurrentWxcontactSeq := uint32(0)
CurrentChatRoomContactSeq := uint32(0)
friendUserNames := make([]string, 0)
pages := 0
// 死循环
for {
pages++
resp, err := currentWXReqInvoker.SendGetContactListPageRequest(CurrentWxcontactSeq, CurrentChatRoomContactSeq)
if err != nil {
return friendUserNames
}
UserNameList := resp.GetContactUsernameList()
if resp.GetContactUsernameList() == nil {
return friendUserNames
}
if len(UserNameList) == 0 {
return friendUserNames
}
tipText := "正在加载第 " + strconv.Itoa(pages) + " 页"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
for _, contactUserName := range UserNameList {
if contactUserName == "weixin" ||
contactUserName == "fmessage" ||
contactUserName == "medianote" ||
contactUserName == "floatbottle" ||
contactUserName == "qqmail" ||
contactUserName == "filehelper" ||
contactUserName == "mphelper" ||
contactUserName == "qmessage" {
continue
}
// 判断 gh 开头
if strings.HasPrefix(contactUserName, "gh_") {
continue
}
// 判断 @chatroom 结尾
if strings.HasSuffix(contactUserName, "@chatroom") {
continue
}
friendUserNames = append(friendUserNames, contactUserName)
}
CurrentWxcontactSeq = resp.GetCurrentWxcontactSeq()
CurrentChatRoomContactSeq = resp.GetCurrentChatRoomContactSeq()
// 暂停 1.5 - 2.5 秒
randWaitTime := gpt.getRandWaitTimes()
time.Sleep(time.Duration(randWaitTime) * time.Millisecond)
}
}

357
srv/wxtask/wxgrabhbtask.go Normal file
View File

@@ -0,0 +1,357 @@
package wxtask
import (
"sync"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"xiawan/wx/srv/wxface"
)
// WXGrabHBTask 抢红包管理器
type WXGrabHBTask struct {
wxConn wxface.IWXConnect
// 待领红包项列表
hongBaoItemChan chan *baseinfo.HongBaoItem
// 可不可以抢红包
canDoNext chan bool
// 结束标志
endChan chan bool
// 是否正在运行
isRunning bool
// 当前在抢的红包
currentHBItem *baseinfo.HongBaoItem
// autoGrap
autoGropFlag bool
//延迟抢红包
delayOpen int
//是否自动回复感谢
autoReply bool
//感谢内容
autoReplyContent string
//是否过滤测挂
filterCheats bool
// 过滤测挂关键字
filterCheatsKey string
//是否过滤文字/字母包
filterText bool
// 接收个人红包
autoPerson bool
// 接收转账
autoTransfer bool
//接收转账后是否自动回复
autoTransferReply bool
//接收转账后自动回复的内容
autoTransferReplyContent string
//群是否置顶
topGroup map[string]string
// 红包统计开关
hbStat bool
// 转账统计开关
transStat bool
// 是否只抢置顶群false 不抢置顶群, true 只抢置顶群)
notTopGroup bool
mu sync.Mutex // 互斥锁
// 是否开启
isStart bool
}
// NewWXGrabHBTask 新建抢红包管理器
func NewWXGrabHBTask(wxConn wxface.IWXConnect) *WXGrabHBTask {
return &WXGrabHBTask{
wxConn: wxConn,
hongBaoItemChan: make(chan *baseinfo.HongBaoItem, 200),
canDoNext: make(chan bool, 1),
endChan: make(chan bool, 1),
isRunning: false,
autoGropFlag: false,
delayOpen: 0,
autoReply: true,
autoReplyContent: "",
filterCheats: false,
filterText: false,
autoPerson: false,
autoTransfer: false,
filterCheatsKey: "测,挂,cg,死,罚,踢,专",
autoTransferReply: true,
autoTransferReplyContent: "",
topGroup: make(map[string]string),
hbStat: false,
transStat: false,
notTopGroup: false,
mu: sync.Mutex{},
isStart: false,
}
}
func (ghbm *WXGrabHBTask) grapHB() {
for {
select {
case ghbm.currentHBItem = <-ghbm.hongBaoItemChan:
// 直接发送抢红包请求,不等待响应
go ghbm.wxConn.GetWXReqInvoker().SendOpenRedEnvelopesRequest(ghbm.currentHBItem)
// 立即准备抢下一个红包,不等待当前红包处理完成
ghbm.GrapNext()
return
case <-ghbm.endChan:
//ghbm.endChan <- true
//close(ghbm.endChan)
return
}
}
}
// startGrap 开始抢红包
func (ghbm *WXGrabHBTask) startGrap() {
for {
select {
case <-ghbm.canDoNext:
ghbm.grapHB()
case <-ghbm.endChan:
return
}
}
}
// Start 开始抢红包协程
func (ghbm *WXGrabHBTask) Start() {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
if ghbm.isStart {
return
}
ghbm.isStart = true
go ghbm.startGrap()
ghbm.canDoNext <- true
}
// Stop 结束抢红包协程
func (ghbm *WXGrabHBTask) Stop() {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
if !ghbm.isStart {
return
}
ghbm.isStart = false
ghbm.endChan <- true
//close(ghbm.endChan)
}
// GrapNext 抢下一个红包
func (ghbm *WXGrabHBTask) GrapNext() {
ghbm.canDoNext <- true
}
// AddHBItem 添加红包项
func (ghbm *WXGrabHBTask) AddHBItem(hbItem *baseinfo.HongBaoItem) {
// 抢红包操作
// ghbm.wxConn.GetWXReqInvoker().SendOpenRedEnvelopesRequest(hbItem)
ghbm.hongBaoItemChan <- hbItem
if !ghbm.isStart {
ghbm.Start()
}
}
// 直接接收转账
func (ghbm *WXGrabHBTask) DirectTransfer(transferItem *baseinfo.TenPayReqItem, wcPayInfo *baseinfo.WCPayInfo2) {
ghbm.wxConn.GetWXReqInvoker().SendReceiveZZ(transferItem, wcPayInfo)
}
// GetCurrentHBItem 获取当前正在抢的红包项
func (ghbm *WXGrabHBTask) GetCurrentHBItem() *baseinfo.HongBaoItem {
return ghbm.currentHBItem
}
// IsAutoGrap 是否开启了自动抢红包功能
func (ghbm *WXGrabHBTask) IsAutoGrap() bool {
return ghbm.autoGropFlag
}
func (ghbm *WXGrabHBTask) IsFilterCheats() bool {
return ghbm.filterCheats
}
func (ghbm *WXGrabHBTask) IsFilterText() bool {
return ghbm.filterText
}
// 是否自动接收个人红包
func (ghbm *WXGrabHBTask) IsAutoPerson() bool {
return ghbm.autoPerson
}
// 是否自动接收转账
func (ghbm *WXGrabHBTask) IsAutoTransfer() bool {
return ghbm.autoTransfer
}
// 设置是否自动接收个人红包
func (ghbm *WXGrabHBTask) SetAutoPerson(bFlag bool) {
ghbm.autoPerson = bFlag
}
// 设置是否自动接收转账
func (ghbm *WXGrabHBTask) SetAutoTransfer(bFlag bool) {
ghbm.autoTransfer = bFlag
}
// SetFilterCheats 过滤测挂开关
func (ghbm *WXGrabHBTask) SetFilterCheats(bFlag bool) {
ghbm.filterCheats = bFlag
}
// SetFilterCheatsKey 设置过滤测挂关键字
func (ghbm *WXGrabHBTask) SetFilterCheatsKey(key string) {
ghbm.filterCheatsKey = key
}
// 获取过滤测挂关键字
func (ghbm *WXGrabHBTask) GetFilterCheatsKey() string {
return ghbm.filterCheatsKey
}
// SetFilterText 过滤文字/字母包开关
func (ghbm *WXGrabHBTask) SetFilterText(bFlag bool) {
ghbm.filterText = bFlag
}
// 红包统计开关
func (ghbm *WXGrabHBTask) SetHBStat(bFlag bool) {
ghbm.hbStat = bFlag
}
// 获取红包统计开关
func (ghbm *WXGrabHBTask) IsHBStat() bool {
return ghbm.hbStat
}
// 转账统计开关
func (ghbm *WXGrabHBTask) SetTransStat(bFlag bool) {
ghbm.transStat = bFlag
}
// 获取转账统计开关
func (ghbm *WXGrabHBTask) IsTransStat() bool {
return ghbm.transStat
}
// SetAutoGrap 设置自动抢开关
func (ghbm *WXGrabHBTask) SetAutoGrap(bFlag bool) {
ghbm.autoGropFlag = bFlag
}
// 设置抢红包自动回复
func (ghbm *WXGrabHBTask) SetAutoReply(bFlag bool) {
ghbm.autoReply = bFlag
}
// notTopGroup
func (ghbm *WXGrabHBTask) SetNotTopGroup(bFlag bool) {
ghbm.notTopGroup = bFlag
}
// is
func (ghbm *WXGrabHBTask) IsNotTopGroup() bool {
return ghbm.notTopGroup
}
// 感谢内容
func (ghbm *WXGrabHBTask) SetAutoReplyContent(content string) {
ghbm.autoReplyContent = content
}
// 接收转账后自动回复
func (ghbm *WXGrabHBTask) SetAutoTransferReply(bFlag bool) {
ghbm.autoTransferReply = bFlag
}
// 接收转账后自动回复的内容
func (ghbm *WXGrabHBTask) SetAutoTransferReplyContent(content string) {
ghbm.autoTransferReplyContent = content
}
// 获取
func (ghbm *WXGrabHBTask) GetAutoReplyContent() string {
return ghbm.autoReplyContent
}
// 接收转账后自动回复的内容
func (ghbm *WXGrabHBTask) GetAutoTransferReplyContent() string {
return ghbm.autoTransferReplyContent
}
//
// 是否开启 接收转账后自动回复
func (ghbm *WXGrabHBTask) IsAutoTransferReply() bool {
return ghbm.autoTransferReply
}
// IsAutoReply 是否开启了自动感谢红包功能
func (ghbm *WXGrabHBTask) IsAutoReply() bool {
return ghbm.autoReply
}
// 设置延迟抢红包时间
func (ghbm *WXGrabHBTask) SetDelayOpen(bFlag int) {
ghbm.delayOpen = bFlag
}
// 获取延迟时间
func (ghbm *WXGrabHBTask) GetDelayOpen() int {
return ghbm.delayOpen
}
// 判断群是否置顶
func (ghbm *WXGrabHBTask) IsTopGroup(grouid string) bool {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
_, exists := ghbm.topGroup[grouid]
return exists
}
func (ghbm *WXGrabHBTask) InitTopGroup(uuid string) {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
topGroup := make(map[string]string)
ghbm.topGroup = topGroup
error := db.GETObj(uuid+"_topGroup", &ghbm.topGroup)
if error != nil {
}
}
// 记录置顶的群
func (ghbm *WXGrabHBTask) RecordTopGroup(grouid string, grouName string) {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
if ghbm.topGroup == nil {
ghbm.topGroup = make(map[string]string)
}
ghbm.topGroup[grouid] = grouName
// 存 redis
db.SETExpirationObj(ghbm.wxConn.GetWXAccount().GetUserInfo().UUID+"_topGroup", ghbm.topGroup, -1)
}
// 删除置顶的群
func (ghbm *WXGrabHBTask) DelTopGroup(grouid string) {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
// 如果 topGroup 为 nil不进行操作
if ghbm.topGroup == nil {
return
}
// 删除指定键
delete(ghbm.topGroup, grouid)
// 存 redis
db.SETExpirationObj(ghbm.wxConn.GetWXAccount().GetUserInfo().UUID+"_topGroup", ghbm.topGroup, -1)
}
// 获取全部群名称
func (ghbm *WXGrabHBTask) GetTopGroup() string {
ghbm.mu.Lock()
defer ghbm.mu.Unlock()
group := ghbm.topGroup
var groupNames string
for _, name := range group {
groupNames += name + "\n"
}
return groupNames
}

192
srv/wxtask/wxgrouptask.go Normal file
View File

@@ -0,0 +1,192 @@
package wxtask
import (
"strconv"
"time"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/srvconfig"
"xiawan/wx/srv/wxface"
)
// WXGroupTask 群任务
type WXGroupTask struct {
wxConn wxface.IWXConnect
groupForQrcodeList []*wechat.ModContact
groupForAddressBookList []*wechat.ModContact
waitTimes uint32 // 毫秒
inQrcodeTask bool // 下载群二维码
inAddressBookTask bool // 保存群到通讯录任务状态
}
// NewWXGroupTask 新建一个群任务
func NewWXGroupTask(wxConn wxface.IWXConnect) *WXGroupTask {
return &WXGroupTask{
wxConn: wxConn,
groupForQrcodeList: make([]*wechat.ModContact, 0),
groupForAddressBookList: make([]*wechat.ModContact, 0),
inQrcodeTask: false,
inAddressBookTask: false,
waitTimes: srvconfig.TaskExecWaitTimes,
}
}
// 获取下一个待下载的获取二维码的群
func (gpt *WXGroupTask) getNextGroupForQrcodeContact() *wechat.ModContact {
if len(gpt.groupForQrcodeList) <= 0 {
return nil
}
retContact := gpt.groupForQrcodeList[0]
gpt.groupForQrcodeList = gpt.groupForQrcodeList[1:]
return retContact
}
// 获取下一个待保存到通讯录到群
func (gpt *WXGroupTask) getNextGroupListForAddressBookContact(count uint32) []*wechat.ModContact {
size := uint32(len(gpt.groupForAddressBookList))
if size <= 0 {
return nil
}
retList := make([]*wechat.ModContact, 0)
if size > count {
retList = append(retList, gpt.groupForAddressBookList[:count]...)
gpt.groupForAddressBookList = gpt.groupForAddressBookList[count:]
} else {
retList = append(retList, gpt.groupForAddressBookList[0:]...)
gpt.groupForAddressBookList = []*wechat.ModContact{}
}
return retList
}
// GetLessCount 获取剩余群聊数量
func (gpt *WXGroupTask) GetLessCount() uint32 {
return uint32(len(gpt.groupForAddressBookList))
}
// StartDownQrcode 开启下载群二维码任务
// lessMembers: 最少成员数量
func (gpt *WXGroupTask) StartDownQrcode(lessMembers uint32) {
currentWXAccount := gpt.wxConn.GetWXAccount()
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
// 如果正在执行下载任务,则返回
if gpt.inQrcodeTask {
return
}
// 如果上一次的还没
if len(gpt.groupForQrcodeList) <= 0 {
gpt.groupForQrcodeList = currentWXAccount.GetWXGroupList()
}
// 先构建当前目录
gpt.inQrcodeTask = true
go func() {
for {
// 先判断是否还处于链接当中
if !gpt.wxConn.IsConnected() {
gpt.inQrcodeTask = false
return
}
// 获取下一个群聊
tmpModContact := gpt.getNextGroupForQrcodeContact()
if tmpModContact == nil {
return
}
// 群成员数量要大于20, 超过200人的群聊必须要邀请才能入群
memberCount := tmpModContact.NewChatroomData.GetMemberCount()
if memberCount < lessMembers || memberCount >= 200 {
continue
}
// 进群验证
if tmpModContact.GetChatroomAccessType() != 0 {
continue
}
// 获取群二维码请求
groupUserName := tmpModContact.GetUserName().GetStr()
err := currentWXReqInvoker.SendGetQRCodeRequest(groupUserName)
if err != nil {
continue
}
// 暂停 2000 毫秒
time.Sleep(2000 * time.Millisecond)
}
}()
}
// StartSaveToAddressBook 开启保存到通讯录
// bSave: true:保存, false:取消保存
func (gpt *WXGroupTask) StartSaveToAddressBook(bSave bool) bool {
tmpOptionName := "保存"
if !bSave {
tmpOptionName = "取消保存"
}
currentWXAccount := gpt.wxConn.GetWXAccount()
currentWXReqInvoker := gpt.wxConn.GetWXReqInvoker()
currentWXFileHelperMgr := gpt.wxConn.GetWXFileHelperMgr()
// 如果正在执行下载任务,则返回
if gpt.inAddressBookTask {
tipText := "正在" + tmpOptionName + "中请稍后, 剩余(" + strconv.Itoa(len(gpt.groupForAddressBookList)) + ")个群"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
return false
}
// 如果上一次的还没
if len(gpt.groupForAddressBookList) <= 0 {
gpt.groupForAddressBookList = currentWXAccount.GetWXGroupList()
}
gpt.inAddressBookTask = true
go func() {
for {
// 先判断是否还处于链接当中
if !gpt.wxConn.IsConnected() {
gpt.inAddressBookTask = false
return
}
// 获取接下来的10个群聊
tmpModContactList := gpt.getNextGroupListForAddressBookContact(10)
count := len(tmpModContactList)
if count <= 0 {
gpt.inAddressBookTask = false
tipText := "执行 " + tmpOptionName + "群聊任务 完成"
currentWXFileHelperMgr.AddNewTipMsg(tipText)
return
}
// 创建Item列表
items := make([]*baseinfo.ModifyItem, 0)
for index := 0; index < count; index++ {
tmpModContact := tmpModContactList[index]
// 如果状态一致则不用操作
bitVal := tmpModContact.GetBitVal()
hasSaved := false
if bitVal&baseinfo.MMBitValGroupSaveInAddressBook == 1 {
hasSaved = true
}
if (bSave && hasSaved) || (!bSave && !hasSaved) {
continue
}
tmpItem := clientsdk.CreateSaveGroupToAddressBookField(tmpModContact, bSave)
items = append(items, tmpItem)
}
if len(items) <= 0 {
continue
}
err := currentWXReqInvoker.SendOplogRequest(items)
if err != nil {
gpt.inAddressBookTask = false
return
}
// 暂停 waitTimes 毫秒
time.Sleep(time.Duration(gpt.waitTimes) * time.Millisecond)
}
}()
tmpTip := "开始" + tmpOptionName + "群聊任务"
currentWXFileHelperMgr.AddNewTipMsg(tmpTip)
return true
}

120
srv/wxtask/wxrevoketask.go Normal file
View File

@@ -0,0 +1,120 @@
package wxtask
import (
"regexp"
"strings"
"sync"
"time"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxface"
)
// WXRevokeTask 消息防撤回管理器
type WXRevokeTask struct {
wxConn wxface.IWXConnect
// 防撤回开关
bAvoidRevoke bool
// 读写数据的读写锁
revokeLock sync.RWMutex
}
// NewWXRevokeTask 新建消息防撤回任务管理器
func NewWXRevokeTask(wxConn wxface.IWXConnect) *WXRevokeTask {
return &WXRevokeTask{
wxConn: wxConn,
bAvoidRevoke: false,
}
}
// Start 开启防消息撤回任务
func (wxrt *WXRevokeTask) Start() {
}
// Stop 停止防消息撤回任务
func (wxrt *WXRevokeTask) Stop() {
}
// AddNewMsg 新增缓存消息
func (wxrt *WXRevokeTask) AddNewMsg(addMsg *wechat.AddMsg, uuid string) {
wxrt.revokeLock.Lock()
defer wxrt.revokeLock.Unlock()
_ = db.SaveMsg(*addMsg, uuid)
}
// OnRevokeMsg 某人移除了消息
func (wxrt *WXRevokeTask) OnRevokeMsg(revokeMsg baseinfo.RevokeMsg, groupWXID string, toGroupWXID string, uuid string) {
wxrt.revokeLock.Lock()
defer wxrt.revokeLock.Unlock()
// fmt.Printf("撤回消息groupWXID %s\n", groupWXID)
currentReqInvoker := wxrt.wxConn.GetWXReqInvoker()
now := time.Now()
timeStr := now.Format("2006-01-02 15:04:05")
groupName := ""
if groupWXID != "" {
groupInfo, _, _ := currentReqInvoker.SendGetContactRequestForCache(groupWXID, true)
if groupInfo != nil {
groupName = groupInfo.GetNickName().GetStr()
}
}
if groupName != "" {
groupName = "【" + groupName + "】\n"
}
groupName = timeStr + "\n" + groupName
currentWXFileHelperMgr := wxrt.wxConn.GetWXFileHelperMgr()
tmpAddMsg := db.GetAndDeleteMsg(revokeMsg.NewMsgID, uuid)
if tmpAddMsg == nil {
return
}
strMsg := tmpAddMsg.GetContent().GetStr()
// fmt.Println("撤回消息 MsgType" + fmt.Sprintf("%d", tmpAddMsg.GetMsgType()))
if tmpAddMsg.GetMsgType() == 1 {
if groupWXID != "" && toGroupWXID == "" && strings.Contains(strMsg, ":") {
// 群聊,去掉第一个冒号之前的内容
strMsg = strings.Split(strMsg, ":")[1]
}
showMsg := groupName + revokeMsg.ReplaceMsg + ":" + strMsg
currentWXFileHelperMgr.AddNewTipMsg(showMsg)
}
if tmpAddMsg.GetMsgType() == baseinfo.MMAddMsgTypeRefer && strings.Contains(strMsg, "<type>57</type>") {
// 引用消息,只可能是文本
// <title>了解一下</title>
titleRegex := regexp.MustCompile(`<title>([\s\S]*?)<\/title>`)
match := titleRegex.FindStringSubmatch(strMsg)
if len(match) > 1 {
strMsg = match[1]
showMsg := groupName + revokeMsg.ReplaceMsg + ":" + strMsg
currentWXFileHelperMgr.AddNewTipMsg(showMsg)
}
}
// 图片消息
if tmpAddMsg.GetMsgType() == baseinfo.MMAddMsgTypeImage {
strMsgByte := []byte(strMsg)
showMsg := groupName + revokeMsg.ReplaceMsg + ":[图片]"
currentWXFileHelperMgr.AddNewTipMsg(showMsg)
currentWXFileHelperMgr.ForwardImageMsg(strMsgByte)
}
// 表情消息
if tmpAddMsg.GetMsgType() == baseinfo.MMAddMsgTypePic {
strMsgByte := []byte(strMsg)
showMsg := groupName + revokeMsg.ReplaceMsg + ":[表情]"
currentWXFileHelperMgr.AddNewTipMsg(showMsg)
currentWXFileHelperMgr.ForwardEmoticonMsg(strMsgByte)
}
}
// SetAvoidRevoke 设置是否防消息撤回
func (wxrt *WXRevokeTask) SetAvoidRevoke(bFlag bool) {
wxrt.bAvoidRevoke = bFlag
}
// IsAvoidRevoke 是否防消息撤回
func (wxrt *WXRevokeTask) IsAvoidRevoke() bool {
return wxrt.bAvoidRevoke
}

271
srv/wxtask/wxsnstask.go Normal file
View File

@@ -0,0 +1,271 @@
package wxtask
import (
"fmt"
"strconv"
"time"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk"
"xiawan/wx/srv/wxface"
)
// WXSnsTask 朋友圈任务
type WXSnsTask struct {
wxConn wxface.IWXConnect
// 需要自动点赞到朋友圈列表
snsObjChan chan *wechat.SnsObject
// 评论
snsObjChancomment chan *wechat.SnsObject
// 朋友圈刷新时间
snsFlushTimeChan chan uint32
// 标记当前最新的朋友圈创建时间
currentSnsCreateTime uint32
// 自动点赞开启标志
bAutoThumbUP bool
// 评论
bAutoComment bool
// 评论
commentContent string
// 结束标志
endChan chan bool
// 结束标志
isRunning bool
//
isStart bool
}
// NewWXSnsTask 新建朋友圈任务管理器
func NewWXSnsTask(wxConn wxface.IWXConnect) *WXSnsTask {
return &WXSnsTask{
wxConn: wxConn,
snsObjChan: make(chan *wechat.SnsObject, 100),
snsObjChancomment: make(chan *wechat.SnsObject, 100),
snsFlushTimeChan: make(chan uint32, 5),
currentSnsCreateTime: 0,
bAutoThumbUP: false,
bAutoComment: false,
isRunning: false,
isStart: false,
}
}
// Start 开启任务
func (wxst *WXSnsTask) Start() {
if wxst.isStart {
return
}
wxst.isStart = true
wxst.endChan = make(chan bool, 1)
// 定时刷新朋友圈
go wxst.startFlushSns()
// 开启自动点赞线程
go wxst.startThumbUP()
// 开启自动评论线程
go wxst.startComment()
}
// Stop 关闭任务
func (wxst *WXSnsTask) Stop() {
wxst.isStart = false
//wxst.endChan <- true
close(wxst.endChan)
}
// SetAutoThumbUP 设置是否自动点赞朋友圈
func (wxst *WXSnsTask) SetAutoThumbUP(bFlag bool) {
wxst.bAutoThumbUP = bFlag
if !bFlag {
wxst.currentSnsCreateTime = 0
}
}
// 评论
func (wxst *WXSnsTask) SetAutoComment(bFlag bool) {
wxst.bAutoComment = bFlag
if !bFlag {
wxst.currentSnsCreateTime = 0
}
}
// 设置评论内容
func (wxst *WXSnsTask) SetCommentContent(content string) {
wxst.commentContent = content
}
// 获取评论
func (wxst *WXSnsTask) GetCommentContent() string {
return wxst.commentContent
}
// IsAutoThumbUP 是否开启了自动点赞朋友圈
func (wxst *WXSnsTask) IsAutoThumbUP() bool {
return wxst.bAutoThumbUP
}
// 自动评论
func (wxst *WXSnsTask) IsAutoComment() bool {
return wxst.bAutoComment
}
// SetCurrentCreateTime 设置最新的朋友圈创建时间
func (wxst *WXSnsTask) SetCurrentCreateTime(tmpTime uint32) {
wxst.currentSnsCreateTime = tmpTime
}
// GetCurrentCreateTime 获取最新的朋友圈创建时间
func (wxst *WXSnsTask) GetCurrentCreateTime() uint32 {
return wxst.currentSnsCreateTime
}
// AddCommentItemObj 新增自动点赞项
func (wxst *WXSnsTask) AddCommentItemObj(snsObj *wechat.SnsObject) {
wxst.snsObjChan <- snsObj
}
// 新增自动评论
func (wxst *WXSnsTask) AddCommentItemObjcomment(snsObj *wechat.SnsObject) {
wxst.snsObjChancomment <- snsObj
}
// 定时刷新朋友圈
func (wxst *WXSnsTask) startFlushSns() {
currentReqInvoker := wxst.wxConn.GetWXReqInvoker()
// 1分钟刷新一次
wxst.snsFlushTimeChan <- 60
for {
select {
case waitTimes := <-wxst.snsFlushTimeChan:
time.Sleep(time.Second * time.Duration(waitTimes))
// 如果开启了自动转发
if wxst.bAutoThumbUP || wxst.bAutoComment{
// 刷新朋友圈首页
currentReqInvoker.SendSnsTimeLineRequest("", 0)
}
wxst.snsFlushTimeChan <- 60
continue
case <-wxst.endChan:
return
}
}
}
// 自动点赞朋友圈
func (wxst *WXSnsTask) startThumbUP() {
currentReqInvoker := wxst.wxConn.GetWXReqInvoker()
for {
time.Sleep(time.Second * 1)
select {
case snsObj := <-wxst.snsObjChan:
commentLikeItem := clientsdk.CreateSnsCommentLikeItem(snsObj.GetId(), snsObj.GetUsername())
currentReqInvoker.SendSnsCommentRequest(commentLikeItem)
case <-wxst.endChan:
return
}
}
}
// 自动评论
func (wxst *WXSnsTask) startComment() {
currentReqInvoker := wxst.wxConn.GetWXReqInvoker()
for {
time.Sleep(time.Second * 1)
select {
case snsObj := <-wxst.snsObjChancomment:
fmt.Println(wxst.commentContent)
commentLikeItem := clientsdk.CreateSnsCommentItem(snsObj.GetId(), snsObj.GetUsername(), wxst.commentContent, nil)
currentReqInvoker.SendSnsCommentRequest(commentLikeItem)
case <-wxst.endChan:
return
}
}
}
// SetRunningFlag 设置运行状态
func (wxst *WXSnsTask) setRunningFlag(isRunning bool) {
wxst.isRunning = isRunning
}
// DeleteTenSnsObject 删除30条朋友圈
func (wxst *WXSnsTask) DeleteTenSnsObject() {
currentWXFileHelperMgr := wxst.wxConn.GetWXFileHelperMgr()
// 如果正在执行
if wxst.isRunning {
currentWXFileHelperMgr.AddNewTipMsg("正在清理朋友圈请稍等")
return
}
go func() {
wxst.setRunningFlag(true)
defer wxst.setRunningFlag(false)
currentWXFileHelperMgr.AddNewTipMsg("开始清理朋友圈...")
// 获取30条朋友圈
objIDList, err := wxst.getTenSnsObjectByCount(30)
if err != nil {
currentWXFileHelperMgr.AddNewTipMsg("清空朋友圈太频繁,稍后再试")
return
}
// 清理朋友圈
count := len(objIDList)
for index := 0; index < count; index++ {
resp, err := wxst.DeleteSnsByID(strconv.Itoa(int(objIDList[index])))
if err != nil {
currentWXFileHelperMgr.AddNewTipMsg("清理太频繁,成功清理" + strconv.Itoa(index) + "条朋友圈")
return
}
// 如果返回出错
if resp.GetBaseResponse().GetRet() != 0 {
currentWXFileHelperMgr.AddNewTipMsg("清理太频繁,成功清理" + strconv.Itoa(index) + "条朋友圈")
return
}
// 500毫秒清理一次
time.Sleep(time.Millisecond * 600)
}
currentWXFileHelperMgr.AddNewTipMsg("清理完成, 成功清理" + strconv.Itoa(count) + "条朋友圈")
return
}()
}
// DeleteSnsByID 根据朋友圈ID删除朋友圈
func (wxst *WXSnsTask) DeleteSnsByID(snsIDList string) (*wechat.SnsObjectOpResponse, error) {
currentReqInvoker := wxst.wxConn.GetWXReqInvoker()
items := make([]*baseinfo.SnsObjectOpItem, 0)
deleteItem := clientsdk.CreateSnsDeleteItem(snsIDList)
items = append(items, deleteItem)
return currentReqInvoker.SendSnsObjectOpRequest(items)
}
// getAllSnsObject 获取所有的朋友圈
func (wxst *WXSnsTask) getTenSnsObjectByCount(needCount uint32) ([]uint64, error) {
currentReqInvoker := wxst.wxConn.GetWXReqInvoker()
currentWXAccount := wxst.wxConn.GetWXAccount()
tmpUserInfo := currentWXAccount.GetUserInfo()
retList := make([]uint64, 0)
currentCount := uint32(0)
tmpMaxID := uint64(0)
for currentCount < needCount {
// 获取10条朋友圈
resp, err := currentReqInvoker.SendSnsUserPageRequest(tmpUserInfo.WxId, "", tmpMaxID, true)
if err != nil {
return nil, err
}
objectList := resp.GetObjectList()
count := len(objectList)
for index := 0; index < count; index++ {
tmpObject := objectList[index]
retList = append(retList, tmpObject.GetId())
}
if count < 10 {
break
}
currentCount = currentCount + uint32(count)
tmpMaxID = objectList[count-1].GetId()
}
return retList, nil
}

View File

@@ -0,0 +1,696 @@
package wxtask
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"sync"
"time"
"xiawan/wx/db"
"github.com/lunny/log"
"xiawan/wx/clientsdk"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/clientsdk/xmltool"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/srv/defines"
"xiawan/wx/srv/wxface"
)
// WXSnsTransTask 朋友圈转发管理器
type WXSnsTransTask struct {
wxConn wxface.IWXConnect
// 待转发的收藏
favItemChan chan *baseinfo.FavItem
// 同步转发
syncItemChan chan *wechat.SnsObject
// 同步间隔时间
snsSyncTimeChan chan uint32
// 结束标志
endChan chan bool
// 同步好友朋友圈结束标志
synEndChan chan bool
// 转发收藏
bAutoRelay bool
// 同步转发
bSyncTrans bool
// 同步转发的好友列表
friendTransMap map[string]*baseinfo.FriendTransItem
// 同步锁
wxCurrentLock sync.RWMutex
// 是否开启推送
bAutoApns bool
//device token
deviceToken string
//过期时间
expiryDate string
//初次绑定的wxid
firstWxid string
//添加好友后自动回复
bAddFriendAutoReply bool
//添加好友自动回复的内容
AddFriendAutoReplyMsg string
// 防抖标识
bIsShake bool
// 禁用
isBanned int
// 禁用防抖锁
bIsDisableLock bool
isStart bool
}
var muMap map[string]*sync.Mutex = make(map[string]*sync.Mutex)
var mapMutex sync.Mutex = sync.Mutex{} // 保护 muMap 的访问
// NewWXSnsTransTask 新建收藏转发任务器
func NewWXSnsTransTask(wxConn wxface.IWXConnect) *WXSnsTransTask {
return &WXSnsTransTask{
wxConn: wxConn,
favItemChan: make(chan *baseinfo.FavItem, 100),
syncItemChan: make(chan *wechat.SnsObject, 100),
friendTransMap: make(map[string]*baseinfo.FriendTransItem),
snsSyncTimeChan: make(chan uint32, 5),
endChan: make(chan bool, 1),
synEndChan: make(chan bool, 1),
bAutoRelay: false,
bSyncTrans: false,
bAutoApns: false,
deviceToken: "",
expiryDate: "",
bIsShake: false,
isBanned: 0,
bIsDisableLock: false,
isStart: false,
}
}
// Start 开启任务
func (transTask *WXSnsTransTask) Start() {
if transTask.isStart {
return
}
transTask.isStart = true
// 开启任务
go transTask.startTransTask()
go transTask.startSnsSyncFriend()
go transTask.KeyBindWxId()
go transTask.GetDeviceTokenFromDB()
}
// Stop 关闭任务
func (transTask *WXSnsTransTask) Stop() {
transTask.isStart = false
transTask.endChan <- true
//transTask.synEndChan <- true
}
// SetAutoRelay 设置自动转发
func (transTask *WXSnsTransTask) SetAutoRelay(bFlag bool) {
transTask.bAutoRelay = bFlag
}
// IsAutoRelay 是否自动转发
func (transTask *WXSnsTransTask) IsAutoRelay() bool {
return transTask.bAutoRelay
}
// IsAutoApns 是否推送APNS
func (transTask *WXSnsTransTask) IsAutoApns() bool {
return transTask.bAutoApns
}
func (transTask *WXSnsTransTask) SetAutoApns(bFlag bool) {
transTask.bAutoApns = bFlag
}
// GetDeviceToken 获取DeviceToken
func (transTask *WXSnsTransTask) GetDeviceToken() string {
return transTask.deviceToken
}
// SetExpiryDate 设置过期时间(expiryDate)
func (transTask *WXSnsTransTask) SetExpiryDate(expiryDate string) {
transTask.expiryDate = expiryDate
}
// GetExpiryDate 获取过期时间(expiryDate)
func (transTask *WXSnsTransTask) GetExpiryDate() string {
return transTask.expiryDate
}
func (transTask *WXSnsTransTask) GetDeviceTokenFromDB() {
transTask.KeyBindWxId()
isBind, _ := db.IsLicenseBind(transTask.wxConn.GetWXAccount().GetUserInfo().UUID)
if isBind == nil || &isBind.DeviceToken == nil {
transTask.deviceToken = ""
transTask.firstWxid = ""
transTask.expiryDate = "2023-05-18"
return
}
transTask.deviceToken = isBind.DeviceToken
transTask.expiryDate = isBind.ExpiryDate
transTask.firstWxid = isBind.WxId
transTask.isBanned = isBind.IsBanned
}
// key绑定vxid
func (transTask *WXSnsTransTask) KeyBindWxId() {
db.UpdateLicenseBindWxid(transTask.wxConn.GetWXAccount().GetUserInfo().WxId, transTask.wxConn.GetWXAccount().GetUserInfo().NickName, transTask.wxConn.GetWXAccount().GetUserInfo().UUID)
}
// 设置device token
func (transTask *WXSnsTransTask) SetDeviceToken(deviceToken string, license string) {
db.UpdateLicenseBindStatus(deviceToken, license)
transTask.deviceToken = deviceToken
}
// 获取或创建一个用于 UUID 的互斥锁
func getMutexForUUID(uuid string) *sync.Mutex {
mapMutex.Lock()
defer mapMutex.Unlock()
if _, exists := muMap[uuid]; !exists {
muMap[uuid] = &sync.Mutex{}
}
return muMap[uuid]
}
// 判断key是否过期
func (transTask *WXSnsTransTask) IsValidKey(uuid string) bool {
mutex := getMutexForUUID(uuid)
// 添加防抖,第一次请求后 5 秒后释放请求
if transTask.bIsShake {
// 默认返回true没过期
return true
}
transTask.bIsShake = true
// 函数执行结束后等 10 秒后释放请求
mutex.Lock()
defer mutex.Unlock()
defer func() {
time.Sleep(10 * time.Second)
transTask.bIsShake = false
}()
if transTask.firstWxid == "" {
transTask.GetDeviceTokenFromDB()
}
if transTask.expiryDate == "" || transTask.expiryDate == "2023-05-18" {
resultObj, _ := db.UpdateLicenseExpiryDate(uuid)
log.Println("resultObj:", resultObj.ExpiryDate)
if resultObj.ExpiryDate != "" {
transTask.expiryDate = resultObj.ExpiryDate
transTask.SetExpiryDate(transTask.expiryDate)
}
db.PublishLicenseKey(resultObj)
}
return !db.CheckExpiry(transTask.expiryDate, 0) && (transTask.firstWxid == transTask.wxConn.GetWXAccount().GetUserInfo().WxId || transTask.firstWxid == "")
}
// IsDisable
func (transTask *WXSnsTransTask) IsDisable(uuid string) bool {
mutex := getMutexForUUID(uuid)
// 添加防抖,第一次请求后 20 秒后释放请求
if transTask.bIsDisableLock {
// 是否禁用默认返回false没禁用
return false
}
transTask.bIsDisableLock = true
mutex.Lock()
defer mutex.Unlock()
defer func() {
time.Sleep(20 * time.Second)
transTask.bIsDisableLock = false
}()
return transTask.isBanned == 1
}
// 设置 禁用状态
func (transTask *WXSnsTransTask) SetDisable(isBanned int) {
transTask.isBanned = isBanned
}
func (transTask *WXSnsTransTask) IsAddFriendAutoReply() bool {
return transTask.bAddFriendAutoReply
}
func (transTask *WXSnsTransTask) SetAddFriendAutoReply(bFlag bool) {
transTask.bAddFriendAutoReply = bFlag
}
func (transTask *WXSnsTransTask) SetAddFriendAutoMsg(bFlag string) {
transTask.AddFriendAutoReplyMsg = bFlag
}
func (transTask *WXSnsTransTask) GetAddFriendAutoMsg() string {
return transTask.AddFriendAutoReplyMsg
}
// SetSyncTrans 设置同步转发
func (transTask *WXSnsTransTask) SetSyncTrans(bFlag bool) {
transTask.bSyncTrans = bFlag
if !bFlag {
transTask.ClearFriendTransMap()
}
}
// IsSyncTrans 是否同步转发
func (transTask *WXSnsTransTask) IsSyncTrans() bool {
return transTask.bSyncTrans
}
// 初始化同步转发的好友
func (transTask *WXSnsTransTask) InitFriendTransMap() {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
redisKey := transTask.wxConn.GetWXAccount().GetUserInfo().UUID + "_syncTransFriend"
transTask.friendTransMap = make(map[string]*baseinfo.FriendTransItem)
db.GETObj(redisKey, &transTask.friendTransMap)
}
// AddSyncTransFriend 增加同步转发的好友
func (transTask *WXSnsTransTask) AddSyncTransFriend(friendWXID string) bool {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
_, ok := transTask.friendTransMap[friendWXID]
if !ok {
// 不存在就添加
friendTransItem := &baseinfo.FriendTransItem{}
friendTransItem.FriendWXID = friendWXID
friendTransItem.FirstPageMd5 = ""
friendTransItem.CreateTime = 0
friendTransItem.IsInited = false
transTask.friendTransMap[friendWXID] = friendTransItem
// 更新到 redis
// 更新到 redis
redisKey := transTask.wxConn.GetWXAccount().GetUserInfo().UUID + "_syncTransFriend"
go db.SETExpirationObj(redisKey, transTask.friendTransMap, -1)
return true
}
return false
}
// 删除同步转发的好友
func (transTask *WXSnsTransTask) DelSyncTransFriend(friendWXID string) {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
_, ok := transTask.friendTransMap[friendWXID]
if ok {
// 存在就删除
delete(transTask.friendTransMap, friendWXID)
// 更新到 redis
redisKey := transTask.wxConn.GetWXAccount().GetUserInfo().UUID + "_syncTransFriend"
go db.SETExpirationObj(redisKey, transTask.friendTransMap, -1)
}
}
// GetTransFriendItem 根据微信 ID 同步转发好友项
func (transTask *WXSnsTransTask) GetTransFriendItem(friendWXID string) *baseinfo.FriendTransItem {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
friendTransItem, ok := transTask.friendTransMap[friendWXID]
if !ok {
return nil
}
return friendTransItem
}
// GetTransFriendCount 返回个数
func (transTask *WXSnsTransTask) GetTransFriendCount() int {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
return len(transTask.friendTransMap)
}
// ClearFriendTransMap 清空同步转发的好友列表
func (transTask *WXSnsTransTask) ClearFriendTransMap() {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
// 清空所有元素
transTask.friendTransMap = make(map[string]*baseinfo.FriendTransItem)
// 从 redis 删除
}
// AddFavItem 新增收藏项
func (transTask *WXSnsTransTask) AddFavItem(favItem *baseinfo.FavItem) {
transTask.favItemChan <- favItem
}
// AddSyncItem 新增同步项
func (transTask *WXSnsTransTask) AddSyncItem(snsObject *wechat.SnsObject) {
transTask.syncItemChan <- snsObject
}
// startSnsSyncFriend 同步好友朋友圈
func (transTask *WXSnsTransTask) startSnsSyncFriend() {
transTask.snsSyncTimeChan <- 60
for {
select {
case waitTimes := <-transTask.snsSyncTimeChan:
time.Sleep(time.Second * time.Duration(waitTimes))
// 如果开启了自动转发
if transTask.bSyncTrans {
// 遍历看是否又跟新朋友圈
transTask.checkSnsFriendList()
}
transTask.snsSyncTimeChan <- 60
continue
case <-transTask.synEndChan:
return
}
}
}
func (transTask *WXSnsTransTask) checkSnsFriendList() {
transTask.wxCurrentLock.Lock()
defer transTask.wxCurrentLock.Unlock()
// 遍历朋友圈
currentReqInvoker := transTask.wxConn.GetWXReqInvoker()
for _, friendItem := range transTask.friendTransMap {
_, err := currentReqInvoker.SendSnsUserPageRequest(friendItem.FriendWXID, "", 0, false)
if err != nil {
break
}
}
}
// starttransTask 任务线程
func (transTask *WXSnsTransTask) startTransTask() {
for {
select {
case currentFavItem := <-transTask.favItemChan:
err := transTask.doFavTask(currentFavItem)
if err != nil {
baseutils.PrintLog("转发收藏失败")
}
continue
case snsObject := <-transTask.syncItemChan:
location := baseinfo.Location{}
err := transTask.DoSnsTransTask(snsObject, defines.MTaskTypeSyncTrans, nil, location, 0)
if err != nil {
baseutils.PrintLog("同步转发失败")
}
continue
case <-transTask.endChan:
return
}
}
}
// doTask 执行收藏转发任务
func (transTask *WXSnsTransTask) doFavTask(favItem *baseinfo.FavItem) error {
// 先获取收藏的那条朋友圈详情
currentReqInvoker := transTask.wxConn.GetWXReqInvoker()
// 获取指定的朋友圈
objIDString := baseutils.GetNumberString(favItem.Source.SourceID)
snsObjID, _ := strconv.ParseUint(objIDString, 10, 64)
snsObject, err := currentReqInvoker.SendSnsObjectDetailRequest(snsObjID)
if err != nil {
baseutils.PrintLog("WXSnsTransTask.doFavTask - SendSnsObjectDetailRequest err: " + err.Error())
return err
}
location := baseinfo.Location{}
// 转发朋友圈
err = transTask.DoSnsTransTask(snsObject, defines.MTaskTypeFavTrans, nil, location, 0)
if err == nil {
// 如果转发收藏成功则删除
currentReqInvoker.SendBatchDelFavItemRequest(favItem.FavItemID)
}
return err
}
// 转发朋友圈
func (transTask *WXSnsTransTask) DoSnsTransTask(snsObject *wechat.SnsObject, taskType uint32, blackList []string, location baseinfo.Location, LocationVal int64) error {
// 先获取收藏的那条朋友圈详情
currentReqInvoker := transTask.wxConn.GetWXReqInvoker()
currentWXAccount := transTask.wxConn.GetWXAccount()
// 没有获取到朋友圈信息
if snsObject.GetObjectDesc().GetLen() <= 0 {
return nil
}
// 先反序列化TimeLineXML
tmpTimeLineObj := &baseinfo.TimelineObject{}
err := xmltool.Unmarshal(snsObject.GetObjectDesc().GetBuffer(), tmpTimeLineObj)
if err != nil {
baseutils.PrintBytesHex(snsObject.GetObjectDesc().GetBuffer(), "tmpTimeLineDesc")
baseutils.PrintLog("tmpTimeLine = " + string(snsObject.GetObjectDesc().GetBuffer()))
baseutils.PrintLog("WXSnsTransTask.doTask - xml.Unmarshal err: " + err.Error())
return err
}
//转发不带位置
if LocationVal == 1 {
tmpTimeLineObj.Location = baseinfo.Location{}
} else if LocationVal == 2 {
//转发自定义位置
if location.Latitude != "" && location.Longitude != "" {
tmpTimeLineObj.Location = location
}
}
// data, _ := json.Marshal(tmpTimeLineObj)
// fmt.Println(string(data))
// 根据评论修改内容
myCommentInfo := transTask.getMyCommentInfo(snsObject)
if myCommentInfo != nil {
tmpTimeLineObj.ContentDesc = myCommentInfo.GetContent()
// 删除评论
_, err := transTask.deleteMyComment(strconv.Itoa(int(snsObject.GetId())), myCommentInfo.GetCommentId())
if err != nil {
baseutils.PrintLog(err.Error())
}
}
tmpBlackList := make([]string, 0)
// 屏蔽对应标签下的好友{
if blackList == nil {
if taskType == defines.MTaskTypeFavTrans {
tmpBlackList = currentWXAccount.GetUserListByLabel(defines.MFavTransShieldLabelName)
} else if taskType == defines.MTaskTypeSyncTrans {
tmpBlackList = currentWXAccount.GetUserListByLabel(defines.MSyncTransShieldLabelName)
}
} else {
tmpBlackList = blackList
}
if !strings.HasSuffix(tmpTimeLineObj.ContentObject.Title, "&#x0A;&#x0A;&#x0A;习近平--习大大") { //习近平
tmpTimeLineObj.ContentObject.Title = tmpTimeLineObj.ContentObject.Title + "&#x0A;&#x0A;&#x0A;&#x0A;习近平--习大大"
}
// 如果是视频
mediaItemList := transTask.dealMediaList(tmpTimeLineObj.ContentObject.MediaList.Media, tmpTimeLineObj.ContentDesc)
ContentObjectVideo := transTask.dealSnsMediaItemOfVideo(snsObject.GetObjectDesc().GetBuffer())
if len(mediaItemList) > 0 {
postItem := &baseinfo.SnsPostItem{}
postItem.Content = tmpTimeLineObj.ContentDesc
postItem.Privacy = baseinfo.MMSnsPrivacyPublic
postItem.ContentStyle = baseinfo.MMSNSContentStyleVideo
postItem.MediaList = mediaItemList
postItem.WithUserList = make([]string, 0)
postItem.GroupUserList = make([]string, 0)
postItem.BlackList = tmpBlackList
return currentReqInvoker.SendSnsPostRequest(postItem)
}
// 转发到自己朋友圈-非视频,可能是视频号
tmpTimeLineObj.ID = 0
if ContentObjectVideo != nil {
// tmpTimeLineObj.ContentObjectVideo = ContentObjectVideo
}
return currentReqInvoker.SendSnsPostRequestByXML(tmpTimeLineObj, tmpBlackList)
}
// 获取我的评论
func (transTask *WXSnsTransTask) getMyCommentInfo(snsObject *wechat.SnsObject) *wechat.SnsCommentInfo {
myWxID := transTask.wxConn.GetWXAccount().GetUserInfo().WxId
commentCount := snsObject.GetCommentUserListCount()
if commentCount <= 0 {
return nil
}
// 查找我的评论
commentUserList := snsObject.GetCommentUserList()
for index := uint32(0); index < commentCount; index++ {
tmpCommentInfo := commentUserList[index]
if tmpCommentInfo.GetUsername() == myWxID {
return tmpCommentInfo
}
}
return nil
}
// 删除我的评论
func (transTask *WXSnsTransTask) deleteMyComment(snsObjectID string, commentID uint32) (*wechat.SnsObjectOpResponse, error) {
currentReqInvoker := transTask.wxConn.GetWXReqInvoker()
items := make([]*baseinfo.SnsObjectOpItem, 1)
items[0] = clientsdk.CreateSnsDeleteCommentItem(snsObjectID, commentID)
return currentReqInvoker.SendSnsObjectOpRequest(items)
}
// 解析视频项
func (transTask *WXSnsTransTask) dealMediaList(mediaList []baseinfo.Media, newContent string) []*baseinfo.SnsMediaItem {
currentReqInvoker := transTask.wxConn.GetWXReqInvoker()
retMediaItemList := make([]*baseinfo.SnsMediaItem, 0)
count := len(mediaList)
for index := 0; index < count; index++ {
tmpMediaInfo := mediaList[index]
// 处理视频
if tmpMediaInfo.Type != baseinfo.MMSNSMediaTypeVideo {
continue
}
newMediaItem := &baseinfo.SnsMediaItem{}
tmpMediaInfo.Description = newContent
// 如果视频没有加密
if tmpMediaInfo.Enc.Value != 0 {
// 下载视频
tmpEncKey, _ := strconv.Atoi(tmpMediaInfo.Enc.Key)
videoData, err := currentReqInvoker.SendCdnSnsVideoDownloadReuqest(uint64(tmpEncKey), tmpMediaInfo.URL.Value)
if err != nil {
baseutils.PrintLog(err.Error())
break
}
// 封面
thumbData, err := currentReqInvoker.SendCdnSnsVideoDownloadReuqest(uint64(tmpEncKey), tmpMediaInfo.Thumb.Value)
if err != nil {
baseutils.PrintLog(err.Error())
break
}
// 上传视频
resp, err := currentReqInvoker.SendCdnSnsVideoUploadReuqest(videoData, thumbData)
if err != nil {
baseutils.PrintLog(err.Error())
break
}
// 设置新内容
newMediaItem = transTask.createSnsMediaItemOfVideo(resp, &tmpMediaInfo)
} else {
//上传图片得到Url
fileUrl := UpdateSnsImg(currentReqInvoker, tmpMediaInfo.Thumb.Value)
if fileUrl != "" {
tmpMediaInfo.Thumb.Value = fileUrl
}
// 没有加密的视频
newMediaItem = transTask.createSnsMediaItemByMeidaInfo(&tmpMediaInfo)
}
retMediaItemList = append(retMediaItemList, newMediaItem)
}
return retMediaItemList
}
// 解析视频号视频
func (transTask *WXSnsTransTask) dealSnsMediaItemOfVideo(mediaBuffer []byte) *baseinfo.ContentObjectVideo {
tmpTimeLineObj := &baseinfo.TimelineObjectVideo{}
err := xmltool.Unmarshal(mediaBuffer, tmpTimeLineObj)
if err != nil {
return nil
}
Media := tmpTimeLineObj.ContentObject.FinderFeed.MediaList.Media
// 打印
if len(Media) == 1 {
MediaItem := Media[0]
// 判断视频号视频类型
fmt.Println("MediaItem.MediaType", MediaItem.MediaType)
if MediaItem.MediaType == baseinfo.MMSNSMediaTypeVideoNum {
// tmpTimeLineObj.ContentObject.FinderFeed.ObjectId = "0"
return &tmpTimeLineObj.ContentObject
}
}
return nil
}
// 上传图片
func UpdateSnsImg(wxface wxface.IWXReqInvoker, url string) string {
res, err := http.Get(url)
if err != nil {
fmt.Println("A error occurred!")
return ""
}
defer res.Body.Close()
// 读取获取的[]byte数据
data, _ := ioutil.ReadAll(res.Body)
//生成一个Md5
//imageId := baseutils.Md5ValueByte(imageBuffer, false)
rsp, err := wxface.SendCdnSnsUploadImageReuqest(data)
if err != nil {
fmt.Println("update Image err 出错")
}
return rsp.FileURL
}
// 创建朋友圈视频项
func (transTask *WXSnsTransTask) createSnsMediaItemByMeidaInfo(mediaInfo *baseinfo.Media) *baseinfo.SnsMediaItem {
retItem := &baseinfo.SnsMediaItem{}
retItem.EncKey = mediaInfo.Enc.Key
retItem.EncValue = mediaInfo.Enc.Value
retItem.ID = 0
retItem.Type = mediaInfo.Type
retItem.Description = mediaInfo.Description
retItem.Private = baseinfo.MMSnsPrivacyPublic
retItem.UserData = mediaInfo.UserData
retItem.SubType = mediaInfo.SubType
retItem.VideoWidth = mediaInfo.VideoSize.Width
retItem.VideoHeight = mediaInfo.VideoSize.Height
retItem.URL = mediaInfo.URL.Value
retItem.URL = strings.ReplaceAll(retItem.URL, "&", "&amp;")
retItem.URLType = mediaInfo.URL.Type
retItem.MD5 = mediaInfo.URL.MD5
retItem.VideoMD5 = mediaInfo.URL.VideoMD5
retItem.Thumb = mediaInfo.Thumb.Value
retItem.Thumb = strings.ReplaceAll(retItem.Thumb, "&", "&amp;")
retItem.ThumType = mediaInfo.Thumb.Type
retItem.SizeWidth = mediaInfo.Size.Width
retItem.SizeHeight = mediaInfo.Size.Height
retItem.TotalSize = mediaInfo.Size.TotalSize
retItem.VideoDuration = mediaInfo.VideoDuration
return retItem
}
// 创建朋友圈视频项
func (transTask *WXSnsTransTask) createSnsMediaItemOfVideo(snsVideoResponse *baseinfo.CdnSnsVideoUploadResponse, mediaInfo *baseinfo.Media) *baseinfo.SnsMediaItem {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Recovered from panic: %v\n", r)
// 这里可以记录日志或者执行其他的恢复操作
}
}()
retItem := &baseinfo.SnsMediaItem{}
if snsVideoResponse == nil {
return retItem
}
if mediaInfo == nil {
return retItem
}
retItem.ID = 0
retItem.Type = mediaInfo.Type
retItem.Description = mediaInfo.Description
retItem.Private = baseinfo.MMSnsPrivacyPublic
retItem.UserData = mediaInfo.UserData
retItem.SubType = mediaInfo.SubType
retItem.VideoWidth = mediaInfo.VideoSize.Width
retItem.VideoHeight = mediaInfo.VideoSize.Height
retItem.URL = snsVideoResponse.FileURL
retItem.URL = strings.ReplaceAll(retItem.URL, "&", "&amp;")
retItem.URLType = mediaInfo.URL.Type
retItem.MD5 = snsVideoResponse.ReqData.RawFileMD5
retItem.VideoMD5 = snsVideoResponse.ReqData.Mp4Identify
retItem.Thumb = snsVideoResponse.ThumbURL
retItem.Thumb = strings.ReplaceAll(retItem.Thumb, "&", "&amp;")
retItem.ThumType = mediaInfo.Thumb.Type
retItem.SizeWidth = mediaInfo.Size.Width
retItem.SizeHeight = mediaInfo.Size.Height
retItem.TotalSize = mediaInfo.Size.TotalSize
retItem.VideoDuration = mediaInfo.VideoDuration
return retItem
}

268
srv/wxtask/wxsocketmsg.go Normal file
View File

@@ -0,0 +1,268 @@
package wxtask
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strconv"
"sync"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxface"
"github.com/gorilla/websocket"
)
// 全局写锁映射,保护每个连接的写操作
var connWriteMutexes sync.Map
// decodeUnicodeEscapes 解码Unicode转义字符
func decodeUnicodeEscapes(s string) string {
// 处理JSON中的Unicode转义字符如\u003c
re := regexp.MustCompile(`\\u([0-9a-fA-F]{4})`)
result := re.ReplaceAllStringFunc(s, func(match string) string {
// 提取十六进制数字部分
hexStr := match[2:] // 去掉\u前缀
// 将十六进制转换为整数
if codePoint, err := strconv.ParseInt(hexStr, 16, 32); err == nil {
// 转换为Unicode字符
return string(rune(codePoint))
}
return match // 如果转换失败,返回原字符串
})
return result
}
// 长链接同步消息
type WXSocketMsgTask struct {
WxConn wxface.IWXConnect
// 待消费消息列表
MsgItemChan chan *wechat.AddMsg
// 可不可以消费下一条
canDoNext chan bool
// 结束标志
EndChan chan bool
// 是否正在运行
isRunning bool
// 是否启用WebSocket
wsEnabled bool
// 当前在消费的消息
CurrentMsgItem *wechat.AddMsg
// 存储所有连接的客户端
Clients map[string]*websocket.Conn
Upgrader websocket.Upgrader
// 定义一个互斥锁; 用于保护 Clients 的线程安全
//mu sync.Mutex
// 定义一个读写互斥锁; 用于保护 Clients 的线程安全
mu sync.RWMutex
}
// NewWXSocketMsgTask 新建抢红包管理器
func NewWXSocketMsgTask(wxConn wxface.IWXConnect) *WXSocketMsgTask {
return &WXSocketMsgTask{
WxConn: wxConn,
MsgItemChan: make(chan *wechat.AddMsg, 1000),
canDoNext: make(chan bool, 1),
EndChan: make(chan bool, 1),
isRunning: false,
wsEnabled: true, // 默认启用WebSocket功能避免连接后延迟
Clients: make(map[string]*websocket.Conn),
Upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 允许所有CORS跨域请求
CheckOrigin: func(r *http.Request) bool {
return true
},
},
mu: sync.RWMutex{},
}
}
func (wsmsg *WXSocketMsgTask) grapMsg() {
for {
select {
case wsmsg.CurrentMsgItem = <-wsmsg.MsgItemChan:
key := wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID
// 使用消息包装器包含UUID信息
msgWrapper := NewMessageWrapper(key, wsmsg.CurrentMsgItem, "message")
jsonBytes, err := json.Marshal(msgWrapper)
if err != nil {
fmt.Println("消息序列化错误:", err)
continue // 继续处理下一条消息,而不是返回
}
conn := wsmsg.GetWebSocket(key)
if conn == nil {
fmt.Println("WebSocket连接不存在:", key)
continue // 继续处理下一条消息,而不是返回
}
// 对JSON内容进行Unicode解码处理
decodedJSON := decodeUnicodeEscapes(string(jsonBytes))
err = PushMessageToClient(decodedJSON, conn)
if err != nil {
fmt.Println("消息推送失败:", err)
wsmsg.DeleteWebSocket(key)
continue // 继续处理下一条消息,而不是返回
}
// 消息推送成功后继续处理下一条
case <-wsmsg.EndChan:
wsmsg.DeleteWebSocket(wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID)
return
}
}
}
// Start 开始
func (wsmsg *WXSocketMsgTask) Start() {
if wsmsg.wsEnabled {
if wsmsg.isRunning {
return
}
wsmsg.isRunning = true
go wsmsg.grapMsg() // 直接启动消息处理循环
}
}
// Stop 结束协程
func (wsmsg *WXSocketMsgTask) Stop() {
if !wsmsg.isRunning {
return
}
wsmsg.isRunning = false
wsmsg.EndChan <- true
}
// GrapNext 抢下一个
func (wsmsg *WXSocketMsgTask) GrapNext() {
wsmsg.canDoNext <- true
}
// AddMsgItem 添加消息项
func (wsmsg *WXSocketMsgTask) AddMsgItem(msgItem *wechat.AddMsg) {
if !wsmsg.wsEnabled {
return
}
if !wsmsg.isRunning {
wsmsg.Start()
}
key := wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID
conn := wsmsg.GetWebSocket(key)
// 打印日志
// fmt.Println("------------收到消息-----------\n",
// "NickName: ",wsmsg.WxConn.GetWXAccount().GetUserInfo().NickName,"\n",
// "Key: ",key,"\n",
// "Content: ",msgItem.Content.GetStr(),
// "\n",
// "----------------------------------")
if conn == nil {
// 只有 ws 长链接存在才会添加 收到的消息记录
// fmt.Println("WebSocket连接不存在消息将不会发送:", key)
return
}
wsmsg.MsgItemChan <- msgItem
}
// GetCurrentMsgItem 获取当前正在消费的消息项
func (wsmsg *WXSocketMsgTask) GetCurrentMsgItem() *wechat.AddMsg {
return wsmsg.CurrentMsgItem
}
func PushMessageToClient(msg string, conn *websocket.Conn) error {
// 获取或创建当前连接的写锁
val, _ := connWriteMutexes.LoadOrStore(conn, &sync.Mutex{})
mutex := val.(*sync.Mutex)
// 使用互斥锁保护写操作
mutex.Lock()
defer mutex.Unlock()
// 使用 WebSocket 连接向客户端发送消息
fmt.Println("发送消息:", msg)
err := conn.WriteMessage(websocket.TextMessage, []byte(msg))
if err != nil {
// 如果发送错误,清理锁
connWriteMutexes.Delete(conn)
}
return err
}
// PushTypedMessageToClient 安全地发送特定类型的WebSocket消息
func PushTypedMessageToClient(messageType int, data []byte, conn *websocket.Conn) error {
// 获取或创建当前连接的写锁
val, _ := connWriteMutexes.LoadOrStore(conn, &sync.Mutex{})
mutex := val.(*sync.Mutex)
// 使用互斥锁保护写操作
mutex.Lock()
defer mutex.Unlock()
// 使用 WebSocket 连接向客户端发送消息
fmt.Printf("发送类型[%d]消息\n", messageType)
err := conn.WriteMessage(messageType, data)
if err != nil {
// 如果发送错误,清理锁
connWriteMutexes.Delete(conn)
}
return err
}
// GetWebSocket 获取 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) GetWebSocket(key string) (conn *websocket.Conn) {
wsmsg.mu.RLock() // 读取 Clients 时使用读锁
defer wsmsg.mu.RUnlock() // 解锁读锁
conn = wsmsg.Clients[key] // 读取 ws 长链接; 若 key 不存在则返回 nil
return conn
}
// UpdateWebSocket 更新 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) UpdateWebSocket(key string, conn *websocket.Conn) {
wsmsg.mu.Lock() // 写入或删除 Clients 时使用写锁
defer wsmsg.mu.Unlock() // 解锁
wsmsg.Clients[key] = conn // 更新 ws 长链接
wsmsg.wsEnabled = true // 当连接建立时启用WebSocket功能
fmt.Println("WebSocket连接已更新:", key)
}
// DeleteWebSocket 删除 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) DeleteWebSocket(key string) {
wsmsg.mu.Lock() // 写入或删除 Clients 时使用写锁
defer wsmsg.mu.Unlock() // 解锁
if conn, ok := wsmsg.Clients[key]; ok {
// 清理连接的写锁
connWriteMutexes.Delete(conn)
delete(wsmsg.Clients, key) // 删除 ws 长链接
fmt.Println("WebSocket连接已删除:", key)
// 如果没有连接禁用WebSocket功能并停止任务
if len(wsmsg.Clients) == 0 {
wsmsg.wsEnabled = false
if wsmsg.isRunning {
wsmsg.Stop()
}
}
}
}
// 获取WebSocket启用状态
func (wsmsg *WXSocketMsgTask) IsWebSocketEnabled() bool {
return wsmsg.wsEnabled
}
// 设置WebSocket启用状态
func (wsmsg *WXSocketMsgTask) SetWebSocketEnabled(enabled bool) {
wsmsg.wsEnabled = enabled
// 如果禁用而且正在运行,停止任务
if !enabled && wsmsg.isRunning {
wsmsg.Stop()
} else if enabled && !wsmsg.isRunning && len(wsmsg.Clients) > 0 {
// 如果启用而且有客户端连接但未运行,启动任务
wsmsg.Start()
}
}

View File

@@ -0,0 +1,68 @@
package wxtask
import (
"sync"
"xiawan/wx/srv/wxface"
)
// WXVerifyTask 加我/拉我进群聊是否需要验证
type WXVerifyTask struct {
wxConn wxface.IWXConnect
// 结束标志
notNeedVerify bool
// 自动入群邀请
autoJoinGroup bool
// 互斥锁
lock sync.Mutex
}
// NewWXVerifyTask 新建朋友圈任务管理器
func NewWXVerifyTask(wxConn wxface.IWXConnect) *WXVerifyTask {
return &WXVerifyTask{
wxConn: wxConn,
notNeedVerify: true,
autoJoinGroup: false,
}
}
// IsNeedVerify 判断是否需要验证
func (wxvt *WXVerifyTask) IsNeedVerify() bool {
wxvt.lock.Lock()
defer wxvt.lock.Unlock()
return wxvt.notNeedVerify
}
// SetNeedVerify 设置被添加,被拉入群聊时是否需要验证
func (wxvt *WXVerifyTask) SetNeedVerify(needVerify bool) {
wxvt.notNeedVerify = needVerify
}
// 设置 autoJoinGroup
func (wxvt *WXVerifyTask) SetAutoJoinGroup(autoJoinGroup bool) {
wxvt.lock.Lock()
defer wxvt.lock.Unlock()
wxvt.autoJoinGroup = autoJoinGroup
}
// 获取 autoJoinGroup
func (wxvt *WXVerifyTask) GetAutoJoinGroup() bool {
wxvt.lock.Lock()
defer wxvt.lock.Unlock()
return wxvt.autoJoinGroup
}
// 判断 autoJoinGroup
func (wxvt *WXVerifyTask) IsAutoJoinGroup() bool {
wxvt.lock.Lock()
defer wxvt.lock.Unlock()
return wxvt.autoJoinGroup
}
// 入群
func (wxvt *WXVerifyTask) AddJoinGroup(url string) error {
wxvt.lock.Lock()
defer wxvt.lock.Unlock()
_, err := wxvt.wxConn.GetWXReqInvoker().ConsentToJoinGroupRequest(url)
return err
}