local config = require("config") local utils = require("utils") local util_notify = {} local sys = require("sys") -- 消息队列 local msg_queue = {} 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 local notify = { -- 发送到 dingtalk ["dingtalk"] = function(msg) local dingtalk_webhook = fskv.kv_get("dingtalk") if utils.is_empty(dingtalk_webhook) then log.error("util_notify", "未配置钉钉机器人webhook地址") return end local title = "" if utils.is_empty(msg.from) == false then title = "来自" .. msg.from .. "的短信" else title = "短信设备通知" end local text = "##### " .. msg.sms .. "\n" if utils.is_empty(msg.from) == false then text = text .. "- 发送号码:".. msg.from .."\n" end if utils.is_empty(msg.to) == false then text = text .. "- 设备号码:".. msg.to .."\n" end if utils.is_empty(msg.hostname) == false then text = text .. "- 设备名称:".. msg.hostname .."\n" end if utils.is_empty(msg.cs_index) == false then text = text .. "- 子设备ID:".. msg.cs_index .."\n" end if utils.is_empty(msg.from_time) == false then text = text .. "- 接收时间:".. msg.from_time .."\n" end if utils.is_empty(msg.oper) == false then text = text .. "- 运营商:".. msg.oper .."\n" end if utils.is_empty(msg.rsrp) == false then text = text .. "- 信号强度:".. msg.rsrp .."\n" end local header = { ["Content-Type"] = "application/json; charset=utf-8" } local body = { msgtype = "markdown", markdown = { title = title, text = text } } local json_data = json.encode(body) -- LuatOS Bug, json.encode 会将 \n 转换为 \b json_data = string.gsub(json_data, "\\b", "\\n") log.info("util_notify", "POST", dingtalk_webhook) return utils.fetch(nil, "POST", dingtalk_webhook, header, json_data) end, -- 发送到 wecom ["wecom"] = function(msg) local wecom_webhook = fskv.kv_get("wecom") if utils.is_empty(wecom_webhook) then log.error("util_notify", "未配置企业微信机器人地址") return end local header = { ["Content-Type"] = "application/json; charset=utf-8" } local text = msg.sms .. "\n" if utils.is_empty(msg.from) == false then text = text .. "发送号码:".. msg.from .."\n" end if utils.is_empty(msg.to) == false then text = text .. "设备号码:".. msg.to .."\n" end if utils.is_empty(msg.hostname) == false then text = text .. "设备名称:".. msg.hostname .."\n" end if utils.is_empty(msg.cs_index) == false then text = text .. "设备ID:".. msg.cs_index .."\n" end if utils.is_empty(msg.from_time) == false then text = text .. "接收时间:".. msg.from_time .."\n" end if utils.is_empty(msg.oper) == false then text = text .. "运营商:".. msg.oper .."\n" end if utils.is_empty(msg.rsrp) == false then text = text .. "信号强度:".. msg.rsrp .."\n" end local body = { msgtype = "text", text = { content = text } } local json_data = json.encode(body) -- LuatOS Bug, json.encode 会将 \n 转换为 \b json_data = string.gsub(json_data, "\\b", "\\n") log.info("util_notify", "POST", wecom_webhook) return utils.fetch(nil, "POST", wecom_webhook, header, json_data) end } --- 发送通知 -- @param msg 消息内容 -- @param channel 通知渠道 -- @return true: 无需重发, false: 需要重发 function util_notify.send(msg, channel) log.info("util_notify.send", "发送通知", channel) -- 判断消息内容 msg if type(msg) ~= "table" then log.error("util_notify.send", "发送通知失败", "参数类型错误", type(msg)) return true end if msg.sms == "" then log.error("util_notify.send", "发送通知失败", "消息内容为空") return true end -- 判断通知渠道 channel if channel and notify[channel] == nil then log.error("util_notify.send", "发送通知失败", "未知通知渠道", channel) return true end -- 发送通知 local code, headers, body = notify[channel](msg) if code == nil then log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body) return true end if code == -6 then -- 发生在 url 过长时, 重发也不会成功 log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body) return true end if code >= 200 and code < 500 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 通知渠道 function util_notify.add(msg, channels) log.info("util_notify.add", "添加到消息队列") if type(msg) ~= "table" then log.info("util_notify.add", "添加消息失败", "参数错误") 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, {channel = channel, msg = msg, retry = 0}) end sys.publish("ADD_NEW_MSG") log.debug("util_notify.add", "添加到消息队列, 当前队列长度:", #msg_queue) end -- 轮询消息队列 -- 发送成功则从消息队列中删除 -- 发送失败则等待下次轮询 local function poll() local item, result while true do -- 消息队列非空, 且网络已注册 if next(msg_queue) ~= nil then log.debug("util_notify.poll", "轮询消息队列中, 当前队列长度:", #msg_queue) item = msg_queue[1] table.remove(msg_queue, 1) if item.retry > (config.NOTIFY_RETRY_MAX or 100) then log.error("util_notify.poll", "超过最大重发次数", "消息内容:", json.encode(item.msg)) else result = util_notify.send(item.msg, item.channel) item.retry = item.retry + 1 if not result then -- 发送失败, 移到队尾 table.insert(msg_queue, item) sys.wait(5000) end end sys.wait(50) else sys.waitUntil("ADD_NEW_MSG", 1000 * 10) end end end sys.taskInit(poll) return util_notify