update 2023-12-24 15:45:08

This commit is contained in:
github-actions[bot] 2023-12-24 15:45:08 +08:00
parent d38103b2a6
commit 8fa7b613af
25 changed files with 2588 additions and 0 deletions

View File

@ -0,0 +1,23 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-serverchan
PKG_VERSION:=2.06.2
PKG_RELEASE:=10
PKG_MAINTAINER:=tty228 <tty228@yeah.net>
LUCI_TITLE:=LuCI support for serverchan
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+iputils-arping +curl +jq
define Package/$(PKG_NAME)/conffiles
/etc/config/serverchan
/usr/share/serverchan/api/diy.json
/usr/share/serverchan/api/logo.jpg
/usr/share/serverchan/api/ipv4.list
/usr/share/serverchan/api/ipv6.list
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,45 @@
# 简介
- 用于 OpenWRT 路由器上进行 微信/Telegram 推送的插件
- 支持列表:
- 微信推送/Server酱 https://sct.ftqq.com/
- 企业微信/应用推送 https://work.weixin.qq.com/api/doc/90000/90135/90248
- 微信推送/WxPusher https://wxpusher.zjiecode.com/docs
- 微信推送/推送加 http://www.pushplus.plus/
- Telegram/BotFather https://t.me/BotFather
- 精力有限如需要钉钉推送、飞书推送、Bark推送等请尝试 https://github.com/zzsj0928/luci-app-pushbot
- 依赖 iputils-arping + curl + jq 命令,安装前请 `opkg update`,小内存路由谨慎安装
#### 主要功能
- 路由 IP、IPv6 变动推送
- 设备 上线、离线 推送
- 设备在线列表及流量使用情况
- CPU 负载、温度监视、PVE 宿主机温度监控
- 路由运行状态定时推送
- 路由 Web、SSH 登录提示,自动拉黑、端口敲门
- 无人值守任务
#### 已知问题
- 基于 X86 OpenWrt v19.07.10 制作,不同系统不同设备,可能会遇到各种问题,**如获取到错误的温度信息、页面显示错误、报错等,自行适配**
- 部分设备无法读取到设备名,脚本使用 `cat /tmp/dhcp.leases` 命令读取设备名,**如果 DHCP 中不存在设备名,则无法读取设备名**如二级路由设备、静态IP设备、OpenWrt 作为旁路网关等情况),请使用设备名备注,或在高级设置处设置从光猫获取
- 使用主动探测设备连接的方式检测设备在线状态,以避免 Wi-Fi 休眠机制,主动探测较为耗时,**如遇设备休眠频繁,请自行调整超时设置**
- 流量统计功能依赖 wrtbwmon ,自行选装或编译,**该插件与 Routing/NAT 、Flow Offloading 冲突,开启无法获取流量,自行选择**
#### PS
- 新功能看情况开发,忙得头晕眼花
- 欢迎各种代码提交
- 审美无能,推送样式将就用吧
- 提交bug时请尽量带上设备信息日志与描述如执行 /usr/share/serverchan/serverchan 后的提示、日志信息、/tmp/serverchan/ 目录下的文件信息,**并附上 sh -x /usr/share/serverchan/serverchan t1 的详细运行信息**
- 三言两句恕我无能为力
#### Download
- [luci-app-serverchan](https://github.com/tty228/luci-app-serverchan/releases)
- [wrtbwmon](https://github.com/brvphoenix/wrtbwmon)
- [luci-app-wrtbwmon](https://github.com/brvphoenix/luci-app-wrtbwmon)
- **L大版本直接编译 luci-app-wrtbwmon ,非原版 LuCI 如使用以上 wrtbwmon请注意安装版本号**
#### Donate
如果你觉得此项目对你有帮助,请捐助我们,以使项目能持续发展,更加完善。
![image](https://github.com/tty228/Python-100-Days/blob/master/res/WX.jpg)

View File

@ -0,0 +1,34 @@
module("luci.controller.serverchan", package.seeall)
function index()
if not nixio.fs.access("/etc/config/serverchan") then
return
end
local page = entry({"admin", "services", "serverchan"}, alias("admin", "services", "serverchan", "setting"), _("微信推送"), 30)
page.dependent = true
page.acl_depends = { "luci-app-serverchan" }
entry({"admin", "services", "serverchan", "setting"}, cbi("serverchan/setting"), _("配置"), 40).leaf = true
entry({"admin", "services", "serverchan", "advanced"}, cbi("serverchan/advanced"), _("高级设置"), 50).leaf = true
entry({"admin", "services", "serverchan", "client"}, form("serverchan/client"), _("在线设备"), 80)
entry({"admin", "services", "serverchan", "log"}, form("serverchan/log"), _("日志"), 99).leaf = true
entry({"admin", "services", "serverchan", "get_log"}, call("get_log")).leaf = true
entry({"admin", "services", "serverchan", "clear_log"}, call("clear_log")).leaf = true
entry({"admin", "services", "serverchan", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.running = luci.sys.call("busybox ps|grep -v grep|grep -c serverchan >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function get_log()
luci.http.write(luci.sys.exec("[ -f '/tmp/serverchan/serverchan.log' ] && cat /tmp/serverchan/serverchan.log"))
end
function clear_log()
luci.sys.call("echo '' > /tmp/serverchan/serverchan.log")
end

View File

@ -0,0 +1,168 @@
local nt = require "luci.sys".net
local fs = require "nixio.fs"
m = Map("serverchan", translate("提示:"))
m.description = translate("如果你不了解这些选项的含义,请不要修改这些选项")
s = m:section(TypedSection, "serverchan", "高级设置")
s.anonymous = true
s.addremove = false
a = s:option(Value, "up_timeout", translate('设备上线检测超时s'))
a.default = "2"
a.optional = false
a.datatype = "uinteger"
a = s:option(Value, "down_timeout", translate('设备离线检测超时s'))
a.default = "20"
a.optional = false
a.datatype = "uinteger"
a = s:option(Value, "timeout_retry_count", translate('离线检测次数'))
a.default = "2"
a.optional = false
a.datatype = "uinteger"
a.description = translate("若无二级路由设备,信号强度良好,可以减少以上数值<br/>因夜间 wifi 休眠较为玄学,遇到设备频繁推送断开,烦请自行调整参数<br/>..╮(╯_╰╭..")
a = s:option(Value, "thread_num", translate('最大并发进程数'))
a.default = "3"
a.datatype = "uinteger"
a.description = translate("低性能设备请勿更改设置值,或酌情减少参数")
a = s:option(Value, "soc_code", "自定义温度读取命令")
a.rmempty = true
a:value("", translate("默认"))
a:value("pve", translate("PVE 虚拟机"))
a.description = translate("自定义命令如需使用特殊符号,如引号、$、!等,则需要自行转义,并在保存后查看 /etc/config/serverchan 文件 soc_code 设置项是否保存正确<br/>可以使用 eval `echo $(uci get serverchan.serverchan.soc_code)` 命令查看命令输出及错误信息<br/>执行结果需为纯数字(可带小数),用于温度对比")
a = s:option(Value, "server_host", translate("宿主机地址"))
a.rmempty = true
a.default = "10.0.0.2"
a.description = translate("")
a:depends({soc_code = "pve"})
a = s:option(Value, "server_port", translate("宿主机 SSH 端口"))
a.rmempty = true
a.default = "22"
a.description = translate("SSH 端口默认为 22如有自定义请填写自定义 SSH 端口<br/>请确认已经设置好密钥登陆,否则会引起脚本无法运行等错误!<br/>PVE 安装 sensors 命令自行百度<br/>密钥登陆例(自行修改地址与端口号):<br/>opkg update #更新列表<br/>opkg install openssh-client openssh-keygen #安装openssh客户端<br/>echo -e \"\\n\" | ssh-keygen -t rsa # 生成密钥文件(空密码)<br/>pve_host=`uci get serverchan.serverchan.server_host` || pve_host=\"10.0.0.3\" # 读取配置文件中的 pve 主机地址,如果不存在请自行填写 <br/>pve_port=`uci get serverchan.serverchan.server_port` || pve_host=\"22\" # 读取配置文件中的 pve 主机 ssh 端口号,,如果不存在请自行填写 <br/>ssh root@${pve_host} -p ${pve_port} \"tee -a ~/.ssh/id_rsa.pub\" < ~/.ssh/id_rsa.pub # 传送公钥到 PVE<br/>ssh root@${pve_host} -p ${pve_port} \"cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys\" # 写入公钥到 PVE<br/>ssh -i /root/.ssh/id_rsa root@${pve_host} -p ${pve_port} sensors # 使用私钥连接 PVE 测试温度命令<br/>刷机党自行将 /root/.ssh/ 加入备份列表,避免重复操作")
a:depends({soc_code = "pve"})
a = s:option(Button, "soc", translate("测试温度命令"))
a.inputtitle = translate("输出信息")
a.write = function()
luci.sys.call("/usr/share/serverchan/serverchan soc")
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "serverchan", "advanced"))
end
if nixio.fs.access("/tmp/serverchan/soc_tmp") then
e = s:option(TextValue,"soc_tmp")
e.rows = 2
e.readonly = true
e.cfgvalue = function()
return luci.sys.exec("cat /tmp/serverchan/soc_tmp && rm -f /tmp/serverchan/soc_tmp")
end
end
a = s:option(Flag, "gateway_info_enable", translate("从光猫获取主机名等信息"))
a.default = 0
a.rmempty = true
a.description = translate("适用于 OpenWrt 作为旁路网关,无法获取设备主机名及完整的局域网设备列表时<br/>仅测试通过 HG5143F/HN8145V 天翼网关,不保证通用性")
a = s:option(Value, "gateway_host_url", translate('光猫登录地址 URL'))
a.rmempty = true
a.default = "http://192.168.1.1/cgi-bin/luci"
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_info_url", translate('设备列表 JSON URL'))
a.rmempty = true
a.default = "http://192.168.1.1/cgi-bin/luci/admin/allInfo"
a.description = translate('使用 F12 控制台自行抓取<br/>ip、devName、model 为必须项JSON 文件信息范例:<br/>{"pc1":{"brand":"","devName":"RouterOS","onlineTime":45,"model":"","upSpeed":229376,"downSpeed":7707033,"type":"pc","ip":"192.168.1.7"}}')
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_logout_url", translate('光猫注销登录 URL'))
a.rmempty = true
a.default = "http://192.168.1.1/cgi-bin/luci/admin/logout"
a.description = translate("非必须项,但可能会影响其他用户登录 Web 管理页面,如 HG5143F")
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_username_id", translate('登录页面帐号输入框 ID'))
a.rmempty = true
a.default = "username"
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_password_id", translate('登录页面密码输入框 ID'))
a.rmempty = true
a.default = "psd"
a.description = translate("浏览器右键-检查元素")
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_username", translate('光猫登录帐号'))
a.rmempty = true
a.default = "useradmin"
a:depends({gateway_info_enable = "1"})
a = s:option(Value, "gateway_password", translate('光猫登录密码'))
a.rmempty = true
a.description = translate("使用普通账号即可,不需要超密")
a:depends({gateway_info_enable = "1"})
a = s:option(Flag, "err_enable", translate("无人值守任务"))
a.default = 0
a.rmempty = true
a.description = translate("请确认脚本可以正常运行,否则可能造成频繁重启等错误!")
a = s:option(Flag, "err_sheep_enable", translate("仅在免打扰时段重拨"))
a.default = 0
a.rmempty = true
a.description = translate("避免白天重拨 DDNS 域名等待解析,此功能不影响断网检测<br/>因夜间跑流量问题,该功能可能不稳定")
a:depends({err_enable = "1"})
a = s:option(DynamicList, "err_device_aliases", translate("关注列表"))
a.rmempty = true
a.description = translate("只会在列表中设备都不在线时才会执行<br/>免打扰时段一小时后关注设备五分钟低流量约100kb/m将视为离线")
nt.mac_hints(function(mac, name) a :value(mac, "%s (%s)" %{ mac, name }) end)
a:depends({err_enable = "1"})
a = s:option(ListValue, "network_err_event", translate("网络断开时"))
a.default = ""
a:depends({err_enable = "1"})
a:value("", translate("无操作"))
a:value("1", translate("重启路由器"))
a:value("2", translate("重新拨号"))
a:value("3", translate("修改相关设置项,尝试自动修复网络"))
a.description = translate("选项 1 选项 2 不会修改设置,并最多尝试 2 次。<br/>选项 3 会将设置项备份于 /usr/share/serverchan/configbak 目录,并在失败后还原。<br/>【!!无法保证兼容性!!】不熟悉系统设置项,不会救砖请勿使用")
a = s:option(ListValue, "system_time_event", translate("定时重启"))
a.default = ""
a:depends({err_enable = "1"})
a:value("", translate("无操作"))
a:value("1", translate("重启路由器"))
a:value("2", translate("重新拨号"))
a = s:option(Value, "autoreboot_time", "系统运行时间大于")
a.rmempty = true
a.default = "24"
a.datatype = "uinteger"
a:depends({system_time_event = "1"})
a.description = translate("单位为小时")
a = s:option(Value, "network_restart_time", "网络在线时间大于")
a.rmempty = true
a.default = "24"
a.datatype = "uinteger"
a:depends({system_time_event = "2"})
a.description = translate("单位为小时")
a = s:option(Flag, "public_ip_event", translate("重拨尝试获取公网 IP"))
a.default = 0
a.rmempty = true
a:depends({err_enable = "1"})
a.description = translate("重拨时不会推送 IP 变动通知,并会导致你的域名无法及时更新 IP 地址<br/>请确认你可以通过重拨获取公网 IP否则这不仅徒劳无功还会引起频繁断网<br/>移动等大内网你就别挣扎了!!")
a = s:option(Value, "public_ip_retry_count", "当天最大重试次数")
a.rmempty = true
a.default = "10"
a.datatype = "uinteger"
a:depends({public_ip_event = "1"})
return m

View File

@ -0,0 +1,34 @@
f = SimpleForm("serverchan")
f.reset = false
f.submit = false
local o = require "luci.dispatcher"
local fs = require "nixio.fs"
local jsonc = require "luci.jsonc"
local sys = require "luci.sys"
local sessions = {}
local session_path = "/var/serverchan/client"
if fs.access(session_path) then
for filename in fs.dir(session_path) do
local session_file = session_path .. "/" .. filename
local file = io.open(session_file, "r")
local t = jsonc.parse(file:read("*a"))
if t then
t.session_file = session_file
sessions[#sessions + 1] = t
end
file:close()
end
end
local client_count = sys.exec("cat /tmp/serverchan/ipAddress | wc -l")
t = f:section(Table, sessions, translate("当前共 ".. client_count .. "台设备在线"))
t:option(DummyValue, "name", translate("主机名"))
t:option(DummyValue, "mac", translate("MAC"))
t:option(DummyValue, "ip", translate("IP"))
t:option(DummyValue, "usage", translate("总计流量"))
t:option(DummyValue, "uptime", translate("在线时间"))
return f

View File

@ -0,0 +1,6 @@
f = SimpleForm("serverchan")
f.reset = false
f.submit = false
f:append(Template("serverchan/serverchan_log"))
return f

View File

@ -0,0 +1,536 @@
local nt = require "luci.sys".net
local fs = require "nixio.fs"
local e = luci.model.uci.cursor()
local net = require "luci.model.network".init()
local sys = require "luci.sys"
local ifaces = sys.net:devices()
m = Map("serverchan", translate("ServerChan"),
translate("「Server酱」英文名「ServerChan」是一款从服务器推送报警信息和日志到微信的工具。<br /><br />如果你在使用中遇到问题,请到这里提交:")
.. [[<a href="https://github.com/tty228/luci-app-serverchan" target="_blank">]]
.. translate("GitHub 项目地址")
.. [[</a>]]
)
m:section(SimpleSection).template = "serverchan/serverchan_status"
s = m:section(NamedSection, "serverchan", "serverchan", translate(""))
s:tab("basic", translate("基本设置"))
s:tab("content", translate("推送内容"))
s:tab("ipset", translate("自动封禁"))
s:tab("crontab", translate("定时推送"))
s:tab("disturb", translate("免打扰"))
s.addremove = false
s.anonymous = true
-- 基本设置
a = s:taboption("basic", Flag, "serverchan_enable", translate("启用"))
a.rmempty = true
a = s:taboption("basic", MultiValue, "lite_enable", translate("精简模式"))
a:value("device", translate("精简当前设备列表"))
a:value("nowtime", translate("精简当前时间"))
a:value("content", translate("只推送标题"))
a.widget = "checkbox"
a.default = nil
a.optional = true
a = s:taboption("basic", ListValue, "jsonpath", translate("推送模式"))
a.default = "/usr/share/serverchan/api/serverchan.json"
a.rmempty = true
a:value("/usr/share/serverchan/api/serverchan.json", translate("微信 Server酱"))
a:value("/usr/share/serverchan/api/qywx_mpnews.json", translate("企业微信 图文消息"))
a:value("/usr/share/serverchan/api/qywx_markdown.json", translate("企业微信 markdown版不支持公众号"))
a:value("/usr/share/serverchan/api/wxpusher.json", translate("微信 wxpusher"))
a:value("/usr/share/serverchan/api/pushplus.json", translate("微信 pushplus"))
a:value("/usr/share/serverchan/api/telegram.json", translate("Telegram"))
a:value("/usr/share/serverchan/api/diy.json", translate("自定义推送"))
a = s:taboption("basic", Value, "sckey", translate('微信推送/新旧共用'), translate("").."Server酱 sendkey <a href='https://sct.ftqq.com/' target='_blank'>点击这里</a><br>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/serverchan.json")
a = s:taboption("basic", Value, "corpid", translate('企业ID(corpid)'), translate("").."获取说明 <a href='https://work.weixin.qq.com/api/doc/10013' target='_blank'>点击这里</a>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_mpnews.json")
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_markdown.json")
a = s:taboption("basic", Value, "userid", translate('帐号(userid)'))
a.rmempty = true
a.description = translate("群发到应用请填入 @all ")
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_mpnews.json")
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_markdown.json")
a = s:taboption("basic", Value, "agentid", translate('应用id(agentid)'))
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_mpnews.json")
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_markdown.json")
a = s:taboption("basic", Value, "corpsecret", translate('应用密钥(Secret)'))
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_mpnews.json")
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_markdown.json")
a = s:taboption("basic", Value, "mediapath", translate('图片缩略图文件路径'))
a.rmempty = true
a.default = "/usr/share/serverchan/api/logo.jpg"
a:depends("jsonpath", "/usr/share/serverchan/api/qywx_mpnews.json")
a.description = translate("只支持 2MB 以内 JPG,PNG 格式 <br> 900*383 或 2.35:1 为佳 ")
a = s:taboption("basic", Value, "wxpusher_apptoken", translate('appToken'), translate("").."获取 appToken <a href='https://wxpusher.zjiecode.com/docs/#/?id=%e5%bf%ab%e9%80%9f%e6%8e%a5%e5%85%a5' target='_blank'>点击这里</a><br>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/wxpusher.json")
a = s:taboption("basic", Value,"wxpusher_uids",translate('uids'))
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/wxpusher.json")
a = s:taboption("basic", Value, "wxpusher_topicIds", translate('topicIds(群发)'), translate("").."接口说明 <a href='https://wxpusher.zjiecode.com/docs/#/?id=%e5%8f%91%e9%80%81%e6%b6%88%e6%81%af-1'target='_blank'>点击这里</a><br>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/wxpusher.json")
a = s:taboption("basic", Value, "pushplus_token", translate('pushplus_token'), translate("").."获取pushplus_token <a href='http://www.pushplus.plus/' target='_blank'>点击这里</a><br>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/pushplus.json")
a = s:taboption("basic", Value, "tg_token", translate("TG_token"), translate("").."获取机器人<a href='https://t.me/BotFather' target='_blank'>点击这里</a><br>与创建的机器人发一条消息,开启对话<br>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/telegram.json")
a = s:taboption("basic", Value, "chat_id", translate('TG_chatid'), translate("").."获取 chat_id <a href='https://t.me/getuserIDbot' target='_blank'>点击这里</a>")
a.rmempty = true
a:depends("jsonpath", "/usr/share/serverchan/api/telegram.json")
a = s:taboption("basic", TextValue, "diy_json", translate("自定义推送"))
a.optional = false
a.rows = 28
a.wrap = "soft"
a.cfgvalue = function(self, section)
return fs.readfile("/usr/share/serverchan/api/diy.json")
end
a.write = function(self, section, value)
fs.writefile("/usr/share/serverchan/api/diy.json", value:gsub("\r\n", "\n"))
end
a:depends("jsonpath", "/usr/share/serverchan/api/diy.json")
a = s:taboption("basic", Button, "__add", translate("发送测试"))
a.inputtitle = translate("发送")
a.inputstyle = "apply"
function a.write(self, section)
luci.sys.call("cbi.apply")
luci.sys.call("/usr/share/serverchan/serverchan test &")
end
a = s:taboption("basic", Value, "device_name", translate('本设备名称'))
a.rmempty = true
a.description = translate("在推送信息标题中会标识本设备名称,用于区分推送信息的来源设备")
a = s:taboption("basic", Value, "sleeptime", translate('检测时间间隔s'))
a.rmempty = true
a.optional = false
a.default = "60"
a.datatype = "and(uinteger,min(10))"
a.description = translate("越短的时间响应越及时,但会占用更多的系统资源")
a = s:taboption("basic", ListValue, "oui_data", translate("MAC设备信息数据库"))
a.rmempty = true
a.default = ""
a:value("", translate("关闭"))
a:value("1", translate("简化版"))
a:value("2", translate("完整版"))
a:value("3", translate("网络查询"))
a.description = translate("需下载 4.36 MB 原始数据,处理后完整版约 1.2 MB简化版约 250 kB <br/>若无梯子,请勿使用网络查询")
a = s:taboption("basic", Flag, "oui_dir", translate("下载到内存"))
a.rmempty = true
a:depends("oui_data", "1")
a:depends("oui_data", "2")
a.description = translate("懒得做自动更新了,下载到内存中,重启会重新下载 <br/>若无梯子,还是下到机身吧")
a = s:taboption("basic", Flag, "reset_regularly", translate("每天零点重置流量数据"))
a.rmempty = true
a = s:taboption("basic", Flag, "debuglevel", translate("开启日志"))
a.rmempty = true
a = s:taboption("basic", DynamicList, "device_aliases", translate("设备别名"))
a.rmempty = true
a.description = translate("<br/> 请输入设备 MAC 和设备别名,用“-”隔开,如:<br/> XX:XX:XX:XX:XX:XX-我的手机")
-- 设备状态
a = s:taboption("content", ListValue, "serverchan_ipv4", translate("IPv4 变动通知"))
a.rmempty = true
a.default = ""
a:value("", translate("关闭"))
a:value("1", translate("通过接口获取"))
a:value("2", translate("通过URL获取"))
a = s:taboption("content", ListValue, "ipv4_interface", translate("接口名称"))
a.rmempty = true
a:depends({serverchan_ipv4 = "1"})
for _, iface in ipairs(ifaces) do
if not (iface == "lo" or iface:match("^ifb.*")) then
local nets = net:get_interface(iface)
nets = nets and nets:get_networks() or {}
for k, v in pairs(nets) do
nets[k] = nets[k].sid
end
nets = table.concat(nets, ",")
a:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
end
end
a.description = translate("<br/>一般选择 wan 接口,多拨环境请自行选择")
a = s:taboption("content", TextValue, "ipv4_list", translate("IPv4 API列表"))
a.optional = false
a.rows = 8
a.wrap = "soft"
a.cfgvalue = function(self, section)
return fs.readfile("/usr/share/serverchan/api/ipv4.list")
end
a.write = function(self, section, value)
fs.writefile("/usr/share/serverchan/api/ipv4.list", value:gsub("\r\n", "\n"))
end
a.description = translate("<br/>会因服务器稳定性、连接频繁等原因导致获取失败<br/>如接口可以正常获取 IP不推荐使用<br/>从以上列表中随机地址访问")
a:depends({serverchan_ipv4 = "2"})
a = s:taboption("content", ListValue, "serverchan_ipv6", translate("IPv6 变动通知"))
a.rmempty = true
a.default = "disable"
a:value("0", translate("关闭"))
a:value("1", translate("通过接口获取"))
a:value("2", translate("通过URL获取"))
a = s:taboption("content", ListValue, "ipv6_interface", translate("接口名称"))
a.rmempty = true
a:depends({serverchan_ipv6 = "1"})
for _, iface in ipairs(ifaces) do
if not (iface == "lo" or iface:match("^ifb.*")) then
local nets = net:get_interface(iface)
nets = nets and nets:get_networks() or {}
for k, v in pairs(nets) do
nets[k] = nets[k].sid
end
nets = table.concat(nets, ",")
a:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
end
end
a.description = translate("<br/>一般选择 wan 接口,多拨环境请自行选择")
a = s:taboption("content", TextValue, "ipv6_list", translate("IPv6 API列表"))
a.optional = false
a.rows = 8
a.wrap = "soft"
a.cfgvalue = function(self, section)
return fs.readfile("/usr/share/serverchan/api/ipv6.list")
end
a.write = function(self, section, value)
fs.writefile("/usr/share/serverchan/api/ipv6.list", value:gsub("\r\n", "\n"))
end
a.description = translate("<br/>会因服务器稳定性、连接频繁等原因导致获取失败<br/>如接口可以正常获取 IP不推荐使用<br/>从以上列表中随机地址访问")
a:depends({serverchan_ipv6 = "2"})
a = s:taboption("content", Flag, "serverchan_up", translate("设备上线通知"))
a.default = 1
a.rmempty = true
a = s:taboption("content", Flag, "serverchan_down", translate("设备下线通知"))
a.default = 1
a.rmempty = true
a = s:taboption("content", Flag, "cpuload_enable", translate("CPU 负载报警"))
a.default = 1
a.rmempty = true
a = s:taboption("content", Value, "cpuload", "负载报警阈值")
a.default = 2
a.rmempty = true
a:depends({cpuload_enable = "1"})
a = s:taboption("content", Flag, "temperature_enable", translate("CPU 温度报警"))
a.default = 1
a.rmempty = true
a.description = translate("请确认设备可以获取温度,如需修改命令,请移步高级设置")
a = s:taboption("content", Value, "temperature", "温度报警阈值")
a.rmempty = true
a.default = "80"
a.datatype = "and(uinteger,min(1))"
a:depends({temperature_enable = "1"})
a.description = translate("<br/>设备报警只会在连续五分钟超过设定值时才会推送<br/>而且一个小时内不会再提醒第二次")
a = s:taboption("content", Flag, "client_usage", translate("设备异常流量"))
a.default = 0
a.rmempty = true
a = s:taboption("content", Value, "client_usage_max", "每分钟流量限制")
a.default = "10M"
a.rmempty = true
a:depends({client_usage = "1"})
a.description = translate("设备异常流量警报byte你可以追加 K 或者 M")
a = s:taboption("content", Flag, "client_usage_disturb", translate("异常流量免打扰"))
a.default = 1
a.rmempty = true
a:depends({client_usage = "1"})
a = s:taboption("content", DynamicList, "client_usage_whitelist", translate("异常流量关注列表"))
nt.mac_hints(function(mac, name) a:value(mac, "%s (%s)" %{ mac, name }) end)
a.rmempty = true
a:depends({client_usage_disturb = "1"})
a.description = translate("请输入设备 MAC")
a = s:taboption("content", Flag, "web_logged", translate("web 登录提醒"))
a.default = 0
a.rmempty = true
a = s:taboption("content", Flag, "ssh_logged", translate("ssh 登录提醒"))
a.default = 0
a.rmempty = true
a = s:taboption("content", Flag, "web_login_failed", translate("web 错误尝试提醒"))
a.default = 0
a.rmempty = true
a = s:taboption("content", Flag, "ssh_login_failed", translate("ssh 错误尝试提醒"))
a.default = 0
a.rmempty = true
a = s:taboption("content", Value, "login_max_num", "错误尝试次数")
a.default = "3"
a.datatype = "and(uinteger,min(1))"
a:depends("web_login_failed", "1")
a:depends("ssh_login_failed", "1")
a.description = translate("超过次数后推送提醒,并可选自动拉黑")
-- 自动封禁
a = s:taboption("ipset", Flag, "web_login_black", translate("自动拉黑非法登录设备"))
a.default = 0
a.rmempty = true
a:depends("web_login_failed", "1")
a:depends("ssh_login_failed", "1")
a = s:taboption("ipset", Value, "ip_black_timeout", "拉黑时间(秒)")
a.default = "86400"
a.datatype = "and(uinteger,min(0))"
a:depends("web_login_black", "1")
a.description = translate("0 为永久拉黑,慎用<br>如不幸误操作,请更改设备 IP 进入 LUCI 界面清空规则")
a = s:taboption("ipset", DynamicList, "ip_white_list", translate("白名单 IP 列表"))
a.datatype = "ipaddr"
a.rmempty = true
luci.ip.neighbors({family = 4}, function(entry)
if entry.reachable then
a:value(entry.dest:string())
end
end)
a:depends("web_logged", "1")
a:depends("ssh_logged", "1")
a:depends("web_login_failed", "1")
a:depends("ssh_login_failed", "1")
a.description = translate("忽略推送,仅在日志中记录,并忽略拉黑操作,暂不支持掩码位表示")
a = s:taboption("ipset", Flag, "port_knocking", translate("端口敲门"))
a.default = 0
a.rmempty = true
a.description = translate("登录成功后开放端口")
a = s:taboption("ipset", Value, "ip_port_white", "端口")
a.default = ""
a.rmempty = true
a.description = translate("例:'22'、'21:25'、'21:25,135:139'")
a:depends("port_knocking", "1")
a = s:taboption("ipset", DynamicList, "port_forward_list", "端口转发")
a.default = ""
a.rmempty = true
a.description = translate("例:将本机(10.0.0.1)的 13389 端口转发到 10.0.0.2 的3389<br/>'10.0.0.1,13389,10.0.0.2,3389'<br/>IPv6 未测试")
a:depends("port_knocking", "1")
a = s:taboption("ipset", Value, "ip_white_timeout", "放行时间(秒)")
a.default = "600"
a.rmempty = true
a.datatype = "and(uinteger,min(0))"
a.description = translate("0 为永久放行,慎用<br/>连接成功后不断开就不需要重新连接,故不需要设置太大<br/>注:响应时间与检测间隔和每一次检测所需的时间相关,故反应不是很快,将就用吧")
a:depends("port_knocking", "1")
a = s:taboption("ipset", TextValue, "ip_black_list", translate("IP 黑名单列表"))
a.optional = false
a.rows = 8
a.wrap = "soft"
a.cfgvalue = function(self, section)
return fs.readfile("/usr/share/serverchan/api/ip_blacklist")
end
a.write = function(self, section, value)
fs.writefile("/usr/share/serverchan/api/ip_blacklist", value:gsub("\r\n", "\n"))
end
a:depends("web_login_black", "1")
a.description = translate("可在此处添加或删除timeout 后的数字为剩余时间(秒),添加时只需要输入 IP")
-- 定时推送
a = s:taboption("crontab", ListValue, "crontab", translate("定时任务设定"))
a.rmempty = true
a.default = ""
a:value("", translate("关闭"))
a:value("1", translate("定时发送"))
a:value("2", translate("间隔发送"))
a = s:taboption("crontab", ListValue, "regular_time", translate("发送时间"))
a.rmempty = true
for t = 0, 23 do
a:value(t, translate("每天"..t..""))
end
a.default = 8
a.datatype = uinteger
a:depends("crontab", "1")
a = s:taboption("crontab", ListValue, "regular_time_2", translate("发送时间"))
a.rmempty = true
a:value("", translate("关闭"))
for t = 0, 23 do
a:value(t, translate("每天"..t..""))
end
a.default = "关闭"
a.datatype = uinteger
a:depends("crontab", "1")
a = s:taboption("crontab", ListValue, "regular_time_3", translate("发送时间"))
a.rmempty = true
a:value("", translate("关闭"))
for t = 0, 23 do
a:value(t, translate("每天"..t..""))
end
a.default = "关闭"
a.datatype = uinteger
a:depends("crontab", "1")
a = s:taboption("crontab", ListValue, "interval_time", translate("发送间隔"))
a.rmempty = true
for t = 1, 23 do
a:value(t, translate(t.."小时"))
end
a.default = 6
a.datatype = uinteger
a:depends("crontab", "2")
a.description = translate("<br/>从 00:00 开始,每 * 小时发送一次")
a = s:taboption("crontab", Value, "send_title", translate("微信推送标题"))
a:depends("crontab", "1")
a:depends("crontab", "2")
a.placeholder = "OpenWrt By tty228 路由状态:"
a.description = translate("<br/>使用特殊符号可能会造成发送失败")
a = s:taboption("crontab", Flag, "router_status", translate("系统运行情况"))
a.default = 1
a:depends("crontab", "1")
a:depends("crontab", "2")
a = s:taboption("crontab", Flag, "router_temp", translate("设备温度"))
a.default = 1
a:depends("crontab", "1")
a:depends("crontab", "2")
a = s:taboption("crontab", Flag, "router_wan", translate("WAN信息"))
a.default = 1
a:depends("crontab", "1")
a:depends("crontab", "2")
a = s:taboption("crontab", Flag, "client_list", translate("客户端列表"))
a.default = 1
a:depends("crontab", "1")
a:depends("crontab", "2")
e = s:taboption("crontab", Button, "_add", translate("手动发送"))
e.inputtitle = translate("发送")
e:depends("crontab", "1")
e:depends("crontab", "2")
e.inputstyle = "apply"
function e.write(self, section)
luci.sys.call("cbi.apply")
luci.sys.call("/usr/share/serverchan/serverchan send &")
end
-- 免打扰
a = s:taboption("disturb", ListValue, "serverchan_sheep", translate("免打扰时段设置"), translate("在指定整点时间段内,暂停推送消息<br/>免打扰时间中,定时推送也会被阻止。"))
a.rmempty = true
a:value("", translate("关闭"))
a:value("1", translate("模式一:脚本挂起"))
a:value("2", translate("模式二:静默模式"))
a.description = translate("模式一停止一切检测,包括无人值守。")
a = s:taboption("disturb", ListValue, "starttime", translate("免打扰开始时间"))
a.rmempty = true
for t = 0, 23 do
a:value(t, translate("每天"..t..""))
end
a.default = 0
a.datatype = uinteger
a:depends({serverchan_sheep = "1"})
a:depends({serverchan_sheep = "2"})
a = s:taboption("disturb", ListValue, "endtime", translate("免打扰结束时间"))
a.rmempty = true
for t = 0, 23 do
a:value(t, translate("每天"..t..""))
end
a.default = 8
a.datatype = uinteger
a:depends({serverchan_sheep = "1"})
a:depends({serverchan_sheep = "2"})
a = s:taboption("disturb", ListValue, "macmechanism", translate("MAC过滤"))
a:value("", translate("disable"))
a:value("allow", translate("忽略列表内设备"))
a:value("block", translate("仅通知列表内设备"))
a:value("interface", translate("仅通知此接口设备"))
a.rmempty = true
a = s:taboption("disturb", DynamicList, "serverchan_whitelist", translate("忽略列表"))
nt.mac_hints(function(mac, name) a :value(mac, "%s (%s)" %{ mac, name }) end)
a.rmempty = true
a:depends({macmechanism = "allow"})
a.description = translate("AA:AA:AA:AA:AA:AA\\|BB:BB:BB:BB:BB:B 可以将多个 MAC 视为同一用户<br/>任一设备在线后不再推送,设备全部离线时才会推送,避免双 wifi 频繁推送")
a = s:taboption("disturb", DynamicList, "serverchan_blacklist", translate("关注列表"))
nt.mac_hints(function(mac, name) a:value(mac, "%s (%s)" %{ mac, name }) end)
a.rmempty = true
a:depends({macmechanism = "block"})
a.description = translate("AA:AA:AA:AA:AA:AA\\|BB:BB:BB:BB:BB:B 可以将多个 MAC 视为同一用户<br/>任一设备在线后不再推送,设备全部离线时才会推送,避免双 wifi 频繁推送")
a = s:taboption("disturb", ListValue, "serverchan_interface", translate("接口名称"))
a:depends({macmechanism = "interface"})
a.rmempty = true
for _, iface in ipairs(ifaces) do
if not (iface == "lo" or iface:match("^ifb.*")) then
local nets = net:get_interface(iface)
nets = nets and nets:get_networks() or {}
for k, v in pairs(nets) do
nets[k] = nets[k].sid
end
nets = table.concat(nets, ",")
a:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
end
end
a = s:taboption("disturb", ListValue, "macmechanism2", translate("MAC过滤2"))
a:value("", translate("disable"))
a:value("MAC_online", translate("列表内任意设备在线时免打扰"))
a:value("MAC_offline", translate("列表内设备都离线后免打扰"))
a.rmempty = true
a = s:taboption("disturb", DynamicList, "MAC_online_list", translate("在线免打扰列表"))
nt.mac_hints(function(mac, name) a:value(mac, "%s (%s)" %{ mac, name }) end)
a.rmempty = true
a:depends({macmechanism2 = "MAC_online"})
a = s:taboption("disturb", DynamicList, "MAC_offline_list", translate("任意离线免打扰列表"))
nt.mac_hints(function(mac, name) a:value(mac, "%s (%s)" %{ mac, name }) end)
a.rmempty = true
a:depends({macmechanism2 = "MAC_offline"})
return m

View File

@ -0,0 +1,33 @@
<%
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
function clearlog(btn) {
XHR.get('<%=dsp.build_url("admin/services/serverchan/clear_log")%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = "";
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
}
XHR.poll(2, '<%=dsp.build_url("admin/services/serverchan/get_log")%>', null,
function(x, data) {
if(x && x.status == 200 && document.getElementById("checkbox1").checked == true) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = x.responseText;
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
//]]>
</script>
<fieldset class="cbi-section" id="_log_fieldset">
<input type="checkbox" id="checkbox1" style="vertical-align:middle;height: auto;"checked><%:自动刷新%></input>
<input class="cbi-button cbi-input-remove" type="button" onclick="clearlog()" value="<%:清除日志%>" />
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="30" wrap="off" readonly="readonly"></textarea>
</fieldset>

View File

@ -0,0 +1,22 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[serverchan]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('serverchan_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color=green>serverchan <%:RUNNING%></font></b></em>';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color=red>serverchan <%:NOT RUNNING%></font></b></em>';
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="serverchan_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,11 @@
config serverchan 'serverchan'
option serverchan_enable '0'
option sleeptime '60'
option serverchan_ipv6 '0'
option serverchan_up '1'
option serverchan_down '1'
option cpuload_enable '1'
option cpuload '2'
option temperature_enable '0'

View File

@ -0,0 +1,26 @@
#!/bin/sh /etc/rc.common
START=99
STOP=10
start() {
state=`pgrep -f "/usr/share/serverchan/serverchan"`
if [ ! -z "$state" ]; then
restart
else
/usr/share/serverchan/serverchan &
fi
echo "serverchan is starting now ..."
}
stop() {
kill -9 `pgrep -f "/usr/share/serverchan/serverchan"` 2>/dev/null
echo "serverchan exit ..."
}
restart(){
stop
sleep 1
start
echo "restarted."
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@serverchan[-1]
add ucitrack serverchan
set ucitrack.@serverchan[-1].init=serverchan
commit ucitrack
EOF
rm -rf /tmp/luci-*
exit 0

View File

@ -0,0 +1,11 @@
{
"luci-app-serverchan": {
"description": "Grant UCI access for luci-app-serverchan",
"read": {
"uci": [ "serverchan" ]
},
"write": {
"uci": [ "serverchan" ]
}
}
}

View File

@ -0,0 +1,42 @@
{
"_//": "-------------------------------------------------------------------------------",
"_readme": "这是 自定义 api 文件,这里以 telegram 为例",
"_readme": "特殊符号请使用斜杠转义,变量使用 ${var} 表示",
"_//": "-------------------------------------------------------------------------------",
"_api": "【DIY 推送】",
"_url": "api 地址",
"_data": "生成的 json 文件路径,一般不需要改,如 api 不支持 json请参考 serverchan 推送接口",
"_content_type": "post 内容类型,这里为 json",
"_//": "-------------------------------------------------------------------------------",
"_str_title_start": "标题粗体字开始符号",
"_str_title_end": "标题粗体字结束符号",
"_str_linefeed": "换行符号",
"_str_splitline": "换行+分隔符",
"_str_space": "空格",
"_str_tab": "TAB用在行首生成文字区块",
"_//": "-------------------------------------------------------------------------------",
"_type":
{
"_readme": "下文中text 为 telegram 推送需要的键值名称,参见 telegram 官方文档,后面的内容为生成标题所需要的字符串和变量,${1} 为标题内容变量,${nowtime} 为推送时间,${2} 为推送内容变量",
"_readme": "下文中chat_id 为 telegram 推送需要的键值名称,参见 telegram 官方文档,${chat_id} 为从脚本配置中读取名为 chat_id 的变量,其实就是你填写的机器人的 chat_id ",
"_readme": "type 对象因为需要转义变量,前后必须使用 斜杠+双引号 转义",
"_readme": "参照上文说明,填写下文相关参数"
},
"_//": "-------------------------------------------------------------------------------",
"url": "https://api.telegram.org/bot${tg_token}/sendMessage",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "<b>",
"str_title_end": "</b>",
"str_linefeed": "\\n",
"str_splitline": "\\n----\\n",
"str_space": " ",
"str_tab": " ",
"type":
{
"text":"\"${str_title_start}${1}${str_title_end}${str_splitline}${nowtime}${2}\"",
"chat_id":"\"${chat_id}\"",
"parse_mode":"\"HTML\""
}
}

View File

@ -0,0 +1,8 @@
cip.cc
ddns.oray.com/checkip
www.net.cn/static/customercare/yourip.asp
myip.ipip.net/s
ip.3322.net
ip.threep.top
10086.cn/web-Center/commonservice/getUserIp.do --referer http://10086.cn -XPOST
https://www.taobao.com/help/getip.php -H 'authority: www.taobao.com'

View File

@ -0,0 +1,5 @@
ipv6.ddnspod.com
speed.neu6.edu.cn/getIP.php
6.ipw.cn
10086.cn/web-Center/commonservice/getUserIp.do --referer http://10086.cn -XPOST
https://www.taobao.com/help/getip.php -H 'authority: www.taobao.com'

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,21 @@
{
"_api": "这是 pushplus api 文件",
"_api": "【pushplus】",
"url": "http://www.pushplus.plus/send",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "##### ",
"str_title_end": "",
"str_linefeed": "\\n",
"str_splitline": "\\n----\\n",
"str_space": " ",
"str_tab": " ",
"type":
{
"title": "\"${1}\"",
"content": "\"${2}\"",
"token": "\"${pushplus_token}\"",
"template":"\"markdown\""
}
}

View File

@ -0,0 +1,23 @@
{
"_api": "这是企业微信 markdown 模板信息 api 文件",
"_api": "【企业微信】",
"url": "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$(curl -s \"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpid}&corpsecret=${corpsecret}\"|jq '.access_token' | sed 's/\"//g')",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "#### ",
"str_title_end": "",
"str_linefeed": "\\n",
"str_splitline": "\\n\\n",
"str_space": " ",
"str_tab": "> ",
"type":
{
"touser": "\"${userid}\"",
"msgtype": "\"markdown\"",
"agentid": "\"${agentid}\"",
"markdown": {
"content": "\"${1}${str_linefeed}${nowtime}${2}\""
}
}
}

View File

@ -0,0 +1,33 @@
{
"_api": "这是企业微信图文信息 api 文件",
"_api": "【企业微信】",
"url": "\"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$(curl -s \"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpid}&corpsecret=${corpsecret}\"|jq '.access_token'|sed 's/\"//g')\"",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "<h3>",
"str_title_end": "</h3>",
"str_linefeed": "\\n",
"str_splitline": "<hr>",
"str_space": " ",
"str_tab": "<li>",
"type":
{
"touser": "\"${userid}\"",
"msgtype": "\"mpnews\"",
"agentid": "\"${agentid}\"",
"mpnews":{
"articles":[
{
"title": "\"${nowtime}${str_linefeed}${1}\"",
"thumb_media_id": "\"`curl \"https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=$(curl -s \"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpid}&corpsecret=${corpsecret}\"|jq '.access_token'|sed 's/\"//g')&type=image\" -F \"file=@${mediapath}\"|jq '.media_id'|sed 's/\"//g'`\"",
"author": "\"\"",
"content_source_url": "\"\"",
"content": "\"${2}\"",
"digest": "\"\""
}
]
},
"safe":0
}
}

View File

@ -0,0 +1,17 @@
{
"_api": "这是 serverchan api 文件",
"_api": "【serverchan】",
"url": "\"https://sctapi.ftqq.com/${sckey}.send\"",
"data": "\"text=${1}&desp=${nowtime}${str_linefeed}${2}\"",
"content_type": "Content-Type:application/x-www-form-urlencoded",
"str_title_start": "#### ",
"str_title_end": "",
"str_linefeed": "%0D%0A%0D%0A",
"str_splitline": "%0D%0A%0D%0A----%0D%0A%0D%0A",
"str_space": " ",
"str_tab": " ",
"type":
{
}
}

View File

@ -0,0 +1,20 @@
{
"_api": "这是 telegram api 文件",
"_api": "【telegram】",
"url": "https://api.telegram.org/bot${tg_token}/sendMessage",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "<b>",
"str_title_end": "</b>",
"str_linefeed": "\\n",
"str_splitline": "\\n----\\n",
"str_space": " ",
"str_tab": " ",
"type":
{
"text":"\"${str_title_start}${1}${str_title_end}${str_splitline}${nowtime}${2}\"",
"chat_id":"\"${chat_id}\"",
"parse_mode":"\"HTML\""
}
}

View File

@ -0,0 +1,23 @@
{
"_api": "这是 wxpusher api 文件",
"_api": "【wxpusher】",
"url": "http://wxpusher.zjiecode.com/api/send/message",
"data": "@${tempjsonpath}",
"content_type": "Content-Type: application/json",
"str_title_start": "#### ",
"str_title_end": "",
"str_linefeed": "\\n",
"str_splitline": "\\n----\\n",
"str_space": " ",
"str_tab": " ",
"type":
{
"summary":"\"${1}\"",
"content":"\"${2}\"",
"appToken":"\"${wxpusher_apptoken}\"",
"topicIds":"[\"${wxpusher_topicIds}\"]",
"uids":"[\"${wxpusher_uids}\"]",
"contentType":3
}
}

File diff suppressed because it is too large Load Diff