1219 lines
38 KiB
Go
1219 lines
38 KiB
Go
package wxcore
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"math/rand"
|
||
"sync"
|
||
"sync/atomic"
|
||
"time"
|
||
|
||
"github.com/lunny/log"
|
||
|
||
"xiawan/wx/clientsdk"
|
||
"xiawan/wx/clientsdk/baseinfo"
|
||
"xiawan/wx/clientsdk/mmtls"
|
||
"xiawan/wx/db"
|
||
"xiawan/wx/srv"
|
||
"xiawan/wx/srv/srvconfig"
|
||
"xiawan/wx/srv/websrv"
|
||
"xiawan/wx/srv/wxface"
|
||
)
|
||
|
||
// WXConnect 微信链接
|
||
type WXConnect struct {
|
||
wxServer wxface.IWXServer
|
||
// 微信链接ID
|
||
wxConnID uint32
|
||
// 微信账号信息
|
||
wxAccount *srv.WXAccount
|
||
// 发送请求缓存队列
|
||
longReqQueue chan wxface.IWXLongRequest
|
||
// 请求调用器
|
||
wxReqInvoker wxface.IWXReqInvoker
|
||
// 缓存器
|
||
wxCache wxface.IWXCache
|
||
// 任务管理器
|
||
wxTaskMgr wxface.IWXTaskMgr
|
||
// 同步请求管理器
|
||
wxSyncMgr wxface.IWXSyncMgr
|
||
// 文件助手消息管理器
|
||
wxFileHelperMgr wxface.IWXFileHelperMgr
|
||
//用户消息管理器
|
||
wxUserMsgMgr wxface.IWXUserMsgMgr
|
||
// web任务管理器
|
||
webTaskMgr *websrv.WebTaskMgr
|
||
// 心跳定时器
|
||
heartBeatTimer *time.Timer
|
||
// 二次登录定时器
|
||
autoAuthTimer *time.Timer
|
||
// 断开链接
|
||
ExitFlagChan chan bool
|
||
// 是否连接着 (原子操作)
|
||
connected int32
|
||
// 首次登录初始化只执行一次
|
||
// onceInit sync.Once
|
||
// 出错次数
|
||
// errCount int
|
||
// 开始的时间戳
|
||
StartDateTime int64
|
||
// 是否开启长连接监控
|
||
IsOpenShortLink bool
|
||
// 心跳时间
|
||
HeartBeatTime int64
|
||
// 扫码时间秒
|
||
ScanCodeTime int64
|
||
// 上次尝试重连长连接的时间
|
||
lastLongRetryTime int64
|
||
// 长连接重试计数器
|
||
longRetryCount int
|
||
// 互斥锁
|
||
mu sync.RWMutex
|
||
// 重启标志
|
||
restarting int32
|
||
}
|
||
|
||
// NewWXConnect 新的微信连接
|
||
func NewWXConnect(wxServer wxface.IWXServer, wxAccount *srv.WXAccount) wxface.IWXConnect {
|
||
// 从根上下文创建一个上下文
|
||
wxconn := &WXConnect{
|
||
wxServer: wxServer,
|
||
wxAccount: wxAccount,
|
||
longReqQueue: make(chan wxface.IWXLongRequest, 5000),
|
||
ExitFlagChan: make(chan bool, 1),
|
||
connected: 0,
|
||
HeartBeatTime: 0, // 默认 0 ,没有开始心跳
|
||
StartDateTime: time.Now().Unix(),
|
||
IsOpenShortLink: true,
|
||
ScanCodeTime: 0,
|
||
lastLongRetryTime: 0,
|
||
longRetryCount: 0,
|
||
restarting: 0,
|
||
}
|
||
wxconn.wxReqInvoker = NewWXReqInvoker(wxconn)
|
||
wxconn.wxTaskMgr = NewWXTaskMgr(wxconn)
|
||
wxconn.wxCache = NewWXCache(wxconn)
|
||
wxconn.wxSyncMgr = NewWXSyncMgr(wxconn)
|
||
//文件助手消息管理器
|
||
wxconn.wxFileHelperMgr = NewWXFileHelperMgr(wxconn)
|
||
//用户消息管理器
|
||
wxconn.wxUserMsgMgr = NewWXUSerMsgMgr(wxconn)
|
||
// 上报任务
|
||
wxconn.webTaskMgr = websrv.NewWebTaskMgr()
|
||
return wxconn
|
||
}
|
||
|
||
// startLongWriter 开启长链接发送数据
|
||
func (wxconn *WXConnect) startLongWriter() {
|
||
StartDateTime := wxconn.StartDateTime
|
||
for {
|
||
// 判断链接断开立马暂停
|
||
if !wxconn.IsConnected() {
|
||
return
|
||
}
|
||
// 判断是否重复启动 for 循环
|
||
if StartDateTime != wxconn.StartDateTime {
|
||
// 结束
|
||
return
|
||
}
|
||
select {
|
||
case longReq := <-wxconn.longReqQueue:
|
||
// 心跳和登录都会走这里
|
||
wxconn.ScanCodeTime = time.Now().Unix()
|
||
mmInfo := wxconn.wxAccount.GetUserInfo().MMInfo
|
||
err := mmtls.MMTCPSendReq(mmInfo, longReq.GetOpcode(), longReq.GetData())
|
||
if err != nil {
|
||
// 断开链接
|
||
fmt.Printf("[%s],[%s] 断开链接 - %s \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName, err.Error())
|
||
// 判断是心跳出错 - 发送二次登录
|
||
if longReq.GetOpcode() == mmtls.MMLongOperationHeartBeat {
|
||
// 等 200 毫秒
|
||
time.Sleep(200 * time.Millisecond)
|
||
if StartDateTime == wxconn.StartDateTime {
|
||
wxconn.restartLong()
|
||
}
|
||
} else {
|
||
wxconn.Stop() // 断开链接
|
||
}
|
||
}
|
||
continue
|
||
case <-wxconn.heartBeatTimer.C:
|
||
// 发送心跳包
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOnLine {
|
||
_ = wxconn.wxReqInvoker.SendHeartBeatRequest()
|
||
}
|
||
// 重置定时器,继续下一次心跳
|
||
wxconn.heartBeatTimer.Reset(time.Second * 175)
|
||
continue
|
||
case <-wxconn.autoAuthTimer.C:
|
||
// 进行二次登录
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOnLine {
|
||
_ = wxconn.wxReqInvoker.SendAutoAuthRequest()
|
||
}
|
||
continue
|
||
case <-wxconn.ExitFlagChan:
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重新链接
|
||
func (wxconn *WXConnect) restartLong() {
|
||
defer TryE("(wxconn *WXConnect) restartLong()")
|
||
|
||
// 防止重复重启
|
||
if !atomic.CompareAndSwapInt32(&wxconn.restarting, 0, 1) {
|
||
log.Printf("[%s] restartLong already in progress, skipping\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName())
|
||
return
|
||
}
|
||
defer atomic.StoreInt32(&wxconn.restarting, 0)
|
||
|
||
wxconn.mu.Lock()
|
||
|
||
// 断开链接
|
||
wxconn.setConnected(false)
|
||
|
||
// 安全发送退出信号
|
||
select {
|
||
case wxconn.ExitFlagChan <- true:
|
||
default:
|
||
// 通道已满或已关闭,忽略
|
||
}
|
||
|
||
// 关闭长链接
|
||
userInfo := wxconn.wxAccount.GetUserInfo()
|
||
if userInfo.MMInfo != nil && userInfo.MMInfo.Conn != nil {
|
||
userInfo.MMInfo.Conn.Close()
|
||
userInfo.MMInfo = nil
|
||
}
|
||
|
||
wxconn.mu.Unlock()
|
||
|
||
// 异步重启以避免阻塞
|
||
go func() {
|
||
defer TryE("restartLong async restart")
|
||
|
||
// 等待 30 秒
|
||
time.Sleep(30 * time.Second)
|
||
|
||
// 检查是否仍需要重启
|
||
if wxconn.IsConnected() {
|
||
log.Printf("[%s] Connection already restored, skipping restart\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName())
|
||
return
|
||
}
|
||
|
||
//重新开始
|
||
err := wxconn.startLongLink()
|
||
if err != nil {
|
||
log.Printf("[%s] restartLong - startLongLink error: %v\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("[%s] 开始长连接状态!startLongLink\n", userInfo.UUID)
|
||
wxconn.SendHeartBeatWaitingSeconds(1)
|
||
// 等待30-60分钟
|
||
minutes := uint32(rand.Int31n(30)) + 30
|
||
// minutes := uint32(1)
|
||
wxconn.SendAutoAuthWaitingMinutes(minutes)
|
||
|
||
log.Printf("[%s] Long connection restarted successfully\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName())
|
||
}()
|
||
}
|
||
|
||
// 监听长连接失败,实际上没掉线
|
||
func (wxconn *WXConnect) StartShortReader() {
|
||
StartDateTime := wxconn.StartDateTime
|
||
// 延时 20 秒
|
||
time.Sleep(20 * time.Second)
|
||
// 20 秒之后大概率已经建立了长连接,如果建立长连接,则重新启动
|
||
// 判断是否关闭
|
||
if !wxconn.IsOpenShortLink {
|
||
return
|
||
}
|
||
forCount := 0
|
||
// 从配置获取最大重试次数和重试间隔
|
||
maxLongRetries := srvconfig.GlobalSetting.ProxyConfig.MaxLongRetryTimes
|
||
minRetryInterval := int64(srvconfig.GlobalSetting.ProxyConfig.LongRetryInterval)
|
||
|
||
for {
|
||
// 判断是否重复启动 for 循环
|
||
if StartDateTime != wxconn.StartDateTime {
|
||
// 结束
|
||
return
|
||
}
|
||
// 已关闭
|
||
if !wxconn.IsOpenShortLink {
|
||
return
|
||
}
|
||
TimeSec := time.Now().Unix()
|
||
if TimeSec-wxconn.ScanCodeTime < 10 {
|
||
if wxconn.ScanCodeTime != 0 {
|
||
// 等待 3 秒
|
||
time.Sleep(3 * time.Second)
|
||
// 跳过
|
||
continue
|
||
}
|
||
}
|
||
// 判断是否在线
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOnLine {
|
||
// 每10次短连接心跳前先尝试长连接
|
||
if forCount%10 == 0 && !wxconn.IsConnected() {
|
||
// 更新重试时间
|
||
wxconn.lastLongRetryTime = time.Now().Unix()
|
||
|
||
// 尝试发送长连接心跳,恢复长连接
|
||
log.Printf("[%s],[%s] 定期尝试恢复长连接心跳\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 使用defer+recover防止恢复过程中的panic影响短连接心跳
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
log.Printf("[%s],[%s] 恢复长连接心跳时发生错误: %v\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName, r)
|
||
}
|
||
}()
|
||
wxconn.restartLong()
|
||
}()
|
||
|
||
// 给长连接一些时间建立
|
||
time.Sleep(2 * time.Second)
|
||
|
||
// 恢复后检查连接状态并记录日志
|
||
if wxconn.IsConnected() {
|
||
log.Printf("[%s],[%s] 长连接心跳恢复成功\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
wxconn.longRetryCount = 0 // 连接成功后重置计数器
|
||
|
||
// 长连接恢复后,立即触发一次消息同步
|
||
wxconn.wxSyncMgr.SendNewSyncRequest()
|
||
time.Sleep(500 * time.Millisecond) // 给同步请求一些处理时间
|
||
|
||
// 如果长连接成功,停止短连接心跳
|
||
return
|
||
} else {
|
||
log.Printf("[%s],[%s] 长连接心跳恢复失败,继续使用短连接同步\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
}
|
||
}
|
||
|
||
// 尝试恢复长连接心跳 - 代理可能已经恢复
|
||
if !wxconn.IsConnected() {
|
||
// 检查是否应该尝试恢复长连接
|
||
// 距离上次尝试时间超过最小间隔,并且重试次数在允许范围内
|
||
currentTime := time.Now().Unix()
|
||
shouldRetry := (currentTime-wxconn.lastLongRetryTime >= minRetryInterval) &&
|
||
(wxconn.longRetryCount < maxLongRetries)
|
||
|
||
// 如果重试计数器已达上限,但已经过去了更长时间(比如10倍间隔),重置计数器
|
||
if wxconn.longRetryCount >= maxLongRetries && (currentTime-wxconn.lastLongRetryTime >= minRetryInterval*10) {
|
||
log.Printf("[%s],[%s] 重置长连接重试计数器(已过去足够长时间)\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
wxconn.longRetryCount = 0
|
||
shouldRetry = true
|
||
}
|
||
|
||
if shouldRetry {
|
||
// 更新重试时间和计数器
|
||
wxconn.lastLongRetryTime = currentTime
|
||
wxconn.longRetryCount++
|
||
|
||
// 尝试发送长连接心跳,恢复长连接
|
||
log.Printf("[%s],[%s] 尝试恢复长连接心跳 (尝试 %d/%d)\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName,
|
||
wxconn.longRetryCount, maxLongRetries)
|
||
|
||
// 使用defer+recover防止恢复过程中的panic影响短连接心跳
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
log.Printf("[%s],[%s] 恢复长连接心跳时发生错误: %v\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName, r)
|
||
}
|
||
}()
|
||
wxconn.restartLong()
|
||
}()
|
||
|
||
// 给长连接一些时间建立
|
||
time.Sleep(1 * time.Second)
|
||
|
||
// 恢复后检查连接状态并记录日志
|
||
if wxconn.IsConnected() {
|
||
log.Printf("[%s],[%s] 长连接心跳恢复成功,重置重试计数器\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
wxconn.longRetryCount = 0 // 连接成功后重置计数器
|
||
|
||
// 长连接恢复后,立即触发一次消息同步
|
||
wxconn.wxSyncMgr.SendNewSyncRequest()
|
||
time.Sleep(500 * time.Millisecond) // 给同步请求一些处理时间
|
||
|
||
// 同时触发收藏同步
|
||
currentTaskMgr := wxconn.GetWXTaskMgr()
|
||
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
|
||
currentSnsTransTask := taskMgr.GetSnsTransTask()
|
||
if currentSnsTransTask.IsSyncTrans() {
|
||
wxconn.wxSyncMgr.SendFavSyncRequest()
|
||
}
|
||
|
||
// 如果长连接成功,停止短连接心跳
|
||
return
|
||
} else {
|
||
log.Printf("[%s],[%s] 长连接心跳恢复失败,将继续使用短连接同步\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
}
|
||
} else if wxconn.longRetryCount >= maxLongRetries {
|
||
// 只有在第一次达到最大重试次数时记录日志
|
||
if wxconn.longRetryCount == maxLongRetries {
|
||
log.Printf("[%s],[%s] 已达到最大重试次数(%d),暂停长连接恢复尝试(将在%d秒后重置)\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName,
|
||
maxLongRetries,
|
||
minRetryInterval*10)
|
||
wxconn.longRetryCount++ // 增加一次以避免重复打印
|
||
}
|
||
}
|
||
}
|
||
|
||
// 发送同步消息
|
||
if forCount%20 == 0 {
|
||
isConn := "true"
|
||
if !wxconn.IsConnected() {
|
||
isConn = "false"
|
||
}
|
||
log.Printf("[%s],[%s] 短链接同步消息 StartShortReader: isConnected = %s \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName, isConn)
|
||
// 同步收藏转发
|
||
// 判断是否开启收藏转发
|
||
if wxconn.IsConnected() {
|
||
currentTaskMgr := wxconn.GetWXTaskMgr()
|
||
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
|
||
currentSnsTransTask := taskMgr.GetSnsTransTask()
|
||
if currentSnsTransTask.IsSyncTrans() {
|
||
wxconn.wxSyncMgr.SendFavSyncRequest()
|
||
// 延时一秒
|
||
time.Sleep(1 * time.Second)
|
||
}
|
||
}
|
||
}
|
||
// 即使长连接没有恢复,也继续使用短链接同步消息
|
||
wxconn.wxSyncMgr.SendNewSyncRequest()
|
||
time.Sleep(3 * time.Second)
|
||
forCount++
|
||
if forCount >= 100 {
|
||
forCount = 0
|
||
}
|
||
continue
|
||
}
|
||
forCount++
|
||
if forCount >= 100 {
|
||
forCount = 0
|
||
}
|
||
time.Sleep(3 * time.Second)
|
||
continue
|
||
}
|
||
}
|
||
|
||
// 关闭重启任务
|
||
func (wxconn *WXConnect) StopShortReader() {
|
||
wxconn.IsOpenShortLink = false
|
||
// 保存状态变更到数据库
|
||
currentUserInfo := wxconn.GetWXAccount().GetUserInfo()
|
||
if currentUserInfo != nil {
|
||
db.SaveShortLinkStatusToDB(currentUserInfo.UUID, false)
|
||
}
|
||
}
|
||
|
||
// startLongReader 开启长连接接受数据
|
||
func (wxconn *WXConnect) startLongReader() {
|
||
StartDateTime := wxconn.StartDateTime
|
||
|
||
// 重试机制相关变量
|
||
retryCount := 0
|
||
maxRetries := 5
|
||
baseDelay := time.Second
|
||
|
||
for {
|
||
// 判断链接断开立马暂停
|
||
if !wxconn.IsConnected() {
|
||
break
|
||
}
|
||
|
||
// 判断是否重复启动 for 循环
|
||
if StartDateTime != wxconn.StartDateTime {
|
||
break
|
||
}
|
||
|
||
// 接收数据
|
||
mmInfo := wxconn.wxAccount.GetUserInfo().MMInfo
|
||
recvInfo, err := mmtls.MMTCPRecvData(mmInfo)
|
||
if err != nil {
|
||
errorMsg := err.Error()
|
||
log.Printf("[%s],[%s] 长连接出错 - %s \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName, errorMsg)
|
||
if !wxconn.IsConnected() {
|
||
return
|
||
}
|
||
// 等待200毫秒
|
||
time.Sleep(200 * time.Millisecond)
|
||
// 判断是否重复启动 for 循环(StartDateTime 标识不对说明已经被重启了)
|
||
if StartDateTime != wxconn.StartDateTime {
|
||
// 结束
|
||
return
|
||
}
|
||
wxconn.restartLong()
|
||
return
|
||
}
|
||
// 系统推送-需要同步消息
|
||
if recvInfo.HeaderInfo.Operation < 1000000000 {
|
||
//fmt.Println(recvInfo.HeaderInfo.Operation)
|
||
loginState := wxconn.GetWXAccount().GetLoginState()
|
||
if loginState != baseinfo.MMLoginStateOnLine {
|
||
continue
|
||
}
|
||
|
||
// 同步收藏
|
||
if recvInfo.HeaderInfo.Operation == baseinfo.MMLongOperatorTypeFavSync {
|
||
wxconn.wxSyncMgr.SendFavSyncRequest()
|
||
continue
|
||
}
|
||
// TODO 发送同步请求
|
||
// syncKey := wxconn.wxAccount.GetUserInfo().SyncKey
|
||
// if syncKey == nil || len(syncKey) == 0 {
|
||
// wxconn.onceInit.Do(func() {
|
||
// wxconn.wxSyncMgr.SendSyncInitRequest()
|
||
// })
|
||
// } else {
|
||
wxconn.wxSyncMgr.SendNewSyncRequest()
|
||
// }
|
||
continue
|
||
}
|
||
|
||
// 解包响应数据
|
||
packHeader, err := clientsdk.DecodePackHeader(recvInfo.RespData, nil)
|
||
if err != nil {
|
||
// 判断是否重复启动 for 循环
|
||
if StartDateTime != wxconn.StartDateTime {
|
||
// 结束
|
||
return
|
||
}
|
||
// TODO 接受消息出错,断开链接-token登陆
|
||
log.Printf("[%s] startLongReader - DecodePackHeader error: %v, operation: %v\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), err, recvInfo.HeaderInfo.Operation)
|
||
/*
|
||
* 注意: 这里新号首次登录时(同省IP首次可能会多次掉线, 同市掉线少, 家里的内网穿透 socks5 代理IP基本不会掉线)
|
||
* 另外, 24小时内还可能会接收消息出错(导致掉线), 三天后基本稳定
|
||
*/
|
||
|
||
// 网络异常超时或解包失败 - 修复空指针问题
|
||
if packHeader == nil {
|
||
retryCount++
|
||
log.Printf("[%s] packHeader is nil (retry %d/%d), restarting connection\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), retryCount, maxRetries)
|
||
|
||
if retryCount >= maxRetries {
|
||
wxconn.restartLong()
|
||
return
|
||
}
|
||
|
||
// 指数退避
|
||
delay := baseDelay * time.Duration(1<<uint(retryCount-1))
|
||
time.Sleep(delay)
|
||
continue
|
||
}
|
||
|
||
// 重置重试计数器(成功解包)
|
||
retryCount = 0
|
||
|
||
// 检查会话超时
|
||
switch packHeader.RetCode {
|
||
case baseinfo.MMErrSessionTimeOut:
|
||
log.Printf("[%s] Session timeout, sending auto auth request\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName())
|
||
wxconn.HeartBeatTime = 0
|
||
wxconn.setConnected(false)
|
||
_ = wxconn.GetWXReqInvoker().SendAutoAuthRequest()
|
||
case baseinfo.MMErrDropped:
|
||
log.Printf("[%s] 账号被强制下线(MMErrDropped), 停止连接\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName())
|
||
wxconn.Stop()
|
||
return
|
||
default:
|
||
if !wxconn.IsConnected() {
|
||
return
|
||
}
|
||
log.Printf("[%s] Decode error with retcode: %d, restarting connection\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), packHeader.RetCode)
|
||
wxconn.restartLong()
|
||
return
|
||
}
|
||
return
|
||
}
|
||
wxResp := NewWXResponse(wxconn, packHeader)
|
||
wxconn.SendToWXMsgHandler(wxResp)
|
||
}
|
||
}
|
||
|
||
// startLong 开启长链接
|
||
func (wxconn *WXConnect) startLongLink() error {
|
||
defer TryE("(wxconn *WXConnect) startLongLink()")
|
||
wxconn.StartDateTime = int64(time.Now().Unix())
|
||
|
||
// 清理旧的定时器,防止资源泄漏
|
||
if wxconn.heartBeatTimer != nil {
|
||
wxconn.heartBeatTimer.Stop()
|
||
}
|
||
if wxconn.autoAuthTimer != nil {
|
||
wxconn.autoAuthTimer.Stop()
|
||
}
|
||
|
||
// 清空 ExitFlagChan 中可能残留的信号
|
||
select {
|
||
case <-wxconn.ExitFlagChan:
|
||
// 清空旧的退出信号
|
||
default:
|
||
// 通道为空,继续
|
||
}
|
||
|
||
// 在这里添加短连接状态的恢复
|
||
wxconn.IsOpenShortLink = true
|
||
// 从数据库恢复短连接状态
|
||
uuid := wxconn.wxAccount.GetUserInfo().UUID
|
||
shortLinkStatus := db.LoadShortLinkStatusFromDB(uuid)
|
||
if shortLinkStatus != nil {
|
||
wxconn.IsOpenShortLink = shortLinkStatus.IsEnabled
|
||
}
|
||
|
||
go wxconn.StartShortReader()
|
||
// 先进行长链接握手
|
||
userInfo := wxconn.wxAccount.GetUserInfo()
|
||
dialer := userInfo.GetDialer()
|
||
tmpMMInfo, err := mmtls.InitMMTLSInfoLong(dialer, userInfo.LongHost, userInfo.ShortHost, nil)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
tmpMMInfo.Dialer = dialer
|
||
|
||
// 设置代理配置到MMInfo
|
||
tmpMMInfo.LongConnTimeout = srvconfig.GlobalSetting.ProxyConfig.LongConnTimeout
|
||
tmpMMInfo.LongConnReadTimeout = srvconfig.GlobalSetting.ProxyConfig.LongConnReadTimeout
|
||
tmpMMInfo.LongConnRetryTimes = srvconfig.GlobalSetting.ProxyConfig.LongConnRetryTimes
|
||
tmpMMInfo.LongConnRetryInterval = srvconfig.GlobalSetting.ProxyConfig.LongConnRetryInterval
|
||
tmpMMInfo.ShortConnTimeout = srvconfig.GlobalSetting.ProxyConfig.ShortConnTimeout
|
||
tmpMMInfo.AllowDirectOnProxyFail = srvconfig.GlobalSetting.ProxyConfig.AllowDirectOnProxyFail
|
||
|
||
wxconn.setConnected(true)
|
||
userInfo.MMInfo = tmpMMInfo
|
||
// 启动长链接发送接收协程(创建新的定时器)
|
||
wxconn.heartBeatTimer = time.NewTimer(time.Second * 175)
|
||
wxconn.autoAuthTimer = time.NewTimer(time.Minute * 30)
|
||
// 收藏转发任务
|
||
wxconn.wxSyncMgr.Start()
|
||
go wxconn.startLongWriter()
|
||
go wxconn.startLongReader()
|
||
return nil
|
||
}
|
||
|
||
// Start 开启微信链接任务
|
||
func (wxconn *WXConnect) Start() error {
|
||
wxconn.mu.Lock()
|
||
// 如果是链接状态
|
||
if wxconn.IsConnected() {
|
||
wxconn.mu.Unlock()
|
||
return nil
|
||
}
|
||
// wxconn.errCount = 0
|
||
userInfo := wxconn.wxAccount.GetUserInfo()
|
||
// 判断微信信息是否为空
|
||
if userInfo == nil {
|
||
wxconn.mu.Unlock()
|
||
return errors.New("wxconn.Start() err: userInfo == nil")
|
||
}
|
||
isLock := true
|
||
defer func() {
|
||
if isLock {
|
||
isLock = false
|
||
wxconn.mu.Unlock()
|
||
}
|
||
}()
|
||
go func() {
|
||
// 等待 30 秒
|
||
time.Sleep(30 * time.Second)
|
||
// 关闭
|
||
if isLock {
|
||
isLock = false
|
||
log.Printf("Start - [%s],[%s] 长连接超时 30s - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
wxconn.mu.Unlock()
|
||
}
|
||
}()
|
||
|
||
// 开启长链接
|
||
err := wxconn.startLongLink()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 开启任务管理器
|
||
// wxconn.wxTaskMgr.Start()
|
||
wxconn.webTaskMgr.Start()
|
||
wxconn.wxFileHelperMgr.Start()
|
||
wxconn.wxUserMsgMgr.Start()
|
||
wxconn.wxServer.GetWXConnectMgr().Add(wxconn)
|
||
|
||
// 初始化置顶群缓存
|
||
currentTaskMgr := wxconn.wxTaskMgr
|
||
uuid := userInfo.UUID
|
||
taskMgr, _ := currentTaskMgr.(*WXTaskMgr)
|
||
currentGrapHBMgr := taskMgr.GetGrabHBTask()
|
||
currentGrapHBMgr.InitTopGroup(uuid) // 初始化群置顶
|
||
// 初始化同步收藏朋友圈
|
||
currentSnsTransTask := taskMgr.GetSnsTransTask()
|
||
currentSnsTransTask.InitFriendTransMap()
|
||
|
||
fmt.Printf("[%s] 开始长连接状态!\n", userInfo.UUID)
|
||
// 设置下二次登录时间12
|
||
//userInfo.UpdateLastAuthTime()
|
||
wxconn.SendWebTask(websrv.TaskStateLoginSuccess, websrv.TaskTypeUploadStatus)
|
||
return nil
|
||
}
|
||
|
||
// Stop 停止链接
|
||
func (wxconn *WXConnect) Stop() {
|
||
wxconn.mu.Lock()
|
||
defer wxconn.mu.Unlock()
|
||
|
||
// 如果已经停止,则不需要再次停止
|
||
if !wxconn.IsConnected() {
|
||
return
|
||
}
|
||
|
||
// 在停止连接前,确保当前状态被持久化保存
|
||
currentUserInfo := wxconn.GetWXAccount().GetUserInfo()
|
||
if currentUserInfo != nil {
|
||
// 获取最后的状态缓存
|
||
statusCache := db.GetCheckStatusCache(currentUserInfo.UUID)
|
||
if statusCache != nil {
|
||
// 确保状态被持久化保存
|
||
db.SaveLastLoginStatus(currentUserInfo.UUID, statusCache)
|
||
}
|
||
|
||
// 保存短链接状态到数据库
|
||
db.SaveShortLinkStatusToDB(currentUserInfo.UUID, wxconn.IsOpenShortLink)
|
||
|
||
// 清理WebSocket连接
|
||
if taskMgr, ok := wxconn.wxTaskMgr.(*WXTaskMgr); ok {
|
||
wsTask := taskMgr.SocketMsgTask
|
||
userUUID := currentUserInfo.UUID
|
||
existingConn := wsTask.GetWebSocket(userUUID)
|
||
if existingConn != nil {
|
||
fmt.Println("Stop()时清理WebSocket连接:", userUUID)
|
||
existingConn.Close()
|
||
wsTask.DeleteWebSocket(userUUID)
|
||
}
|
||
// 禁用WebSocket功能
|
||
wsTask.SetWebSocketEnabled(false)
|
||
}
|
||
}
|
||
|
||
// 断开链接
|
||
wxconn.setConnected(false)
|
||
|
||
// 安全发送退出信号
|
||
select {
|
||
case wxconn.ExitFlagChan <- true:
|
||
default:
|
||
// 通道已满,忽略
|
||
}
|
||
|
||
// 停止定时器
|
||
if wxconn.heartBeatTimer != nil {
|
||
wxconn.heartBeatTimer.Stop()
|
||
}
|
||
if wxconn.autoAuthTimer != nil {
|
||
wxconn.autoAuthTimer.Stop()
|
||
}
|
||
|
||
// 关闭短链接同步消息
|
||
go wxconn.StopShortReader()
|
||
// 重置长连接开始时间
|
||
wxconn.StartDateTime = int64(time.Now().Unix())
|
||
fmt.Println("Stop() 关闭长链接: ", wxconn.StartDateTime)
|
||
// 关闭长链接
|
||
userInfo := wxconn.wxAccount.GetUserInfo()
|
||
if userInfo.MMInfo != nil && userInfo.MMInfo.Conn != nil {
|
||
userInfo.MMInfo.Conn.Close()
|
||
}
|
||
go func() {
|
||
// 设置成离线状态
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOnLine {
|
||
wxconn.wxAccount.SetLoginState(baseinfo.MMLoginStateOffLine)
|
||
db.UpdateUserInfo(userInfo)
|
||
db.UpdateLoginStatus(userInfo.GetUserName(), int32(userInfo.GetLoginState()), "已离线")
|
||
}
|
||
}()
|
||
wxconn.HeartBeatTime = 0
|
||
wxconn.SendWebTask(websrv.TaskStateLogout, websrv.TaskTypeUploadStatus)
|
||
fmt.Printf("[%s],[%s],[%s] 退出!\n", userInfo.GetUserName(), userInfo.NickName, userInfo.UUID)
|
||
|
||
// 延迟关闭其他资源,确保状态保存完成
|
||
time.Sleep(time.Millisecond * 200)
|
||
go func() {
|
||
// 关闭任务管理器
|
||
wxconn.wxSyncMgr.Stop()
|
||
wxconn.wxFileHelperMgr.Stop()
|
||
wxconn.wxUserMsgMgr.Stop()
|
||
|
||
// 延迟从管理器中移除连接实例,给前端足够时间获取最后的状态
|
||
time.Sleep(time.Second * 5)
|
||
wxconn.wxServer.GetWXConnectMgr().Remove(wxconn)
|
||
}()
|
||
}
|
||
|
||
// SetWXConnID 设置微信链接ID
|
||
func (wxconn *WXConnect) SetWXConnID(wxConnID uint32) {
|
||
wxconn.wxConnID = wxConnID
|
||
}
|
||
|
||
// GetWXConnID 获取WX链接ID
|
||
func (wxconn *WXConnect) GetWXConnID() uint32 {
|
||
return wxconn.wxConnID
|
||
}
|
||
|
||
// GetWXAccount 获取微信帐号信息
|
||
func (wxconn *WXConnect) GetWXAccount() *srv.WXAccount {
|
||
return wxconn.wxAccount
|
||
}
|
||
|
||
// GetWXServer 获取微信服务器
|
||
func (wxconn *WXConnect) GetWXServer() wxface.IWXServer {
|
||
return wxconn.wxServer
|
||
}
|
||
|
||
// GetWXCache 获取缓存器
|
||
func (wxconn *WXConnect) GetWXCache() wxface.IWXCache {
|
||
return wxconn.wxCache
|
||
}
|
||
|
||
// GetWXReqInvoker 获取调用器
|
||
func (wxconn *WXConnect) GetWXReqInvoker() wxface.IWXReqInvoker {
|
||
return wxconn.wxReqInvoker
|
||
}
|
||
|
||
// GetWXTaskMgr 获取任务管理器
|
||
func (wxconn *WXConnect) GetWXTaskMgr() wxface.IWXTaskMgr {
|
||
return wxconn.wxTaskMgr
|
||
}
|
||
|
||
// GetWXSyncMgr 获取同步管理器
|
||
func (wxconn *WXConnect) GetWXSyncMgr() wxface.IWXSyncMgr {
|
||
return wxconn.wxSyncMgr
|
||
}
|
||
|
||
// GetWXFileHelperMgr 获取文件传输助手管理器
|
||
func (wxconn *WXConnect) GetWXFileHelperMgr() wxface.IWXFileHelperMgr {
|
||
return wxconn.wxFileHelperMgr
|
||
}
|
||
|
||
// GetWXFriendMsgMgr 好友消息管理器
|
||
func (wxconn *WXConnect) GetWXFriendMsgMgr() wxface.IWXUserMsgMgr {
|
||
return wxconn.wxUserMsgMgr
|
||
}
|
||
|
||
// 获取 HeartBeatTime
|
||
func (wxconn *WXConnect) GetHeartBeatTime() int64 {
|
||
return wxconn.HeartBeatTime
|
||
}
|
||
|
||
// 设置 HeartBeatTime
|
||
func (wxconn *WXConnect) SetHeartBeatTime(heartBeatTime int64) {
|
||
wxconn.HeartBeatTime = heartBeatTime
|
||
}
|
||
|
||
// IsConnected 判断是否长链接状态
|
||
func (wxconn *WXConnect) IsConnected() bool {
|
||
return atomic.LoadInt32(&wxconn.connected) == 1
|
||
}
|
||
|
||
// setConnected 安全地设置连接状态
|
||
func (wxconn *WXConnect) setConnected(connected bool) {
|
||
if connected {
|
||
atomic.StoreInt32(&wxconn.connected, 1)
|
||
} else {
|
||
atomic.StoreInt32(&wxconn.connected, 0)
|
||
}
|
||
}
|
||
|
||
// isRestarting 检查是否正在重启
|
||
func (wxconn *WXConnect) isRestarting() bool {
|
||
return atomic.LoadInt32(&wxconn.restarting) == 1
|
||
}
|
||
|
||
// setRestarting 设置重启状态
|
||
func (wxconn *WXConnect) setRestarting(restarting bool) {
|
||
if restarting {
|
||
atomic.StoreInt32(&wxconn.restarting, 1)
|
||
} else {
|
||
atomic.StoreInt32(&wxconn.restarting, 0)
|
||
}
|
||
}
|
||
|
||
// SendToWXMsgHandler 发送给消息队列去处理
|
||
func (wxconn *WXConnect) SendToWXMsgHandler(wxResp wxface.IWXResponse) {
|
||
wxconn.wxServer.GetWXMsgHandler().SendWXRespToTaskQueue(wxResp)
|
||
}
|
||
|
||
// SendToWXLongReqQueue 添加到长链接请求队列
|
||
func (wxconn *WXConnect) SendToWXLongReqQueue(wxLongReq wxface.IWXLongRequest) {
|
||
wxconn.longReqQueue <- wxLongReq
|
||
}
|
||
|
||
// SendHeartBeatWaitingSeconds 添加到微信心跳包队列
|
||
func (wxconn *WXConnect) SendHeartBeatWaitingSeconds(seconds uint32) {
|
||
wxconn.heartBeatTimer.Reset(time.Second * time.Duration(seconds))
|
||
}
|
||
|
||
// SendAutoAuthWaitingMinutes 添加到微信二次包包队列
|
||
func (wxconn *WXConnect) SendAutoAuthWaitingMinutes(minutes uint32) {
|
||
wxconn.autoAuthTimer.Reset(time.Minute * time.Duration(minutes))
|
||
}
|
||
|
||
// SendWebTask 发送web任务
|
||
func (wxconn *WXConnect) SendWebTask(status string, taskType uint32) {
|
||
currentAccount := wxconn.wxAccount
|
||
isServerRestart := currentAccount.GetUserInfo().GetIsServerRestart()
|
||
if isServerRestart {
|
||
return
|
||
}
|
||
connectInfo := wxconn.GetWXServer().GetWXConnectMgr().GetConnectInfo()
|
||
wxconn.webTaskMgr.AddWebTask(currentAccount.GetUserInfo(), currentAccount.GetTaskInfo(), status, taskType, connectInfo)
|
||
}
|
||
|
||
// AddGroupQrcodeData 添加群二维码
|
||
func (wxconn *WXConnect) AddGroupQrcodeData(fileData []byte, fileName string) {
|
||
wxconn.wxServer.GetWXFileMgr().AddGroupQrcodeData(fileData, fileName)
|
||
}
|
||
|
||
// DumpGroupQrcode 保存群二维码
|
||
func (wxconn *WXConnect) DumpGroupQrcode() {
|
||
taskMgr, _ := wxconn.wxTaskMgr.(*WXTaskMgr)
|
||
taskMgr.GetGroupTask().StartDownQrcode(10)
|
||
}
|
||
|
||
// CheckOnLineStatus 检查在线状态(获取二维码|唤醒)
|
||
func (wxconn *WXConnect) CheckOnLineStatusLogin() bool {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf("CheckOnLineStatus recovered from panic: %v\n", r)
|
||
// 可以在这里记录日志或执行其他紧急恢复处理措施
|
||
}
|
||
}()
|
||
state := wxconn.GetWXAccount().GetLoginState()
|
||
uuid := wxconn.GetWXAccount().GetUserInfo().UUID
|
||
if state == baseinfo.MMLoginStateNoLogin || state == baseinfo.MMLoginStateLogout {
|
||
return false
|
||
} else if state == baseinfo.MMLoginStateOnLine && wxconn.IsConnected() {
|
||
// 已经是长连接状态,无需发送短连接心跳包
|
||
return true
|
||
} else if state == baseinfo.MMLoginStateOffLine || //离线状态
|
||
(state == baseinfo.MMLoginStateOnLine && !wxconn.IsConnected()) { //数据库状态为在线但是没有连接服务器
|
||
|
||
// 先尝试恢复长连接
|
||
log.Printf("[%s],[%s] 尝试恢复长连接\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 尝试启动长连接
|
||
if !wxconn.IsConnected() {
|
||
err := wxconn.Start()
|
||
if err == nil {
|
||
// 长连接成功建立
|
||
wxconn.SendHeartBeatWaitingSeconds(1)
|
||
// 取一个 30-60 之间的随机数, 错开发送二次登录包
|
||
minutes := uint32(rand.Int31n(30)) + 30
|
||
wxconn.SendAutoAuthWaitingMinutes(minutes)
|
||
|
||
// 等待长连接稳定
|
||
time.Sleep(2 * time.Second)
|
||
|
||
if wxconn.IsConnected() {
|
||
log.Printf("[%s],[%s] 长连接恢复成功\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
if state == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
go func() {
|
||
// 判断重启服务器不执行下面代码
|
||
fmt.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "【"+wxconn.GetWXAccount().GetUserInfo().NickName+"】", "重新上线成功!")
|
||
// 获取账号的wxProfile
|
||
_ = wxconn.GetWXReqInvoker().SendGetProfileRequest()
|
||
// 判断是否初始化完成
|
||
initContact := db.QueryInitContactStatus(uuid)
|
||
if initContact == 0 {
|
||
_ = wxconn.GetWXReqInvoker().SendInitContactRequest(0)
|
||
}
|
||
wxconn.GetWXCache().Clear()
|
||
wxconn.GetWXCache().SetInitContactFinished(true)
|
||
wxconn.GetWXCache().SetInitFavSyncFinished(true)
|
||
}()
|
||
|
||
return true
|
||
}
|
||
// 长连接似乎启动后断开了,继续走短连接
|
||
log.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "启动长链接后连接丢失!")
|
||
} else {
|
||
log.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "启动长链接失败!", err.Error())
|
||
}
|
||
}
|
||
|
||
// 长连接恢复失败,尝试短连接心跳
|
||
log.Printf("[%s],[%s] 尝试使用短连接心跳\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 先发送心跳 如果心跳发送成功则等待10分钟后再调用二次 ,心跳发送失败直接调用二次确认在线状态
|
||
_, _, err := wxconn.GetWXReqInvoker().SendHeartBeatShortRequest()
|
||
if err != nil {
|
||
wxconn.HeartBeatTime = 0
|
||
err := wxconn.GetWXReqInvoker().SendAutoAuthRequest()
|
||
if err != nil ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateNoLogin ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateLogout {
|
||
return false
|
||
}
|
||
}
|
||
|
||
if state == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
// 如果此时已经建立了长连接,则重启长连接
|
||
if wxconn.IsConnected() {
|
||
wxconn.restartLong()
|
||
}
|
||
|
||
go func() {
|
||
// 判断重启服务器不执行下面代码
|
||
fmt.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "【"+wxconn.GetWXAccount().GetUserInfo().NickName+"】", "重新上线成功!")
|
||
// 获取账号的wxProfile
|
||
_ = wxconn.GetWXReqInvoker().SendGetProfileRequest()
|
||
// 判断是否初始化完成
|
||
initContact := db.QueryInitContactStatus(uuid)
|
||
if initContact == 0 {
|
||
_ = wxconn.GetWXReqInvoker().SendInitContactRequest(0)
|
||
}
|
||
wxconn.GetWXCache().Clear()
|
||
wxconn.GetWXCache().SetInitContactFinished(true)
|
||
wxconn.GetWXCache().SetInitFavSyncFinished(true)
|
||
}()
|
||
return true
|
||
} else {
|
||
//真正掉线
|
||
return false
|
||
}
|
||
}
|
||
|
||
// CheckOnLineStatus 检查在线状态
|
||
func (wxconn *WXConnect) CheckOnLineStatus() bool {
|
||
state := wxconn.GetWXAccount().GetLoginState()
|
||
uuid := wxconn.GetWXAccount().GetUserInfo().UUID
|
||
if state == baseinfo.MMLoginStateNoLogin || state == baseinfo.MMLoginStateLogout {
|
||
return false
|
||
} else if state == baseinfo.MMLoginStateOnLine && wxconn.IsConnected() {
|
||
return true
|
||
} else if state == baseinfo.MMLoginStateOffLine || //离线状态
|
||
(state == baseinfo.MMLoginStateOnLine && !wxconn.IsConnected()) { //数据库状态为在线但是没有连接服务器
|
||
|
||
// 先尝试恢复长连接
|
||
log.Printf("[%s],[%s] 尝试恢复长连接\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 尝试启动长连接
|
||
if !wxconn.IsConnected() {
|
||
err := wxconn.Start()
|
||
if err == nil {
|
||
// 长连接成功建立
|
||
wxconn.SendHeartBeatWaitingSeconds(1)
|
||
// 取一个 30-60 之间的随机数, 错开发送二次登录包
|
||
minutes := uint32(rand.Int31n(30)) + 30
|
||
wxconn.SendAutoAuthWaitingMinutes(minutes)
|
||
|
||
// 等待长连接稳定
|
||
time.Sleep(2 * time.Second)
|
||
|
||
if wxconn.IsConnected() {
|
||
log.Printf("[%s],[%s] 长连接恢复成功\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
if state == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
go func() {
|
||
// 获取账号的wxProfile
|
||
_ = wxconn.GetWXReqInvoker().SendGetProfileRequest()
|
||
wxconn.GetWXCache().Clear()
|
||
// 判断是否初始化完成
|
||
initContact := db.QueryInitContactStatus(uuid)
|
||
if initContact == 0 {
|
||
_ = wxconn.GetWXReqInvoker().SendInitContactRequest(0)
|
||
}
|
||
wxconn.GetWXCache().SetInitContactFinished(true)
|
||
wxconn.GetWXCache().SetInitFavSyncFinished(true)
|
||
}()
|
||
|
||
return true
|
||
}
|
||
// 长连接似乎启动后断开了,继续走短连接
|
||
log.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "启动长链接后连接丢失!")
|
||
} else {
|
||
log.Println(wxconn.GetWXAccount().GetUserInfo().UUID, "启动长链接失败!", err.Error())
|
||
}
|
||
}
|
||
|
||
// 长连接恢复失败,尝试短连接心跳
|
||
log.Printf("[%s],[%s] 尝试使用短连接心跳\n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(),
|
||
wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 先发送心跳 如果心跳发送成功则等待10分钟后再调用二次 ,心跳发送失败直接调用二次确认在线状态
|
||
_, _, err := wxconn.GetWXReqInvoker().SendHeartBeatShortRequest()
|
||
if err != nil {
|
||
wxconn.HeartBeatTime = 0
|
||
err := wxconn.GetWXReqInvoker().SendAutoAuthRequest()
|
||
if err != nil ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateNoLogin ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateLogout {
|
||
return false
|
||
}
|
||
|
||
}
|
||
|
||
if state == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
// 如果此时已经建立了长连接,则重启长连接
|
||
if wxconn.IsConnected() {
|
||
wxconn.restartLong()
|
||
} else {
|
||
// 最后尝试一次启动长连接
|
||
err := wxconn.Start()
|
||
if err == nil {
|
||
wxconn.SendHeartBeatWaitingSeconds(1)
|
||
minutes := uint32(rand.Int31n(30)) + 30
|
||
wxconn.SendAutoAuthWaitingMinutes(minutes)
|
||
}
|
||
}
|
||
|
||
go func() {
|
||
// 获取账号的wxProfile
|
||
_ = wxconn.GetWXReqInvoker().SendGetProfileRequest()
|
||
wxconn.GetWXCache().Clear()
|
||
// 判断是否初始化完成
|
||
initContact := db.QueryInitContactStatus(uuid)
|
||
if initContact == 0 {
|
||
_ = wxconn.GetWXReqInvoker().SendInitContactRequest(0)
|
||
}
|
||
wxconn.GetWXCache().SetInitContactFinished(true)
|
||
wxconn.GetWXCache().SetInitFavSyncFinished(true)
|
||
}()
|
||
|
||
return true
|
||
} else {
|
||
//真正掉线
|
||
return false
|
||
}
|
||
}
|
||
|
||
// 重新验证是否在线
|
||
func (wxconn *WXConnect) ReCheckOnLineStatus() bool {
|
||
// 先尝试恢复长连接
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 尝试恢复长连接 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 如果已经是连接状态,先重启一下
|
||
if wxconn.IsConnected() {
|
||
wxconn.restartLong()
|
||
time.Sleep(2 * time.Second)
|
||
if wxconn.IsConnected() {
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 长连接重启成功 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
return true
|
||
}
|
||
}
|
||
|
||
// 如果不是连接状态,尝试启动长连接
|
||
if !wxconn.IsConnected() {
|
||
err := wxconn.Start()
|
||
if err == nil {
|
||
time.Sleep(2 * time.Second)
|
||
if wxconn.IsConnected() {
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 长连接启动成功 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOffLine {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
// 长连接恢复失败,尝试短连接心跳
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 长连接恢复失败,尝试短连接心跳 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
|
||
// 先发送心跳 如果心跳发送成功则等待10分钟后再调用二次 ,心跳发送失败直接调用二次确认在线状态
|
||
_, _, err := wxconn.GetWXReqInvoker().SendHeartBeatShortRequest()
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 发送心跳 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
if err != nil {
|
||
wxconn.Stop()
|
||
wxconn.HeartBeatTime = 0
|
||
log.Printf("ReCheckOnLineStatus - [%s],[%s] 开始二次登录 - \n",
|
||
wxconn.GetWXAccount().GetUserInfo().GetUserName(), wxconn.GetWXAccount().GetUserInfo().NickName)
|
||
err := wxconn.GetWXReqInvoker().SendAutoAuthRequest()
|
||
|
||
if err != nil ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateNoLogin ||
|
||
wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateLogout {
|
||
wxconn.Stop()
|
||
return false
|
||
}
|
||
}
|
||
|
||
if wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOffLine ||
|
||
(wxconn.wxAccount.GetLoginState() == baseinfo.MMLoginStateOnLine) {
|
||
wxconn.GetWXAccount().SetLoginState(baseinfo.MMLoginStateOnLine)
|
||
db.UpdateLoginStatus(wxconn.GetWXAccount().GetUserInfo().GetUserName(), int32(wxconn.GetWXAccount().GetLoginState()), "重新上线成功!")
|
||
}
|
||
|
||
// 最后尝试一次恢复长连接
|
||
wxconn.restartLong()
|
||
return true
|
||
}
|