local skynet = require "skynet" local oo = require "Class" local gameCmd = require "GameCmd" local json =require "json" local log = require "Log" local sqlUrl = require "SqlUrl" local errorInfo = require "ErrorInfo" local dataType = require "DataType" local serverId = tonumber(skynet.getenv "serverId") local redisKeyUrl = require "RedisKeyUrl" local httpc = require "http.httpc" local Subscription = oo.class() local protocol ="https" --域名 local domain = "api.weixin.qq.com" --推送地址 暂时写在这里 local pushUrl = "/cgi-bin/message/subscribe/send?access_token=%s" --获取token地址 --local getTokenUrl = "/cgi-bin/token?grant_type=client_credential&appid=wxa2951c2cf9636811&secret=bada9eefee3db35b9c9fa21ed40787d8" local getTokenStableUrl="/cgi-bin/stable_token"--稳定模式 local tokenStablePostData={ grant_type="client_credential",--固定值 appid="wxa2951c2cf9636811",--appid secret="bada9eefee3db35b9c9fa21ed40787d8"--appsecret } --方便管理订阅一个管理列表 local subscriptionMap={} -- 版本更新提醒 local data1={ character_string1={ value="1.0.0"--版本号 }, thing2={ value="版本描述" }, date3={ value="2024/01/01"--完成时间 }, time4={ value="10:00:00"--更新时间 }, } subscriptionMap[8]=data1 -- 邮箱金币 local data2={ date2={ value = os.date("%H:%M",os.time()) }, thing1={ value="待领取" }, } subscriptionMap[1]=data2 -- 种花成熟提醒 local data3={ phrase1={ value="任务名称" }, date2={ value="完成日期" }, time3={ value="完成时间" }, thing4={ value="温馨提醒" }, } subscriptionMap[2]=data3 -- 食堂进货提醒 local data4={ phrase1={ value="任务名称" }, date2={ value="完成日期" }, } subscriptionMap[3]=data4 -- 萌宠乐园出游提醒 local data5={ phrase1={ value="任务名称" }, time3={ value="完成时间" }, thing4={ value="温馨提醒" }, } subscriptionMap[4]=data5 -- 渔店钓鱼提醒 local data6={ phrase1={ value="任务名称" }, date2={ value="完成日期" }, time3={ value="完成时间" }, thing4={ value="温馨提醒" }, } subscriptionMap[5]=data6 -- 恋家置业收租提醒 local data7={ phrase1={ value="任务名称" }, time3={ value="完成时间" }, thing4={ value="温馨提醒" }, } subscriptionMap[6]=data7 -- 造型材料提醒 local data8={ phrase1={ value="任务名称" }, date2={ value="完成日期" }, thing11={ value="未领取" }, } subscriptionMap[7]=data8 --初始化 function Subscription:Init() end --每5秒调一次(测试阶段) function Subscription:On5SecTimer() -- log.debug("微信订阅 每5秒调一次") -- --目前只有微信订阅消息 -- self:GetSubscription(1) end --获取订阅信息 function Subscription:GetSubscription(subscribeType) --获取已经订阅的玩家key local keys= skynet.server.redis:smembers(redisKeyUrl.ThirdSubscribeSetKeys) if keys == nil or #keys == 0 then log.debug("GetSubscription keys is nil or #keys == 0") return end --订阅类型配置 local cfgSubscriptions = skynet.server.gameConfig:GetAllCfg("Subscription") if not cfgSubscriptions then log.debug("cfgSubscriptions is nil") return end --循环数据 for _, setKey in ipairs(keys) do local key=string.format(redisKeyUrl.ThirdSubscribe, setKey) --获取订阅信息 local subscribeInfoListStr = skynet.server.redis:hget(key,tostring(subscribeType)) --不存在数据跳出当前循环 if subscribeInfoListStr == nil or subscribeInfoListStr == "" then goto coroutine1 end --反序列化成对象 local subscribeInfoList = json:decode(subscribeInfoListStr) --判断反序列化是否成功 if subscribeInfoList == nil or next(subscribeInfoList) == nil then log.debug(string.format("消息发布订阅反序列化失败,key:%s,subscribeInfoListStr:%s",key,subscribeInfoListStr)) goto coroutine1 end --倒序遍历,删除不影响table后续索引 for i = #subscribeInfoList, 1, -1 do local subscribeInfo = subscribeInfoList[i] --不存在存在数据 if subscribeInfo == nil or next(subscribeInfo) == nil then goto coroutine end --判断是否过期 if subscribeInfo.expireTime < skynet.GetTime() and subscribeInfo.expireTime ~= 0 then --删除订阅信息 table.remove(subscribeInfoList,i) goto coroutine end --是否需要推送 if subscribeInfo.triggerTimestamp > skynet.GetTime() or subscribeInfo.triggerTimestamp == 0 --当前在线,没有推送时间 or subscribeInfo.subscribeStatus == 2 --已推送 or subscribeInfo.subscribeStatus == 3 then goto coroutine end local cfgSubscription={} --根据配置获取提醒标题 for _, value in pairs(cfgSubscriptions) do if value.id == subscribeInfo.subscribeId then cfgSubscription=value end end --推送消息到微信 local ret = Subscription:pushMessage(subscribeType,cfgSubscription,subscribeInfo) if ret and cfgSubscription.subscribeType == 1 and skynet.GetTime() > subscribeInfo.expireTime then--触发订阅,并且未过期 才重置 --更新状态为已推送 subscribeInfo.subscribeStatus = 2 else--手动订阅则删除 table.remove(subscribeInfoList,i) end -- 跳出当前循环 ::coroutine:: end if next(subscribeInfoList) ~= nil then --更新订阅信息 skynet.server.redis:hset(key,tostring(subscribeType),json:encode(subscribeInfoList)) else --删除 skynet.server.redis:hdel(key,tostring(subscribeType)) --删除索引 skynet.server.redis:srem(redisKeyUrl.ThirdSubscribeSetKeys,setKey) end -- 跳出当前循环 ::coroutine1:: end end --每5分钟调一次 function Subscription:On5MinTimer() log.debug("微信订阅 五分钟检查一次") self:GetSubscription(1) end --推送消息 function Subscription:pushMessage(subscribeType,cfgSubscription,subscribeInfo) --失败次数 local failCount=0 --处理微信订阅消息(目前只有微信) if subscribeType ==1 then --订阅内容 local postData={ touser = subscribeInfo.uniqueId, template_id = "", miniprogram_state = "developer", lang = "zh_CN", data={} } local template_id="" local data={} --获取token地址 local tokenInfo = self:GetWxToken(false) if tokenInfo == "" then log.debug("获取token失败") return false end --模版id postData.template_id=cfgSubscription.subscribeModelId --设置订阅内容 if subscriptionMap[cfgSubscription.id] then postData.data=subscriptionMap[cfgSubscription.id] --版本更新 if postData.data.character_string1 then --修改推送数值值 postData.data.character_string1.value="1.0.0" --版本号 end --邮件时间 if cfgSubscription.id == 1 then postData.data.date2.value= os.date("%H:%M",skynet.GetTime()) postData.data.thing1.value= cfgSubscription.subscribeDesc end --基本通用模板 if postData.data.phrase1 then postData.data.phrase1.value= cfgSubscription.subscribeTitle end if postData.data.thing4 then postData.data.thing4.value= cfgSubscription.subscribeDesc end if postData.data.date2 then postData.data.date2.value= os.date("%m月%d日",subscribeInfo.triggerTimestamp) end if postData.data.time3 then postData.data.time3.value= os.date("%H:%M:%S",subscribeInfo.triggerTimestamp) end else--模版id不存在 log.debug(string.format("消息订阅 消息id不存在,subscribeId:%s",cfgSubscription.id)) return false end -- --版本更新提醒 -- if cfgSubscription.id==8 then -- postData.data=data1 -- --修改推送数值值 -- postData.data.character_string1.value="1.0.0" --版本号 -- --邮箱金币 -- elseif cfgSubscription.id==1 then -- postData.data=data2 -- postData.data.date2.value= os.date("%H:%M",skynet.GetTime()) -- postData.data.thing1.value= cfgSubscription.subscribeDesc -- -- "种花成熟提醒 -- -- 渔店钓鱼提醒 -- -- 食堂进货提醒 -- -- 萌宠乐园出游提醒 -- -- 恋家置业收租提醒 -- -- 食堂收菜提醒 -- -- 造型材料提醒" -- elseif cfgSubscription.id>=2 and cfgSubscription.id<=7 then -- postData.data=data3 -- postData.data.phrase1.value= cfgSubscription.subscribeTitle -- postData.data.thing4.value= cfgSubscription.subscribeDesc -- postData.data.date2.value= os.date("%m月%d日",subscribeInfo.triggerTimestamp) -- postData.data.time3.value= os.date("%H:%M:%S",subscribeInfo.triggerTimestamp) -- else--模版id不存在 -- log.debug(string.format("消息订阅 消息id不存在,subscribeId:%s",cfgSubscription.id)) -- return false -- end ::top:: local url =string.format(pushUrl,tokenInfo) --推送 local status,body = skynet.server.httpClient:PostJson(domain,url,json:encode(postData),protocol) if type(status)=="table" then return false end if status ~= 200 then log.warning(string.format("微信订阅推送 返回状态 ~= 200 url:%s,status:%d,body:%s",url,status,body)) return false end --反序列化结果 local bodyObj = json:decode(body) local errcode = tonumber(bodyObj.errcode) --推送失败,就意味着后面都会失败,没必要再次推送 if errcode ~= 0 then if (errcode == 40001 or errcode == 42001)and failCount < 5 then --token过期,重试5次 log.debug("重新获取token:",errcode,failCount) --获取token地址 tokenInfo = self:GetWxToken(true) if tokenInfo == "" then log.debug("获取token失败") return false end failCount=failCount+1 goto top end return true end return true end end --微信token信息 local token="" local expires_in=0 --推送消息到微信等 function Subscription:GetWxToken(isForce) --判断token是否过期,未过期直接返回之前的token if expires_in > 0 and expires_in > skynet.GetTime() and token ~= "" and not isForce then return token end --获取token-stable模式 local status,body =skynet.server.httpClient:PostJson(domain,getTokenStableUrl,json:encode(tokenStablePostData),protocol) -- skynet.server.httpClient:Get(domain,getTokenUrl,protocol) if status ~= 200 then log.warning(string.format("微信订阅推送 获取token失败,domain:%s,url:%s,status:%d,body:%s",domain,getTokenStableUrl,status,body)) return 0 end --反序列号结果 local bodyObj = json:decode(body) --反序列化失败 if bodyObj == nil or next(bodyObj) == nil then log.warning(string.format("微信订阅推送 bodyObj反序列化失败,domain:%s,url:%s,status:%d,body:%s",domain,getTokenStableUrl,status,body)) return 0 end token = bodyObj.access_token expires_in =skynet.GetTime() + tonumber(bodyObj.expires_in) if token == nil or expires_in == nil then log.warning(string.format("微信订阅推送 token反序列化失败,domain:%s,url:%s,status:%d,body:%s",domain,getTokenStableUrl,status,body)) return 0 end return token end skynet.server.subscription = Subscription return Subscription