local util_notify_channel = require "util_notify_channel" local util_notify = {} -- 消息队列 local msg_queue = {} -- 发送计数 local msg_count = 0 local error_count = 0 local function urlencodeTab(params) local msg = {} for k, v in pairs(params) do table.insert(msg, string.urlEncode(k) .. "=" .. string.urlEncode(v)) table.insert(msg, "&") end table.remove(msg) return table.concat(msg) end --- 发送通知 -- @param msg 消息内容 -- @param channel 通知渠道 -- @return true: 无需重发, false: 需要重发 local function send(msg, channel) log.info("util_notify.send", "发送通知", channel) -- 判断消息内容 msg if type(msg) ~= "string" or msg == "" then log.error("util_notify.send", "发送通知失败", "msg 参数错误", type(msg)) return true end -- 判断通知渠道 channel if channel and util_notify_channel[channel] == nil then log.error("util_notify.send", "发送通知失败", "未知通知渠道", channel) return true end -- 发送通知 local code, headers, body = util_notify_channel[channel](msg) if code == nil then log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body) return true end if code >= 200 and code < 500 and code ~= 408 and code ~= 409 and code ~= 425 and code ~= 429 then log.info("util_notify.send", "发送通知成功", "code:", code, "body:", body) return true end log.error("util_notify.send", "发送通知失败, 等待重发", "code:", code, "body:", body) return false end --- 添加到消息队列 -- @param msg 消息内容 -- @param channels 通知渠道 -- @param id 消息唯一标识 function util_notify.add(msg, channels, id) msg_count = msg_count + 1 if id == nil or id == "" then id = "msg-t" .. os.time() .. "c" .. msg_count .. "r" .. math.random(9999) end if type(msg) == "table" then msg = table.concat(msg, "\n") end channels = channels or config.NOTIFY_TYPE if type(channels) ~= "table" then channels = { channels } end for _, channel in ipairs(channels) do table.insert(msg_queue, { id = id, channel = channel, msg = msg, retry = 0 }) end sys.publish("NEW_MSG") log.debug("util_notify.add", "添加到消息队列, 当前队列长度:", #msg_queue, "消息内容:", msg:gsub("\r", "\\r"):gsub("\n", "\\n")) end --- 轮询消息队列, 发送成功则从队列中删除, 发送失败则等待下次 local function poll() -- 打印网络状态 if mobile.status() ~= 1 then log.warn("util_notify.poll", "mobile.status", mobile_status, util_mobile.status()) end -- 消息队列非空 if next(msg_queue) == nil then sys.waitUntil("NEW_MSG", 1000 * 10) return end local item = msg_queue[1] table.remove(msg_queue, 1) local msg = item.msg log.info("util_notify.poll", "轮询消息队列中", "总长度: " .. #msg_queue, "当前ID: " .. item.id, "当前重发次数: " .. item.retry, "连续失败次数: " .. error_count) -- 通知内容添加设备信息 if config.NOTIFY_APPEND_MORE_INFO and not string.find(msg, "开机时长:") then msg = msg .. util_mobile.appendDeviceInfo() end -- 通知内容添加重发次数 if error_count > 0 then msg = msg .. "\n重发次数: " .. error_count end -- 超过最大重发次数 if item.retry > (config.NOTIFY_RETRY_MAX or 20) then log.warn("util_notify.poll", "超过最大重发次数, 放弃重发", item.msg) return end -- 开始发送 local result = send(msg, item.channel) -- 发送成功 if result then error_count = 0 -- 检查 fskv 中如果存在则删除 if fskv.get(item.id) then fskv.del(item.id) end return end -- 发送失败 error_count = error_count + 1 item.retry = item.retry + 1 table.insert(msg_queue, item) log.info("util_notify.poll", "等待下次重发", "当前重发次数", item.retry, "连续失败次数", error_count) sys.waitUntil("IP_READY", 1000 * 5) -- 每连续失败 2 次, 开关飞行模式 if error_count % 2 == 0 then -- 开关飞行模式 log.warn("util_notify.poll", "连续失败次数过多, 开关飞行模式") mobile.flymode(0, true) mobile.flymode(0, false) sys.wait(1000) end -- 每条消息第 1 次重发失败后, 保存到 fskv, 断电开机可恢复重发 if item.retry == 1 then if not (string.find(item.msg, "#SMS") or string.find(item.msg, "#CALL")) then return end log.info("util_notify.poll", "当前第 1 次重发失败, 保存到 fskv", item.id) if fskv.get(item.id) then log.info("util_notify.poll", "fskv 已存在, 跳过写入", item.id) return end local kv_set_result = fskv.set(item.id, item.msg) log.info("util_notify.poll", "fskv.set", kv_set_result, "used,total,count:", fskv.status()) end end sys.taskInit(function() while true do poll() sys.wait(100) end end) sys.taskInit(function() sys.waitUntil("IP_READY") sys.wait(10000) local iter = fskv.iter() while iter do local k = fskv.next(iter) if not k then break end local v = fskv.get(k) if not (v and v ~= "" and string.find(k, "msg-")) then break end log.info("util_notify", "检查到 fskv 中有历史消息", k, v:gsub("\r", "\\r"):gsub("\n", "\\n")) util_notify.add(v, nil, k) end end) return util_notify