From 0cc038fef145ce289b86b792f7edc2ed364878ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 5 Aug 2023 20:01:09 +0800 Subject: [PATCH] update 2023-08-05 20:01:09 --- luci-app-pushbot/Makefile | 22 - luci-app-pushbot/README.md | 93 - .../luasrc/controller/pushbot.lua | 32 - .../luasrc/model/cbi/pushbot/advanced.lua | 124 -- .../luasrc/model/cbi/pushbot/client.lua | 6 - .../luasrc/model/cbi/pushbot/log.lua | 5 - .../luasrc/model/cbi/pushbot/setting.lua | 552 ------ .../luasrc/view/pushbot/pushbot_log.htm | 33 - .../luasrc/view/pushbot/pushbot_status.htm | 22 - luci-app-pushbot/root/etc/config/pushbot | 10 - luci-app-pushbot/root/etc/init.d/pushbot | 26 - .../root/etc/uci-defaults/luci-pushbot | 11 - .../root/usr/bin/pushbot/api/bark.json | 36 - .../root/usr/bin/pushbot/api/dingding.json | 32 - .../root/usr/bin/pushbot/api/diy.json | 50 - .../root/usr/bin/pushbot/api/ent_wechat.json | 32 - .../root/usr/bin/pushbot/api/feishu.json | 61 - .../root/usr/bin/pushbot/api/ip_blacklist | 1 - .../root/usr/bin/pushbot/api/ipv4.list | 6 - .../root/usr/bin/pushbot/api/ipv6.list | 5 - .../root/usr/bin/pushbot/api/pushdeer.json | 31 - .../root/usr/bin/pushbot/api/pushplus.json | 34 - luci-app-pushbot/root/usr/bin/pushbot/pushbot | 1274 ------------ .../share/rpcd/acl.d/luci-app-pushbot.json | 11 - luci-app-wechatpush/Makefile | 24 + luci-app-wechatpush/README.md | 92 + luci-app-wechatpush/README_en.md | 81 + .../resources/view/wechatpush/advanced.js | 200 ++ .../resources/view/wechatpush/client.js | 264 +++ .../resources/view/wechatpush/config.js | 696 +++++++ .../resources/view/wechatpush/log.js | 133 ++ luci-app-wechatpush/po/zh-cn | 1 + luci-app-wechatpush/po/zh_Hans/wechatpush.po | 976 +++++++++ .../root/etc/config/wechatpush | 9 + .../root/etc/init.d/wechatpush | 36 + .../root/etc/uci-defaults/luci-wechatpush | 179 ++ .../root/usr/libexec/wechatpush-call | 48 + .../luci/menu.d/luci-app-wechatpush.json | 49 + .../share/rpcd/acl.d/luci-app-wechatpush.json | 25 + .../share/wechatpush/api/device_aliases.list | 3 + .../root/usr/share/wechatpush/api/diy.json | 42 + .../share/wechatpush/api/ip_attribution.list | 2 + .../usr/share/wechatpush/api/ip_blacklist | 0 .../root/usr/share/wechatpush/api/ipv4.list | 7 + .../root/usr/share/wechatpush/api/ipv6.list | 5 + .../root/usr/share/wechatpush/api/logo.jpg | Bin 0 -> 33685 bytes .../usr/share/wechatpush/api/pushplus.json | 21 + .../share/wechatpush/api/qywx_markdown.json | 23 + .../usr/share/wechatpush/api/qywx_mpnews.json | 33 + .../usr/share/wechatpush/api/serverchan.json | 17 + .../usr/share/wechatpush/api/telegram.json | 20 + .../usr/share/wechatpush/api/wxpusher.json | 23 + .../root/usr/share/wechatpush/wechatpush | 1759 +++++++++++++++++ 53 files changed, 4768 insertions(+), 2509 deletions(-) delete mode 100644 luci-app-pushbot/Makefile delete mode 100644 luci-app-pushbot/README.md delete mode 100644 luci-app-pushbot/luasrc/controller/pushbot.lua delete mode 100644 luci-app-pushbot/luasrc/model/cbi/pushbot/advanced.lua delete mode 100644 luci-app-pushbot/luasrc/model/cbi/pushbot/client.lua delete mode 100644 luci-app-pushbot/luasrc/model/cbi/pushbot/log.lua delete mode 100644 luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua delete mode 100644 luci-app-pushbot/luasrc/view/pushbot/pushbot_log.htm delete mode 100644 luci-app-pushbot/luasrc/view/pushbot/pushbot_status.htm delete mode 100644 luci-app-pushbot/root/etc/config/pushbot delete mode 100755 luci-app-pushbot/root/etc/init.d/pushbot delete mode 100755 luci-app-pushbot/root/etc/uci-defaults/luci-pushbot delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/bark.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/dingding.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/diy.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/ent_wechat.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/feishu.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/ip_blacklist delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/ipv4.list delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/ipv6.list delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json delete mode 100644 luci-app-pushbot/root/usr/bin/pushbot/api/pushplus.json delete mode 100755 luci-app-pushbot/root/usr/bin/pushbot/pushbot delete mode 100644 luci-app-pushbot/root/usr/share/rpcd/acl.d/luci-app-pushbot.json create mode 100755 luci-app-wechatpush/Makefile create mode 100644 luci-app-wechatpush/README.md create mode 100644 luci-app-wechatpush/README_en.md create mode 100644 luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js create mode 100644 luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/client.js create mode 100644 luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js create mode 100644 luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js create mode 120000 luci-app-wechatpush/po/zh-cn create mode 100644 luci-app-wechatpush/po/zh_Hans/wechatpush.po create mode 100644 luci-app-wechatpush/root/etc/config/wechatpush create mode 100755 luci-app-wechatpush/root/etc/init.d/wechatpush create mode 100755 luci-app-wechatpush/root/etc/uci-defaults/luci-wechatpush create mode 100755 luci-app-wechatpush/root/usr/libexec/wechatpush-call create mode 100644 luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json create mode 100644 luci-app-wechatpush/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/device_aliases.list create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/diy.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/ip_attribution.list create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/ip_blacklist create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/ipv4.list create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/ipv6.list create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/logo.jpg create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/pushplus.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/qywx_markdown.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/qywx_mpnews.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/serverchan.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/telegram.json create mode 100644 luci-app-wechatpush/root/usr/share/wechatpush/api/wxpusher.json create mode 100755 luci-app-wechatpush/root/usr/share/wechatpush/wechatpush diff --git a/luci-app-pushbot/Makefile b/luci-app-pushbot/Makefile deleted file mode 100644 index 2a779819..00000000 --- a/luci-app-pushbot/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-pushbot -PKG_VERSION:=3.60 -PKG_RELEASE:=1 - -PKG_MAINTAINER:=tty228 zzsj0928 - -LUCI_TITLE:=LuCI support for Pushbot -LUCI_PKGARCH:=all -LUCI_DEPENDS:=+iputils-arping +curl +jq - -define Package/$(PKG_NAME)/conffiles -/etc/config/pushbot -/usr/bin/pushbot/api/diy.json -/usr/bin/pushbot/api/ipv4.list -/usr/bin/pushbot/api/ipv6.list -endef - -include $(TOPDIR)/feeds/luci/luci.mk - -# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-pushbot/README.md b/luci-app-pushbot/README.md deleted file mode 100644 index 3c3284a5..00000000 --- a/luci-app-pushbot/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# 改名公告 -#### 2021年04月25日 起luci-app-serverchand 改名为 luci-app-pushbot - -如需拉取编译 -请把: - -`# git clone https://github.com/zzsj0928/luci-app-serverchand package/luci-app-serverchand` - -改为 - -`git clone https://github.com/zzsj0928/luci-app-pushbot package/luci-app-pushbot` - -并把 .config 中 - -`CONFIG_PACKAGE_luci-app-serverchand=y` - -改为 - -`CONFIG_PACKAGE_luci-app-pushbot=y` - -注意:本次改名需要提前备份serverchand配置,并于PushBot中重新配置。 - -再次谢谢各位支持 - -# 申明 -- 本插件由[tty228/luci-app-serverchan](https://github.com/tty228/luci-app-serverchan)原创. -- 因微信推送存在诸多弊端(无法分开聊天工具与功能性消息推送,通知内不显示内容,内容需要点开才能查看等), -- 故由 然后七年 @zzsj0928 重新修改为本插件,为钉钉机器人API使用。 -- 本插件工作在:openwrt -- 本插件支持:钉钉推送,企业微信推送,PushPlus推送,微信推送,企业微信应用推送,飞书推送,钉钉机器人推送,企业微信机器人推送,飞书机器人推送,一对多推送,Bark推送(仅iOS),PushDeer,PushDeer自架 -- 自20210911之后的版本,支持Bark群组,群组名默认为设备名 -- 自20210901之后的版本,增加依赖jq,请重新编译或在安装前同步安装jq - -# 显示效果 -## 通知栏:直接显示推送主题,一目了然,按设备不同,分组显示 - - -## 消息列表:直接显示最新推送的标题 - - -## 消息内容:直接显示所有推送信息,不用二次点开再查看 - - -# 下载 -- [luci-app-pushbot](https://github.com/zzsj0928/luci-app-pushbot/releases) - - ------------------------------------------------------ -##################################################### ------------------------------------------------------ - -# 以下为原插件简介: - -# 简介 -- 用于 OpenWRT/LEDE 路由器上进行 Server酱 微信/Telegram 推送的插件 -- 基于 serverchan 提供的接口发送信息,Server酱说明:http://sc.ftqq.com/1.version -- **基于斐讯 k3 制作,不同系统不同设备,请自行修改部分代码,无测试条件无法重现的 bug 不考虑修复** -- 依赖 iputils-arping + curl 命令,安装前请 `opkg update`,小内存路由谨慎安装 -- 使用主动探测设备连接的方式检测设备在线状态,以避免WiFi休眠机制,主动探测较为耗时,**如遇设备休眠频繁,请自行调整超时设置** -- 流量统计功能依赖 wrtbwmon ,自行选装或编译,该插件与 Routing/NAT 、Flow Offloading 冲突,开启无法获取流量,自行选择,L大版本直接编译 luci-app-wrtbwmon - -#### 主要功能 -- 路由 ip/ipv6 变动推送 -- 设备别名 -- 设备上线推送 -- 设备离线推送及流量使用情况 -- CPU 负载、温度监视 -- 定时推送设备运行状态 -- MAC 白名单、黑名单、按接口检测设备 -- 免打扰 -- 无人值守任务 - -#### 说明 -- 潘多拉系统、或不支持 sh 的系统,请将脚本开头 `#!/bin/sh` 改为 `#!/bin/bash`,或手动安装 `sh` -- 追新是没有意义的,没有问题没必要更新,上班事情忙完了,摸鱼又不会摸,只能靠写几行 bug ,才能缓解无聊这样子 - -#### 已知问题 -- 直接关闭接口时,该接口的离线设备会忽略检测 -- 部分设备无法读取到设备名,脚本使用 `cat /var/dhcp.leases` 命令读取设备名,如果 dhcp 中不存在设备名,则无法读取设备名(如二级路由设备、静态ip设备),请使用设备名备注 - -# 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) - -#### ps -- 新功能看情况开发 -- 王者荣耀新赛季,不思进取中 -- 欢迎各种代码提交 -- 提交bug时请尽量带上设备信息,日志与描述(如执行`/usr/bin/serverchan/serverchan`后的提示、日志信息、/tmp/serverchan/ipAddress 文件信息) -- 三言两句恕我无能为力 -- 武汉加油 - diff --git a/luci-app-pushbot/luasrc/controller/pushbot.lua b/luci-app-pushbot/luasrc/controller/pushbot.lua deleted file mode 100644 index 5b586154..00000000 --- a/luci-app-pushbot/luasrc/controller/pushbot.lua +++ /dev/null @@ -1,32 +0,0 @@ -module("luci.controller.pushbot",package.seeall) - -function index() - if not nixio.fs.access("/etc/config/pushbot") then - return - end - - entry({"admin", "services", "pushbot"}, alias("admin", "services", "pushbot", "setting"),_("全能推送"), 30).dependent = true - entry({"admin", "services", "pushbot", "setting"}, cbi("pushbot/setting"),_("配置"), 40).leaf = true - entry({"admin", "services", "pushbot", "advanced"}, cbi("pushbot/advanced"),_("高级设置"), 50).leaf = true - entry({"admin", "services", "pushbot", "client"}, form("pushbot/client"), "在线设备", 80) - entry({"admin", "services", "pushbot", "log"}, form("pushbot/log"),_("日志"), 99).leaf = true - entry({"admin", "services", "pushbot", "get_log"}, call("get_log")).leaf = true - entry({"admin", "services", "pushbot", "clear_log"}, call("clear_log")).leaf = true - entry({"admin", "services", "pushbot", "status"}, call("act_status")).leaf = true -end - -function act_status() - local e={} - e.running=luci.sys.call("busybox ps|grep -v grep|grep -c pushbot >/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/pushbot/pushbot.log' ] && cat /tmp/pushbot/pushbot.log")) -end - -function clear_log() - luci.sys.call("echo '' > /tmp/pushbot/pushbot.log") -end diff --git a/luci-app-pushbot/luasrc/model/cbi/pushbot/advanced.lua b/luci-app-pushbot/luasrc/model/cbi/pushbot/advanced.lua deleted file mode 100644 index a6fb78ae..00000000 --- a/luci-app-pushbot/luasrc/model/cbi/pushbot/advanced.lua +++ /dev/null @@ -1,124 +0,0 @@ -local nt = require "luci.sys".net -local fs=require"nixio.fs" - -m=Map("pushbot",translate("提示"), -translate("如果你不了解这些选项的含义,请不要修改这些选项。")) - -s = m:section(TypedSection, "pushbot", "高级设置") -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("若无二级路由设备,信号强度良好,可以减少以上数值
因夜间 wifi 休眠较为玄学,遇到设备频繁推送断开,烦请自行调整参数
..╮(╯_╰)╭..") - -a=s:option(Value,"thread_num",translate('最大并发进程数')) -a.default = "3" -a.datatype="uinteger" - -a=s:option(Value, "soc_code", "自定义温度读取命令") -a.rmempty = true -a:value("",translate("默认")) -a:value("pve",translate("PVE 虚拟机")) -a.description = translate("请尽量避免使用特殊符号,如双引号、$、!等,执行结果需为数字,用于温度对比") - -a=s:option(Value,"pve_host",translate("宿主机地址")) -a.rmempty=true -a.default="10.0.0.2" -a.description = translate("请确认已经设置好密钥登陆,否则会引起脚本无法运行等错误!
PVE 安装 sensors 命令自行百度
密钥登陆例:
opkg update #更新列表
opkg install openssh-client openssh-keygen #安装openssh客户端
ssh-keygen -t rsa # 生成密钥文件(自行设定密码等信息)
ssh root@10.0.0.2 \"tee -a ~/.ssh/id_rsa.pub\" < ~/.ssh/id_rsa.pub # 传送公钥到 PVE
ssh root@10.0.0.2 \"cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys\" # 写入公钥到 PVE
ssh -i ~/.ssh/id_rsa root@10.0.0.2 sensors # 测试温度命令") -a:depends({soc_code="pve"}) - -a=s:option(Value,"pve_port",translate("SSH端口")) -a.rmempty=true -a.default="22" -a.description = translate("默认为22,如有自定义,请填写自定义SSH端口") -a:depends({soc_code="pve"}) - -a=s:option(Button,"soc",translate("测试温度命令")) -a.inputtitle = translate("输出信息") -a.write = function() - luci.sys.call("/usr/bin/pushbot/pushbot soc") - luci.http.redirect(luci.dispatcher.build_url("admin","services","pushbot","advanced")) -end - -if nixio.fs.access("/tmp/pushbot/soc_tmp") then -e=s:option(TextValue,"soc_tmp") -e.rows=2 -e.readonly=true -e.cfgvalue = function() - return luci.sys.exec("cat /tmp/pushbot/soc_tmp && rm -f /tmp/pushbot/soc_tmp") -end -end - -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 域名等待解析,此功能不影响断网检测
因夜间跑流量问题,该功能可能不稳定") -a:depends({err_enable="1"}) - -a= s:option(DynamicList, "err_device_aliases", translate("关注列表")) -a.rmempty = true -a.description = translate("只会在列表中设备都不在线时才会执行
免打扰时段一小时后,关注设备五分钟低流量(约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 次。
选项 3 会将设置项备份于 /usr/bin/pushbot/configbak 目录,并在失败后还原。
【!!无法保证兼容性!!】不熟悉系统设置项,不会救砖请勿使用") - -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 地址
请确认你可以通过重拨获取公网 ip,否则这不仅徒劳无功还会引起频繁断网
移动等大内网你就别挣扎了!!") - -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 diff --git a/luci-app-pushbot/luasrc/model/cbi/pushbot/client.lua b/luci-app-pushbot/luasrc/model/cbi/pushbot/client.lua deleted file mode 100644 index dbb80f2b..00000000 --- a/luci-app-pushbot/luasrc/model/cbi/pushbot/client.lua +++ /dev/null @@ -1,6 +0,0 @@ -f = SimpleForm("pushbot") -luci.sys.call("/usr/bin/pushbot/pushbot client") -f.reset = false -f.submit = false -f:append(Template("pushbot/pushbot_client")) -return f diff --git a/luci-app-pushbot/luasrc/model/cbi/pushbot/log.lua b/luci-app-pushbot/luasrc/model/cbi/pushbot/log.lua deleted file mode 100644 index 0b73ae08..00000000 --- a/luci-app-pushbot/luasrc/model/cbi/pushbot/log.lua +++ /dev/null @@ -1,5 +0,0 @@ -f = SimpleForm("pushbot") -f.reset = false -f.submit = false -f:append(Template("pushbot/pushbot_log")) -return f diff --git a/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua b/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua deleted file mode 100644 index f6330cf2..00000000 --- a/luci-app-pushbot/luasrc/model/cbi/pushbot/setting.lua +++ /dev/null @@ -1,552 +0,0 @@ - -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("pushbot",translate("PushBot"), -translate("「全能推送」,英文名「PushBot」,是一款从服务器推送报警信息和日志到各平台的工具。
支持钉钉推送,企业微信推送,PushPlus推送。
本插件由tty228/luci-app-serverchan创建,然后七年修改为全能推送自用。

如果你在使用中遇到问题,请到这里提交:") -.. [[]] -.. translate("github 项目地址") -.. [[]] -) - -m:section(SimpleSection).template = "pushbot/pushbot_status" - -s=m:section(NamedSection,"pushbot","pushbot",translate("")) -s:tab("basic", translate("基本设置")) -s:tab("content", translate("推送内容")) -s:tab("crontab", translate("定时推送")) -s:tab("disturb", translate("免打扰")) -s.addremove = false -s.anonymous = true - ---基本设置 -a=s:taboption("basic", Flag,"pushbot_enable",translate("启用")) -a.default=0 -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/bin/pushbot/api/dingding.json" -a.rmempty = true -a:value("/usr/bin/pushbot/api/dingding.json",translate("钉钉")) -a:value("/usr/bin/pushbot/api/ent_wechat.json",translate("企业微信")) -a:value("/usr/bin/pushbot/api/feishu.json",translate("飞书")) -a:value("/usr/bin/pushbot/api/bark.json",translate("Bark")) -a:value("/usr/bin/pushbot/api/pushplus.json",translate("PushPlus")) -a:value("/usr/bin/pushbot/api/pushdeer.json",translate("PushDeer")) -a:value("/usr/bin/pushbot/api/diy.json",translate("自定义推送")) - -a=s:taboption("basic", Value,"dd_webhook",translate('Webhook'), translate("钉钉机器人 Webhook")..",只输入access_token=后面的即可
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/dingding.json") - -a=s:taboption("basic", Value, "we_webhook", translate("Webhook"),translate("企业微信机器人 Webhook")..",只输入key=后面的即可
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/ent_wechat.json") - -a=s:taboption("basic", Value,"pp_token",translate('PushPlus Token'), translate("PushPlus Token").."
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushplus.json") - -a=s:taboption("basic", ListValue,"pp_channel",translate('PushPlus Channel')) -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushplus.json") -a:value("wechat",translate("wechat:PushPlus微信公众号")) -a:value("cp",translate("cp:企业微信应用")) -a:value("webhook",translate("webhook:第三方webhook")) -a:value("sms",translate("sms:短信")) -a:value("mail",translate("mail:邮箱")) -a.description = translate("第三方webhook:企业微信、钉钉、飞书、server酱
sms短信/mail邮箱:PushPlus暂未开放
具体channel设定参见:点击这里") - -a=s:taboption("basic", Value,"pp_webhook",translate('PushPlus Custom Webhook'), translate("PushPlus 自定义Webhook").."
第三方webhook或企业微信调用
具体自定义Webhook设定参见:点击这里

") -a.rmempty = true -a:depends("pp_channel","cp") -a:depends("pp_channel","webhook") - -a=s:taboption("basic", Flag,"pp_topic_enable",translate("PushPlus 一对多推送")) -a.default=0 -a.rmempty = true -a:depends("pp_channel","wechat") - -a=s:taboption("basic", Value,"pp_topic",translate('PushPlus Topic'), translate("PushPlus 群组编码").."
一对多推送时指定的群组编码
具体群组编码Topic设定参见:点击这里

") -a.rmempty = true -a:depends("pp_topic_enable","1") - -a=s:taboption("basic", Value,"pushdeer_key",translate('PushDeer Key'), translate("PushDeer Key").."
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushdeer.json") - -a=s:taboption("basic", Flag,"pushdeer_srv_enable",translate("自建 PushDeer 服务器")) -a.default=0 -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/pushdeer.json") - -a=s:taboption("basic", Value,"pushdeer_srv",translate('PushDeer Server'), translate("PushDeer 自建服务器地址").."
如https://your.domain:port
具体自建服务器设定参见:点击这里

") -a.rmempty = true -a:depends("pushdeer_srv_enable","1") - -a=s:taboption("basic", Value,"fs_webhook",translate('WebHook'), translate("飞书 WebHook").."
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/feishu.json") - -a=s:taboption("basic", Value,"bark_token",translate('Bark Token'), translate("Bark Token").."
调用代码获取点击这里

") -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/bark.json") - -a=s:taboption("basic", Flag,"bark_srv_enable",translate("自建 Bark 服务器")) -a.default=0 -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/bark.json") - -a=s:taboption("basic", Value,"bark_srv",translate('Bark Server'), translate("Bark 自建服务器地址").."
如https://your.domain:port
具体自建服务器设定参见:点击这里

") -a.rmempty = true -a:depends("bark_srv_enable","1") - -a=s:taboption("basic", Value,"bark_sound",translate('Bark Sound'), translate("Bark 通知声音").."
如silence.caf
具体设定参见:点击这里

") -a.rmempty = true -a.default = "silence.caf" -a:depends("jsonpath","/usr/bin/pushbot/api/bark.json") - -a=s:taboption("basic", Flag,"bark_icon_enable",translate(" Bark 通知图标")) -a.default=0 -a.rmempty = true -a:depends("jsonpath","/usr/bin/pushbot/api/bark.json") - -a=s:taboption("basic", Value,"bark_icon",translate('Bark Icon'), translate("Bark 通知图标").."(仅 iOS15 或以上支持)
如http://day.app/assets/images/avatar.jpg
具体设定参见:点击这里

") -a.rmempty = true -a.default = "http://day.app/assets/images/avatar.jpg" -a:depends("bark_icon_enable","1") - -a=s:taboption("basic", Value,"bark_level",translate('Bark Level'), translate("Bark 时效性通知").."
可选参数值:
active:不设置时的默认值,系统会立即亮屏显示通知。
timeSensitive:时效性通知,可在专注状态下显示通知。
passive:仅将通知添加到通知列表,不会亮屏提醒。") -a.rmempty = true -a.default = "active" -a:depends("jsonpath","/usr/bin/pushbot/api/bark.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/bin/pushbot/api/diy.json") -end -a.write = function(self, section, value) - fs.writefile("/usr/bin/pushbot/api/diy.json", value:gsub("\r\n", "\n")) -end -a:depends("jsonpath","/usr/bin/pushbot/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/bin/pushbot/pushbot test &") -end - -a=s:taboption("basic", Value,"device_name",translate('本设备名称')) -a.rmempty = true -a.description = translate("在推送信息标题中会标识本设备名称,用于区分推送信息的来源设备") - -a=s:taboption("basic", Value,"sleeptime",translate('检测时间间隔')) -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.36m 原始数据,处理后完整版约 1.2M,简化版约 250kb
若无梯子,请勿使用网络查询") - -a=s:taboption("basic", Flag,"oui_dir",translate("下载到内存")) -a.rmempty = true -a:depends("oui_data","1") -a:depends("oui_data","2") -a.description = translate("懒得做自动更新了,下载到内存中,重启会重新下载
若无梯子,还是下到机身吧") - -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("
请输入设备 MAC 和设备别名,用“-”隔开,如:
XX:XX:XX:XX:XX:XX-我的手机") - ---设备状态 -a=s:taboption("content", ListValue,"pushbot_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({pushbot_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("
一般选择 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/bin/pushbot/api/ipv4.list") -end -a.write = function(self, section, value) - fs.writefile("/usr/bin/pushbot/api/ipv4.list", value:gsub("\r\n", "\n")) -end -a.description = translate("
会因服务器稳定性、连接频繁等原因导致获取失败
如接口可以正常获取 IP,不推荐使用
从以上列表中随机地址访问") -a:depends({pushbot_ipv4="2"}) - -a=s:taboption("content", ListValue,"pushbot_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({pushbot_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("
一般选择 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/bin/pushbot/api/ipv6.list") -end -a.write = function(self, section, value) - fs.writefile("/usr/bin/pushbot/api/ipv6.list", value:gsub("\r\n", "\n")) -end -a.description = translate("
会因服务器稳定性、连接频繁等原因导致获取失败
如接口可以正常获取 IP,不推荐使用
从以上列表中随机地址访问") -a:depends({pushbot_ipv6="2"}) - -a=s:taboption("content", Flag,"pushbot_up",translate("设备上线通知")) -a.default=1 -a.rmempty = true - -a=s:taboption("content", Flag,"pushbot_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="uinteger" -a:depends({temperature_enable="1"}) -a.description = translate("
设备报警只会在连续五分钟超过设定值时才会推送
而且一个小时内不会再提醒第二次") - -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") - ---LoginNoti -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("content", Flag,"web_login_black",translate("自动拉黑")) -a.default=0 -a.rmempty = true -a:depends("web_login_failed","1") -a:depends("ssh_login_failed","1") -a.description = translate("直到重启前都不会重置次数,请先添加白名单") - -a= s:taboption("content", Value, "ip_black_timeout", "拉黑时间(秒)") -a.default = "86400" -a.datatype="and(uinteger,min(0))" -a:depends("web_login_black","1") -a.description = translate("0 为永久拉黑,慎用
如不幸误操作,请更改设备 IP 进入 LUCI 界面清空规则") - -a=s:taboption("content", 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("content", TextValue, "ip_black_list", translate("IP 黑名单列表")) -a.optional = false -a.rows = 8 -a.wrap = "soft" -a.cfgvalue = function(self, section) - return fs.readfile("/usr/bin/pushbot/api/ip_blacklist") -end -a.write = function(self, section, value) - fs.writefile("/usr/bin/pushbot/api/ip_blacklist", value:gsub("\r\n", "\n")) -end -a:depends("web_login_black","1") - ---定时推送 -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("
从 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("
使用特殊符号可能会造成发送失败") - -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") - -a=s:taboption("crontab", Value,"google_check_timeout",translate("全球互联检测超时时间")) -a.rmempty = true -a.optional = false -a.default = "10" -a.datatype = "and(uinteger,min(3))" -a.description = translate("过短的时间可能导致检测不准确") - -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/bin/pushbot/pushbot send &") -end - ---免打扰 -a=s:taboption("disturb", ListValue,"pushbot_sheep",translate("免打扰时段设置"),translate("在指定整点时间段内,暂停推送消息
免打扰时间中,定时推送也会被阻止。")) -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({pushbot_sheep="1"}) -a:depends({pushbot_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({pushbot_sheep="1"}) -a:depends({pushbot_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, "pushbot_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 视为同一用户
任一设备在线后不再推送,设备全部离线时才会推送,避免双 wifi 频繁推送") - -a = s:taboption("disturb", DynamicList, "pushbot_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 视为同一用户
任一设备在线后不再推送,设备全部离线时才会推送,避免双 wifi 频繁推送") - -a = s:taboption("disturb", ListValue, "pushbot_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 diff --git a/luci-app-pushbot/luasrc/view/pushbot/pushbot_log.htm b/luci-app-pushbot/luasrc/view/pushbot/pushbot_log.htm deleted file mode 100644 index 42b1e52e..00000000 --- a/luci-app-pushbot/luasrc/view/pushbot/pushbot_log.htm +++ /dev/null @@ -1,33 +0,0 @@ -<% -local dsp = require "luci.dispatcher" --%> - - -
- <%:自动刷新%> - - -
diff --git a/luci-app-pushbot/luasrc/view/pushbot/pushbot_status.htm b/luci-app-pushbot/luasrc/view/pushbot/pushbot_status.htm deleted file mode 100644 index a60c1967..00000000 --- a/luci-app-pushbot/luasrc/view/pushbot/pushbot_status.htm +++ /dev/null @@ -1,22 +0,0 @@ - - -
-

- <%:Collecting data...%> -

-
diff --git a/luci-app-pushbot/root/etc/config/pushbot b/luci-app-pushbot/root/etc/config/pushbot deleted file mode 100644 index 89af5a7a..00000000 --- a/luci-app-pushbot/root/etc/config/pushbot +++ /dev/null @@ -1,10 +0,0 @@ - -config pushbot 'pushbot' - option pushbot_enable '0' - option sleeptime '60' - option pushbot_ipv6 '0' - option pushbot_up '1' - option pushbot_down '1' - option cpuload_enable '1' - option cpuload '2' - option temperature_enable '0' diff --git a/luci-app-pushbot/root/etc/init.d/pushbot b/luci-app-pushbot/root/etc/init.d/pushbot deleted file mode 100755 index 915ee989..00000000 --- a/luci-app-pushbot/root/etc/init.d/pushbot +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 -STOP=10 - -start() { - state=`pgrep -f "/usr/bin/pushbot/pushbot"` - if [ ! -z "$state" ]; then - restart - else - /usr/bin/pushbot/pushbot & - fi - echo "pushbot is starting now ..." -} - -stop() { - kill -9 `pgrep -f "/usr/bin/pushbot/pushbot"` 2>/dev/null - echo "pushbot exit ..." -} - -restart(){ - stop - sleep 1 - start - echo "restarted." -} diff --git a/luci-app-pushbot/root/etc/uci-defaults/luci-pushbot b/luci-app-pushbot/root/etc/uci-defaults/luci-pushbot deleted file mode 100755 index b29a280f..00000000 --- a/luci-app-pushbot/root/etc/uci-defaults/luci-pushbot +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -uci -q batch <<-EOF >/dev/null - delete ucitrack.@pushbot[-1] - add ucitrack pushbot - set ucitrack.@pushbot[-1].init=pushbot - commit ucitrack -EOF - -rm -rf /tmp/luci-* -exit 0 diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/bark.json b/luci-app-pushbot/root/usr/bin/pushbot/api/bark.json deleted file mode 100644 index 8d1f73ab..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/bark.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "_api": "这是Bark推送 post 模板信息 api 文件", - "_api": "【Bark推送】", - - "url": "${bark_srv}/push", - "data": "@${tempjsonpath}", - "content_type": "Content-Type: application/json; charset=utf-8", - "str_title_start": "【", - "str_title_end": "】", - "str_linefeed": "\\n", - "str_splitline": "\\n\\n", - "str_space": " ", - "str_tab": " ", - "table_tab": "", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "", - "type": { - "device_key": "\"${bark_token}\"", - "title": "\"${1}\"", - "body": "\"${nowtime}${str_linefeed}${2}\"", - "ext_params": { - "group": "\"${device_name}\"", - "isArchive": "1", - "icon": "\"${bark_icon}\"", - "level": "\"${bark_level}\"" - }, - "sound": "\"${bark_sound}\"" - } -} \ No newline at end of file diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/dingding.json b/luci-app-pushbot/root/usr/bin/pushbot/api/dingding.json deleted file mode 100644 index c28a90ed..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/dingding.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "_api": "这是 Pushbot:钉钉 api 文件", - "_api": "【钉钉推送】", - - "url": "\"https://oapi.dingtalk.com/robot/send?access_token=${dd_webhook}\"", - "data": "@${tempjsonpath}", - "content_type": "Content-Type:application/json", - "str_title_start": "**", - "str_title_end": "**", - "str_linefeed": "\\n\\n", - "str_splitline": "\\n\\n---\\n\\n", - "str_space": " ", - "str_tab": " ", - "table_tab": "", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "**", - "type": - { - "msgtype": "\"markdown\"", - "markdown": { - "title": "\"${1}\"", - "text": "\"${str_title_start}${font_purple}${1}${font_end}${str_title_end}${str_linefeed}${nowtime}${str_linefeed}${2}${str_linefeed}${font_purple}${1}${font_end}\"" - } - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/diy.json b/luci-app-pushbot/root/usr/bin/pushbot/api/diy.json deleted file mode 100644 index c22f0bc7..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/diy.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "_//": "-------------------------------------------------------------------------------", - "_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": "type 对象因为需要转义变量,前后必须使用 斜杠+双引号 转义", - "_readme": "参照上文说明,填写下文相关参数" - }, - "_//": "-------------------------------------------------------------------------------", - - "url": "https://api.telegram.org/bot${tg_token}/sendMessage", - "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": " ", - "table_tab": "", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "25", - "boldstar": "**", - "type": - { - "text":"\"${str_title_start}${1}${str_title_end}${str_splitline}${nowtime}${2}\"", - "chat_id":"\"${chat_id}\"", - "parse_mode":"\"HTML\"" - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/ent_wechat.json b/luci-app-pushbot/root/usr/bin/pushbot/api/ent_wechat.json deleted file mode 100644 index 14d3ea74..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/ent_wechat.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "_api": "这是企业微信 markdown 模板信息 api 文件", - "_api": "【企业微信】", - - "url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${we_webhook}", - "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": " ", - "table_tab": "", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "**", - "type": - { - "msgtype": "\"markdown\"", - "markdown": { - "title": "\"${1}\"", - "content": "\"${str_title_start}${font_purple}${1}${font_end}${str_title_end}${str_linefeed}${nowtime}${str_linefeed}${2}\"" - } - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/feishu.json b/luci-app-pushbot/root/usr/bin/pushbot/api/feishu.json deleted file mode 100644 index 0d16bf93..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/feishu.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "_api": "这是飞书推送 post 模板信息 api 文件", - "_api": "【飞书推送】", - - "url": "https://open.feishu.cn/open-apis/bot/v2/hook/${fs_webhook}", - "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": " ", - "table_tab": "", - "font_green": "", - "font_green": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "**", - "type": - { - "msg_type": "\"interactive\"", - "card": { - "config": { - "wide_screen_mode": "true" - }, - "header": { - "template": "\"purple\"", - "title": { - "content": "\"${1}\"", - "tag": "\"plain_text\"" - } - }, - "elements": [ - { - "tag": "\"div\"", - "text": { - "content": "\"${nowtime}${str_linefeed}${2}\"", - "tag": "\"lark_md\"" - } - }, - { - "tag": "\"hr\"" - }, - { - "elements": [ - { - "content": "\"来自${device_name}\"", - "tag": "\"lark_md\"" - } - ], - "tag": "\"note\"" - } - ] - } - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/ip_blacklist b/luci-app-pushbot/root/usr/bin/pushbot/api/ip_blacklist deleted file mode 100644 index 8b137891..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/ip_blacklist +++ /dev/null @@ -1 +0,0 @@ - diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/ipv4.list b/luci-app-pushbot/root/usr/bin/pushbot/api/ipv4.list deleted file mode 100644 index 72081532..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/ipv4.list +++ /dev/null @@ -1,6 +0,0 @@ -www.cip.cc -ipv4.ddnspod.com -ifcfg.cn -speed.neu.edu.cn/getIP.php -ddns.oray.com/checkip -www.net.cn/static/customercare/yourip.asp diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/ipv6.list b/luci-app-pushbot/root/usr/bin/pushbot/api/ipv6.list deleted file mode 100644 index 5f1ca230..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/ipv6.list +++ /dev/null @@ -1,5 +0,0 @@ -ip.sb -ipv6.ddnspod.com -api-ipv6.ip.sb/ip -speed.neu6.edu.cn/getIP.php -v6.myip.la/json diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json b/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json deleted file mode 100644 index 441d7d30..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/pushdeer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "_api": "这是 PushDeer推送 api 文件", - "_api": "【PushDeer推送】", - - "url": "${pushdeer_srv}/message/push", - "data": "@${tempjsonpath}", - "content_type": "Content-Type:application/json", - "str_title_start": "**【", - "str_title_end": "】**", - "str_linefeed": "\\n\\n", - "str_splitline": "\\n\\n---\\n\\n", - "str_space": " ", - "str_tab": " ", - "table_tab": "┋", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "**", - "type": - { - "pushkey": "\"${pushdeer_key}\"", - "type": "\"markdown\"", - "text": "\"${1}\"", - "desp": "\"${nowtime}${str_linefeed}${2}\"" - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/api/pushplus.json b/luci-app-pushbot/root/usr/bin/pushbot/api/pushplus.json deleted file mode 100644 index 855472ff..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/api/pushplus.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "_api": "这是 Pushbot: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\\n", - "str_splitline": "\\n----\\n", - "str_space": " ", - "str_tab": " ", - "table_tab": "", - "font_green": "", - "font_green2": "", - "font_red": "", - "font_blue": "", - "font_purple": "", - "font_end": "", - "font_end2": "", - "percentsym": "", - "boldstar": "", - "type": - { - "token": "\"${pp_token}\"", - "channel": "\"${pp_channel}\"", - "webhook": "\"${pp_webhook}\"", - "topic": "\"${pp_topic}\"", - "title": "\"${1}\"", - "content": "\"${2}\"", - "template": "\"markdown\"" - } -} diff --git a/luci-app-pushbot/root/usr/bin/pushbot/pushbot b/luci-app-pushbot/root/usr/bin/pushbot/pushbot deleted file mode 100755 index 7c9b7858..00000000 --- a/luci-app-pushbot/root/usr/bin/pushbot/pushbot +++ /dev/null @@ -1,1274 +0,0 @@ -#!/bin/sh - -# 读取设置文件 -function get_config(){ - while [[ "$*" != "" ]]; do - eval ${1}='`uci get pushbot.pushbot.$1`' 2>/dev/null - shift - done -} - -# 初始化设置信息 -function read_config(){ - get_config "pushbot_enable" "lite_enable" "device_name" "sleeptime" "oui_dir" "oui_data" "reset_regularly" "debuglevel" "device_aliases" \ - "pushbot_ipv4" "ipv4_interface" "pushbot_ipv6" "ipv6_interface" "pushbot_up" "pushbot_down" "cpuload_enable" "cpuload" "temperature_enable" "temperature" \ - "regular_time" "regular_time_2" "regular_time_3" "interval_time" \ - "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ - "web_logged" "ssh_logged" "web_login_failed" "ssh_login_failed" "login_max_num" "web_login_black" "ip_white_list" "ip_black_timeout" \ - "pushbot_sheep" "starttime" "endtime" "pushbot_whitelist" "pushbot_blacklist" "pushbot_interface" "MAC_online_list" "MAC_offline_list" \ - "up_timeout" "down_timeout" "timeout_retry_count" "thread_num" "soc_code" "pve_host" "pve_port"\ - "err_enable" "err_sheep_enable" "err_device_aliases" "network_err_event" "system_time_event" "autoreboot_time" "network_restart_time" "public_ip_event" "public_ip_retry_count" \ - "jsonpath" "dd_webhook" "we_webhook" "pp_token" "pp_channel" "pp_webhook" "pp_topic_enable" "pp_topic" "fs_webhook" "pushdeer_key" "pushdeer_srv_enable" "pushdeer_srv" "bark_srv_enable" "bark_srv" "bark_token" "bark_sound" "bark_icon" "bark_icon_enable" "bark_level" - - for str_version in "wrtbwmon" "iputils-arping" "curl" "iw"; do - eval `echo ${str_version:0:2}"_version"`=`opkg list-installed|grep -w ^${str_version}|awk '{print $3}'` 2>/dev/null - done - dir="/tmp/pushbot/" && mkdir -p ${dir} - tempjsonpath="/tmp/pushbot/temp.json" - ip_blacklist_path="/usr/bin/pushbot/api/ip_blacklist" - [ ! -z "$oui_dir" ] && [ "$oui_dir" -eq "1" ] && oui_base="${dir}oui_base.txt" || oui_base="/usr/bin/pushbot/oui_base.txt" - debuglevel=`echo "$debuglevel"` && [ -z "$debuglevel" ] && logfile="/dev/null" || logfile="${dir}pushbot.log" - pushbot_blacklist=`echo "$pushbot_blacklist"|sed 's/ /\n/g'` 2>/dev/null - pushbot_whitelist=`echo "$pushbot_whitelist"|sed 's/ /\n/g'` 2>/dev/null - device_aliases=`echo "$device_aliases"|sed 's/ /\n/g'|sed 's/-/ /'` 2>/dev/null - err_device_aliases=`echo "$err_device_aliases"|sed 's/ /\n/g'` 2>/dev/null - client_usage_whitelist=`echo "$client_usage_whitelist"|sed 's/ /\n/g'` 2>/dev/null - ip_white_list=`echo "$ip_white_list"|sed 's/ /\n/g'` 2>/dev/null - mark_mac_list="${MAC_online_list} ${MAC_offline_list}" - mark_mac_list=`echo "$mark_mac_list"|sed 's/ /\n/g'|sed 's/-/ /'` 2>/dev/null - ipv4_urllist=`cat /usr/bin/pushbot/api/ipv4.list` 2>/dev/null - ipv6_urllist=`cat /usr/bin/pushbot/api/ipv6.list` 2>/dev/null - [ -z "$pushbot_ipv4" ] && pushbot_ipv4=0 - [ -z "$pushbot_ipv6" ] && pushbot_ipv6=0 - [ "$iw_version" ] && wlan_interface=`iw dev|grep Interface|awk '{print $2}'` >/dev/null 2>&1 - [ -z "$up_timeout" ] || [ "$up_timeout" -eq "0" ] && up_timeout="2" - [ -z "$down_timeout" ] || [ "$down_timeout" -eq "0" ] && down_timeout="20";down_timeout=`expr ${down_timeout} / 2 + 1` - [ -z "$timeout_retry_count" ] && timeout_retry_count="2";[ "$timeout_retry_count" -eq "0" ] && timeout_retry_count="1" - [ ! -z "$bark_token" ] && [ -z "$bark_srv" ] && bark_srv="https://api.day.app" - [ ! -z "$pushdeer_key" ] && [ -z "$pushdeer_srv" ] && pushdeer_srv="https://api2.pushdeer.com" - -# 字符串读取 - str_title_start=`/usr/bin/jq -r '.str_title_start' ${jsonpath}` - str_title_end=`/usr/bin/jq -r '.str_title_end' ${jsonpath}` - str_linefeed=`/usr/bin/jq -r '.str_linefeed' ${jsonpath}` - str_splitline=`/usr/bin/jq -r '.str_splitline' ${jsonpath}` - str_space=`/usr/bin/jq -r '.str_space' ${jsonpath}` - str_tab=`/usr/bin/jq -r '.str_tab' ${jsonpath}` - font_red=`/usr/bin/jq -r '.font_red' ${jsonpath}` - font_green=`/usr/bin/jq -r '.font_green' ${jsonpath}` - font_green2=`/usr/bin/jq -r '.font_green2' ${jsonpath}` - font_blue=`/usr/bin/jq -r '.font_blue' ${jsonpath}` - font_purple=`/usr/bin/jq -r '.font_purple' ${jsonpath}` - font_end=`/usr/bin/jq -r '.font_end' ${jsonpath}` - font_end2=`/usr/bin/jq -r '.font_end2' ${jsonpath}` - percentsym=`/usr/bin/jq -r '.percentsym' ${jsonpath}` - boldstar=`/usr/bin/jq -r '.boldstar' ${jsonpath}` - table_tab=`/usr/bin/jq -r '.tabletab' ${jsonpath}` - ( echo "$lite_enable"|grep -q "content" ) && str_title_start="" && str_title_end="" && str_splitline="" && str_linefeed="" && str_tab="" -} - - - -# 初始化 -function pushbot_init(){ - enable_detection - if [ -f "/usr/bin/pushbot/errlog" ]; then - cat /usr/bin/pushbot/errlog > ${logfile} - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】载入上次重启前日志" >> ${logfile} - echo "--------------------------------------------------------" >> ${logfile} - fi - down_oui & - deltemp - get_syslog - add_ip_black - - rm -f ${dir}fd1 ${dir}sheep_usage ${dir}old_sheep_usage ${dir}client_usage_aliases ${dir}old_client_usage_aliases /usr/bin/pushbot/errlog >/dev/null 2>&1 - [ ! -f "/usr/sbin/wrtbwmon" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】未安装 wrtbwmon ,流量统计不可用" >> ${logfile} - [ -z "$ip_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 iputils-arping 版本号,请确认插件是否正常运行" >> ${logfile} - [ -z "$cu_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 curl 版本号,请确认插件是否正常运行" >> ${logfile} - [ -z "${dd_webhook}${pp_token}${we_webhook}${fs_webhook}${bark_token}${pushdeer_key}" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】请填写正确的Token/Webhook " >> ${logfile} && return 1 - local interfacelist=`getinterfacelist` && [ -z "$interfacelist" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法正确获取接口信息,请确认插件是否正常运行" >> ${logfile} - return 0 -} - -# 推送 -function diy_send(){ - ( ! echo "$lite_enable"|grep -q "content" ) && ( ! echo "$lite_enable"|grep -q "nowtime" ) && local nowtime=`date "+%Y-%m-%d %H:%M:%S"` - local diyurl=`/usr/bin/jq -r .url ${3}` && local diyurl=`eval echo ${diyurl}` - local type=`/usr/bin/jq -r '.type' ${3}` && local type=`eval echo ${type}` - local data=`/usr/bin/jq -r '.data' ${3}` && local data=`eval echo ${data}` - local content_type=`/usr/bin/jq -r '.content_type' ${3}` - /usr/bin/jq ".type + $type" ${jsonpath} > ${tempjsonpath} - /usr/bin/jq -r '.[]' ${tempjsonpath}|grep -w "null" && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】参数值错误,请检查设置项 `/usr/bin/jq -r '.' ${tempjsonpath}|grep "null"`" >> ${logfile} && return 1 - [ -f ${tempjsonpath} ] && local logrow=$(grep -c "" ${tempjsonpath}) || local logrow="0" - [ $logrow -eq "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】json 文件生成失败,请检查文件格式" >> ${logfile} && return 1 - /usr/bin/jq -r '.[]' ${tempjsonpath}|grep "null" && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】参数变量生成失败,请检查设置项 `/usr/bin/jq -r '.' ${tempjsonpath}|grep "null"`" >> ${logfile} - - curl -X POST -H "$content_type" -d "${data}" "${diyurl}" -} - -# 下载设备MAC厂商信息 -function down_oui(){ - [ -f ${oui_base} ] && local logrow=$(grep -c "" ${oui_base}) || local logrow="0" - [ $logrow -lt "10" ] && rm -f ${oui_base} >/dev/null 2>&1 - if [ ! -z "$oui_data" ] && [ "$oui_data" -ne "3" ] && [ ! -f ${oui_base} ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】设备MAC厂商信息不存在,重新下载" >> ${logfile} - wget --no-check-certificate -t 3 -T 15 -O ${dir}oui.txt https://standards-oui.ieee.org/oui/oui.txt >/dev/null 2>&1 - if [ -f ${dir}oui.txt ] && [ "$oui_data" -eq "1" ]; then - cat ${dir}oui.txt|grep "base 16"|grep -i "apple\|aruba\|asus\|autelan\|belkin\|bhu\|buffalo\|cctf\|cisco\|comba\|datang\|dell\|dlink\|dowell\|ericsson\|fast\|feixun\|\ -fiberhome\|fujitsu\|grentech\|h3c\|hisense\|hiwifi\|honghai\|honghao\|hp\|htc\|huawei\|intel\|jinli\|jse\|lenovo\|lg\|liteon\|malata\|meizu\|mercury\|meru\|moto\|netcore\|\ -netgear\|nokia\|omron\|oneplus\|oppo\|philips\|router_unkown\|samsung\|shanzhai\|sony\|start_net\|sunyuanda\|tcl\|tenda\|texas\|tianyu\|tp-link\|ubq\|undefine\|VMware\|\ -utstarcom\|volans\|xerox\|xiaomi\|zdc\|zhongxing\|smartisan" > ${oui_base} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】设备MAC厂商信息下载成功" >> ${logfile} || echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备MAC厂商信息下载失败" >> ${logfile} - fi - if [ -f ${dir}oui.txt ] && [ "$oui_data" -eq "2" ]; then - cat ${dir}oui.txt|grep "base 16" > ${oui_base} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】设备MAC厂商信息下载成功" >> ${logfile} || echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备MAC厂商信息下载失败" >> ${logfile} - fi - rm -f ${dir}oui.txt >/dev/null 2>&1 - fi -} - -# 清理临时文件 -function deltemp(){ - unset title content ipAddress_logrow online_list online_mac mac_online_status - rm -f ${dir}title ${dir}content ${dir}tmp_downlist ${dir}send_enable.lock ${tempjsonpath} >/dev/null 2>&1 - LockFile unlock - [ -f ${logfile} ] && local logrow=$(grep -c "" ${logfile}) || local logrow="0" - [ $logrow -gt 500 ] && sed -i '1,100d' ${logfile} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【清理】日志超出上限,删除前 100 条" >> ${logfile} -} - -# 检测程序开关 -function enable_detection(){ - [ ! "$1" ] && local time_n=1 - for i in `seq 1 $time_n`; do - get_config pushbot_enable;[ -z "$pushbot_enable" ] || [ "$pushbot_enable" -eq "0" ] && `/etc/init.d/pushbot stop` || sleep 1 - done -} - -# 获取 ip -function getip(){ - [ ! "$1" ] && return - if [ $1 == "wanipv4" ] ;then - [ ! -z "$ipv4_interface" ] && local wanIP=$(/sbin/ifconfig ${ipv4_interface}|awk '/inet addr/ {print $2}'|awk -F: '{print $2}'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - [ -z "$ipv4_interface" ] && local wanIP=$(getinterfacelist|grep '\"address\"'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - echo "$wanIP" - elif [ $1 == "hostipv4" ] ;then - function get_hostipv4() - { - local url_number=`echo "$ipv4_urllist"|wc -l` - local ipv4_URL=`echo "$ipv4_urllist"| sed -n "$(rand 1 $url_number)p"|sed -e 's/\r//g'` - [ ! -z "$ipv4_interface" ] && local hostIP=$(curl -k -s -4 --interface ${ipv4_interface} -m 5 ${ipv4_URL}) || local hostIP=$(curl -k -s -4 -m 5 ${ipv4_URL}) - echo $hostIP|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|head -n1 - } - local hostIP=`get_hostipv4` - [ -z "$hostIP" ] && local hostIP=`get_hostipv4` - [ -z "$hostIP" ] && local hostIP=`get_hostipv4` - echo $hostIP # 重试,偷懒,有空再优化 - elif [ $1 == "wanipv6" ] ;then - [ ! -z "$ipv6_interface" ] && local wanIPv6=$(ip addr show ${ipv6_interface}|grep -v deprecated|grep -A1 'inet6 [^f:]'|sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;ta'|sort -nr|head -n1|awk '{print $2}') - [ -z "$ipv6_interface" ] && local wanIPv6=$(ip addr show|grep -v deprecated|grep -A1 'inet6 [^f:]'|sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;ta'|sort -nr|head -n1|awk '{print $2}') - echo "$wanIPv6" - elif [ $1 == "hostipv6" ] ;then - function get_hostipv6() - { - local urlv6_number=`echo "$ipv6_urllist"|wc -l` - local ipv6_URL=`echo "$ipv6_urllist"| sed -n "$(rand 1 $urlv6_number)p"|sed -e 's/\r//g'` - [ ! -z "$ipv6_interface" ] && local hostIPv6=$(curl -k -s -6 --interface ${ipv6_interface} -m 5 ${ipv6_URL}) || local hostIPv6=$(curl -k -s -6 -m 5 ${ipv6_URL}) - echo $hostIPv6|grep -oE '([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}'|head -n1 - } - local hostIPv6=`get_hostipv6` - [ -z "$hostIPv6" ] && local hostIPv6=`get_hostipv6` - [ -z "$hostIPv6" ] && local hostIPv6=`get_hostipv6` - echo $hostIPv6 # 重试,偷懒,有空再优化 - fi -} - -# 获取接口信息 -function getinterfacelist(){ - [ `ubus list|grep -w -i "network.interface.wan"|wc -l` -ge "1" ] && ubus call network.interface.wan status && return - [ `ubus list|grep -i "network.interface."|grep -v "loopback"|grep -v "wan6"|wc -l` -eq "1" ] && ubus call `ubus list|grep "network.interface."|grep -v "loopback"` status && return -} - -# 获取接口在线时间 -function getinterfaceuptime(){ - getinterfacelist|grep \"uptime\"|sed $'s/\"uptime": //g'|sed $'s/\,//g' -} - -# 查询 mac 地址 -function getmac(){ - ( echo "$tmp_mac"|grep -q "unknown" ) && unset tmp_mac # 为unknown时重新读取 - [ -f "${dir}ipAddress" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - [ -f "${dir}tmp_downlist" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - [ -f "/var/dhcp.leases" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat /var/dhcp.leases|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - [ -z "$tmp_mac" ] && local tmp_mac=`cat /proc/net/arp|grep "0x2\|0x6"|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - [ -z "$tmp_mac" ] && local tmp_mac="unknown" - echo "$tmp_mac" -} - -# 查询主机名 -function getname(){ - [ -z "$tmp_name" ] && local tmp_name=`echo "$device_aliases"|grep -i $2|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - [ -f "${dir}ipAddress" ] && [ -z "$tmp_name" ] && local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - [ -f "${dir}tmp_downlist" ] && [ -z "$tmp_name" ] && local tmp_name=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - ( ! echo "$tmp_name"|grep -q -w "unknown\|*" ) && [ ! -z "$tmp_name" ] && echo "$tmp_name" && return || unset tmp_name # 为unknown时重新读取 - [ -f "/var/dhcp.leases" ] && [ -z "$tmp_name" ] && local tmp_name=`cat /var/dhcp.leases|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - ( ! echo "$tmp_name"|grep -q -w "unknown\|*" ) && [ ! -z "$tmp_name" ] && echo "$tmp_name" && return || unset tmp_name # 为unknown时重新读取 - [ -z "$dhcp_config" ] && dhcp_config=`uci show dhcp|grep "ip\|mac\|name"` - for dhcp_config_str in "host" "domain"; do - local dhcp_ip_n=`echo "$dhcp_config"|grep -w ^dhcp.@${dhcp_config_str}.*ip=.${1}|sed -nr 's#^dhcp.(.*).ip.*#\1#gp'` 2>/dev/null - [ ! -z "$dhcp_ip_n" ] && [ -z "$tmp_name" ] && local tmp_name=`uci get dhcp.${dhcp_ip_n}.name` 2>/dev/null - local dhcp_mac_n=`echo "$dhcp_config"|grep -i ^dhcp.@${dhcp_config_str}.*mac=.${2}|sed -nr 's#^dhcp.(.*).mac.*#\1#gp'` 2>/dev/null - [ ! -z "$dhcp_mac_n" ] && [ -z "$tmp_name" ] && local tmp_name=`uci get dhcp.${dhcp_ip_n}.name` 2>/dev/null - [ ! -z "$tmp_name" ] && break - done - ( ! echo "$tmp_name"|grep -q -w "unknown\|*" ) && [ ! -z "$tmp_name" ] && echo "$tmp_name" && return || unset tmp_name # 为unknown时重新读取 - [ -f "$oui_base" ] && local tmp_name=$(cat $oui_base|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp'|sed 's/ /_/g') - [ ! -z "$oui_data" ] && [ "$oui_data" -eq "4" ] && local tmp_name=$(curl -sS "https://standards-oui.ieee.org/oui/oui.txt"|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp'|sed 's/ /_/g') - [ -z "$tmp_name" ] && local tmp_name="unknown" - echo "$tmp_name" -} - -# 查询设备接口 -function getinterface(){ - [ -f "${dir}ipAddress" ] && local ip_interface=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` - [ -f "${dir}tmp_downlist" ] && [ -z "$ip_interface" ] && local ip_interface=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` - if [ -z "$ip_interface" ] && [ ! -z "$wlan_interface" ]; then - for interface in $wlan_interface; do - local ip_interface=`iw dev $interface station dump 2>/dev/null|grep Station|grep -i -w ${1}|sed -nr 's#^.*on (.*))#\1#gp'` >/dev/null 2>&1 - [ ! -z "$ip_interface" ] && echo "$ip_interface" && return - done - fi - [ -z "$ip_interface" ] && local ip_interface=`cat /proc/net/arp|grep "0x2\|0x6"|grep -i -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1` - echo "$ip_interface" -} - -# ping -function getping(){ - [ "$iw_version" ] && local wlan_online=`iw dev ${ip_interface} station dump|grep -i -w ${ip_mac}|grep Station` >/dev/null 2>&1 - [ "$wlan_online" ] && return 0 - for i in `seq 1 ${3}`; do - ( ! echo "$ip_ms"|grep -q "ms" ) && local ip_ms=$( arping -I `cat /proc/net/arp|grep -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1` -c 20 -f -w ${2} $1 ) 2>/dev/null - ( ! echo "$ip_ms"|grep -q "ms" ) && local ip_ms=`ping -c 5 -w ${2} ${1}|grep -v '100% packet loss'` 2>/dev/null - ( ! echo "$ip_ms"|grep -q "ms" ) && sleep 1 - done - ( echo "$ip_ms"|grep -q "ms" ) -} - -# CPU 占用率 -function getcpu(){ - local AT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') - sleep 3 - local BT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') - printf "%.01f%%" $(echo ${AT} ${BT}|awk '{print (($4-$2)/($3-$1))*100}') -} - -# 获取SOC温度 (取所有传感器温度最大值) -function soc_temp(){ - [ -z "$soc_code" ] && local soctemp=`sensors 2>/dev/null|grep °C|sed -nr 's#^.*:.*\+(.*)°C .*#\1#gp'|sort -nr|head -n1` - [ -z "$soc_code" ] && [ -z "$soctemp" ] && local soctemp=`cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null|sort -nr|head -n1|cut -c-2` - [ "$soc_code" == "pve" ] && [ ! -z "$pve_host" ] && local soctemp=`ssh -i ~/.ssh/id_rsa root@${pve_host} -p ${pve_port} sensors 2>/dev/null|grep °C|sed -nr 's#^.*:.*\+(.*)°C .*#\1#gp'|sort -nr|head -n1` - [ ! -z "$soctemp" ] && echo "$soctemp" && return - [ ! -z "$soc_code" ] && eval `echo "$soc_code"` 2>/dev/null -} - -# 流量数据 -function usage(){ - [ ! -f "/usr/sbin/wrtbwmon" ] || [ ! "$1" ] && return - if [ $1 == "update" ] ;then - function version_le() { test "$(echo "$@"|tr " " "\n"|sort -n|head -n 1)" == "$1"; } - function version_ge() { test "$(echo "$@"|tr " " "\n"|sort -r|head -n 1)" == "$1"; } - [ ! -z "$wr_version" ] && ( version_ge "${wr_version}" "1.2.0" ) && wrtbwmon -f ${dir}usage.db 2>/dev/null && return - [ ! -z "$wr_version" ] && ( version_le "${wr_version}" "1.0.0" ) || [ -z "$wr_version" ] && wrtbwmon update ${dir}usage.db 2>/dev/null && return - elif [ $1 == "get" ] ;then - [ ! -f "${dir}usage.db" ] && [ ! "$3" ] && echo `bytes_for_humans 0` && return - [ ! -f "${dir}usage.db" ] && [ "$3" ] && echo 0 && return - [ -z "$total_n" ] && total_n=`cat ${dir}usage.db|head -n1|grep "total"|sed 's/,/\n/g'|awk '/total/{print NR}'` 2>/dev/null - [ -z "$total_n" ] && total_n="6" - [ "$2" ] && local tmptotal=`cat ${dir}usage.db|sed 's/,,,/,0,0,/g'|sed 's/,,/,0,/g'|sed 's/,/ /g'|grep -i -w ${2}|awk "{print "'$'$total_n"}"|grep -v "^$"|sort -u|head -n1` 2>/dev/null - [ -z "$tmptotal" ] && local tmptotal="0" - [ ! "$3" ] && echo `bytes_for_humans ${tmptotal}` || echo "$tmptotal" - elif [ $1 == "down" ] ;then - [ "$2" ] && sed -i "/,${2},/d" ${dir}usage.db 2>/dev/null - fi -} - -# 流量数据单位换算 -function bytes_for_humans { - [ ! "$1" ] && return - [ "$1" -gt 1073741824 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1073741824'}'` G" && return - [ "$1" -gt 1048576 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1048576'}'` M" && return - [ "$1" -gt 1024 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1024'}'` K" && return - echo "${1} bytes" -} - -# 设备异常流量检测 -function get_client_usage(){ - [ -z "$client_usage" ] && return - [ "$client_usage" -ne "1" ] && return - [ -z "$client_usage_max" ] && return - [ -z "$get_client_usage_time" ] && get_client_usage_time=`date +%s` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "K\|k" ) && client_usage_max=`expr ${client_usage_max%?} \* 1024` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "M\|m" ) && client_usage_max=`expr ${client_usage_max%?} \* 1048576` - ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "G\|g" ) && client_usage_max=`expr ${client_usage_max%?} \* 1073741824` - [ -z "$client_usage_disturb" ] && client_usage_disturb="0" - [ "$client_usage_disturb" -eq "0" ] && [ -f "${dir}ipAddress" ] && local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - [ "$client_usage_disturb" -eq "1" ] && [ ! -z "$client_usage_whitelist" ] && local MACLIST=`echo "$client_usage_whitelist"` - [ -z "$MACLIST" ] && return - - if [ "$((`date +%s`-$get_client_usage_time))" -ge "60" ]; then - > ${dir}client_usage_aliases - for mac in $MACLIST; do - ( ! cat ${dir}ipAddress|grep -q -i -w $mac|grep -v "^$"|sort -u|head -n1 ) && continue - echo "$mac" `usage get ${mac} bytes` >> ${dir}client_usage_aliases - [ -f "${dir}old_client_usage_aliases" ] && get_client_usage_bytes=`cat ${dir}old_client_usage_aliases|grep -i -w $mac|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` || continue - [ -z "$get_client_usage_bytes" ] && get_client_usage_bytes="0" - if [ "$((`usage get ${mac} bytes`-$get_client_usage_bytes))" -ge "$client_usage_max" ]; then - local ip=`cat ${dir}ipAddress|grep -i -w $mac|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - local ip_name=`getname ${ip} ${mac}` - local tmp_usage=$(bytes_for_humans $(expr `usage get ${mac} bytes` - ${get_client_usage_bytes})) - local time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local ip_total=`usage get $mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) - if [ -z "$title" ]; then - title="${ip_name} 流量异常" - content="${content}${str_splitline}${str_title_start}${font_red} 设备流量异常${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - elif ( echo "$title"|grep -q "流量异常" ); then - title="${ip_name} ${title}" - content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_red} 设备流量异常${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - fi - fi - done - cat ${dir}client_usage_aliases > ${dir}old_client_usage_aliases - get_client_usage_time=`date +%s` - fi -} - -# 时间单位换算 -function time_for_humans { - [ ! "$1" ] && return - if [ "$1" -lt 60 ]; then - echo "${1} 秒" - elif [ "$1" -lt 3600 ]; then - local usetime_min=`expr $1 / 60` - local usetime_sec=`expr $usetime_min \* 60` - local usetime_sec=`expr $1 - $usetime_sec` - echo "${usetime_min} 分 ${usetime_sec} 秒" - elif [ "$1" -lt 86400 ]; then - local usetime_hour=`expr $1 / 3600` - local usetime_min=`expr $usetime_hour \* 3600` - local usetime_min=`expr $1 - $usetime_min` - local usetime_min=`expr $usetime_min / 60` - echo "${usetime_hour} 小时 ${usetime_min} 分" - else - local usetime_day=`expr $1 / 86400` - local usetime_hour=`expr $usetime_day \* 86400` - local usetime_hour=`expr $1 - $usetime_hour` - local usetime_hour=`expr $usetime_hour / 3600` - echo "${usetime_day} 天 ${usetime_hour} 小时" - fi -} - -# 计算字符真实长度 -function length_str { - [ ! "$1" ] && return - local length_zh=`echo "$1"|awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` - local length_en=`echo "$1"|awk '{print gensub(/[^\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` - echo `expr $length_zh / 3 \* 2 + $length_en` -} - -# 截取字符,避免中文乱码 -function cut_str { - [ ! "$1" ] && return - [ ! "$2" ] && return - [ `length_str $1` -le "$2" ] && echo "$1" && return - local temp_length=$2 - while [ $(length_str `echo "$1"|cut -c -$temp_length`) -lt "$2" ]; do - temp_length=`expr $temp_length + 1` - done - while [ $(printf "%d" \'`echo "$1"|cut -c $temp_length`) -ge "128" ] && [ $(printf "%d" \'`echo "$1"|cut -c $temp_length`) -lt "224" ]; do - temp_length=`expr $temp_length + 1` - done - temp_length=`expr $temp_length - 1` - echo $(echo "$1"|cut -c -$temp_length)"..." -} - -# 随机数 -function rand(){ - local min=$1 - local max=$(($2- $min + 1)) - local num=$(date +%s%N) - echo $(($num % $max + $min)) -} - -# 在线设备列表 -function pushbot_first(){ - [ -f "${dir}ipAddress" ] && local IPLIST=`cat ${dir}ipAddress|awk '{print $1}'|grep -v "^$"|sort -u` - for ip in $IPLIST; do - read -u 5 - { - down $ip - echo "" >&5 - }& - done - wait - unset ip IPLIST - local IPLIST=`cat /proc/net/arp|grep "0x2\|0x6"|awk '{print $1}'|grep -v "^169.254."|grep -v "^$"|sort -u|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'` - for ip in $IPLIST; do - read -u 5 - { - up $ip - echo "" >&5 - }& - done - wait -} -# 创建计划任务 -function pushbot_cron(){ - function del_cron(){ - ( echo `crontab -l 2>/dev/null`|grep -q "pushbot" ) && crontab -l > conf && sed -i "/pushbot/d" conf && crontab conf && rm -f conf >/dev/null 2>&1 - } - function re_cron(){ - /etc/init.d/cron stop - /etc/init.d/cron start - } - del_cron - if [ -z "$pushbot_enable" ]; then - re_cron - return - fi - - # 重置流量 - if [ ! -z "$reset_regularly" ] && [ "$reset_regularly" -eq "1" ]; then - crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm /tmp/pushbot/usage.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 - crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm /tmp/pushbot/usage6.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 - fi - [ ! -z "$regular_time_2" ] && local regular_time_2=",${regular_time_2}" - [ ! -z "$regular_time_3" ] && local regular_time_3=",${regular_time_3}" - # 定时发送 - if [ ! -z "$regular_time" ] || [ ! -z "$regular_time_2" ] || [ ! -z "$regular_time_3" ]; then - crontab -l 2>/dev/null > conf && echo -e "0 $regular_time$regular_time_2$regular_time_3 * * * /usr/bin/pushbot/pushbot send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 - # 间隔发送 - elif [ ! -z "$interval_time" ]; then - crontab -l 2>/dev/null > conf && echo -e "0 */$interval_time * * * /usr/bin/pushbot/pushbot send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 - fi - re_cron -} - -# 免打扰检测 -function pushbot_disturb(){ - [ -z "$pushbot_sheep" ] || [ -z "$starttime" ] || [ -z "$endtime" ] && return 0 - if [ `date +%H` -ge $endtime -a $starttime -lt $endtime ] || [ `date +%H` -lt $starttime -a $starttime -lt $endtime ] || [ `date +%H` -lt $starttime -a `date +%H` -ge $endtime -a $starttime -gt $endtime ]; then - unset sheep_starttime - rm -f ${dir}sheep_usage ${dir}old_sheep_usage 2>/dev/null - disturb_text=`/usr/bin/jq -r '._api' ${jsonpath}` - return 0 - else - [ -z "$sheep_starttime" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【免打扰】夜深了,该休息了" >> ${logfile} && sheep_starttime=`date +%s` - if [ "$pushbot_sheep" -eq "1" ] ;then - while [ `date +%H` -lt "$endtime" ]; do - enable_detection - sleep $sleeptime - done - elif [ "$pushbot_sheep" -eq "2" ] ;then - disturb_text="【免打扰】" - return 1 - fi - fi -} - -# 文件锁 -function LockFile(){ - if [ $1 = "lock" ] ;then - [ ! -f "${dir}pushbot.lock" ] && > ${dir}pushbot.lock && return - while [ -f "${dir}pushbot.lock" ]; do - enable_detection 1 - done - LockFile lock - fi - [ $1 = "unlock" ] && rm -f ${dir}pushbot.lock >/dev/null 2>&1 - return 0 -} - -# 检测黑白名单 -function blackwhitelist(){ - [ ! "$1" ] && return 1 - [ -z "$pushbot_whitelist" ] && [ -z "$pushbot_blacklist" ] && [ -z "$pushbot_interface" ] && [ -z "$MAC_online_list" ] && [ -z "$MAC_offline_list" ] && return 0 - [ ! -z "$pushbot_whitelist" ] && ( echo "$pushbot_whitelist"|grep -q -i -w $1 ) && return 1 - [ ! -z "$pushbot_blacklist" ] && ( ! echo "$pushbot_blacklist"|grep -q -i -w $1 ) && return 1 - [ ! -z "$pushbot_interface" ] && ( ! echo `getinterface ${1}`|grep -q -i -w $pushbot_interface ) && return 1 - [ ! -z "$MAC_online_list" ] && [ ! -z "$mac_online_status" ] && return 1 - [ ! -z "$MAC_online_list" ] && ( echo "$MAC_online_list"|grep -q -i -w $1 ) && return 1 - [ ! -z "$MAC_offline_list" ] && [ -z "$mac_online_status" ] && return 1 - return 0 -} - -function get_client(){ - if [ -f "${dir}ipAddress" ]; then - while read line; do - local js_str="${js_str}
" - local js_str="${js_str}
<%:`echo "$line"|awk '{print $3}'`%>
" - local tmp_mac=`echo "$line"|awk '{print $2}'` - local js_str="${js_str}
<%:${tmp_mac}%>
" - local js_str="${js_str}
<%:`echo "$line"|awk '{print $1}'`%>
" - local tmp_usage=`usage get ${tmp_mac}` - local js_str="${js_str}
<%:${tmp_usage}%>
" - local tmp_uptime=`echo "$line"|awk '{print $4}'` - local tmp_timenow=`date +%s` - local tmp_uptime=$(time_for_humans `expr ${tmp_timenow} - ${tmp_uptime}`) - local js_str="${js_str}
<%:${tmp_uptime}%>
" - done < ${dir}ipAddress - fi -cat>/usr/lib/lua/luci/view/pushbot/pushbot_client.htm<<%:在线设备列表%>
<%:客户端名%>
<%:MAC%>
<%:IP%>
<%:总计流量%>
<%:在线时间%>
-$js_str -
-EOF -} - -# 重启网络服务 -function network_restart(){ -cat>${dir}network_restart</dev/null 2>&1 & -/etc/init.d/firewall restart >/dev/null 2>&1 & -/etc/init.d/dnsmasq restart >/dev/null 2>&1 & -EOF - chmod 0755 ${dir}network_restart && ${dir}network_restart - rm -f ${dir}network_restart >/dev/null 2>&1 -} - -# 查看无人值守任务设备是否在线 -function geterrdevicealiases(){ - [ -z "$err_device_aliases" ] && return - [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return - local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - for mac in $MACLIST; do - [ -z "$err_mac" ] && [ ! -z "$mac" ] && local err_mac=`echo "$err_device_aliases"|grep -i $mac|grep -v "^$"|sort -u|head -n1` - done - # 进入免打扰时间已经超过一小时 - if [ ! -z "$sheep_starttime" ] && [ "$((`date +%s`-$sheep_starttime))" -ge "3600" ]; then - > ${dir}sheep_usage - local MACLIST=`echo "$err_device_aliases"|grep -v "^$"|sort -u` - for mac in $MACLIST; do - [ ! -z "$mac" ] && local tmptotal=`usage get ${mac} bytes` - [ ! -z "$tmptotal" ] && awk 'BEGIN{printf "%.0f\n",'$tmptotal'/'204800'}' 2>/dev/null >> ${dir}sheep_usage - done - old_sheep_usage=`cat ${dir}old_sheep_usage` 2>/dev/null - sheep_usage=`cat ${dir}sheep_usage` 2>/dev/null - [ "$old_sheep_usage" == "$sheep_usage" ] && [ -z "$sheep_nousage_starttime" ] && sheep_nousage_starttime=`date +%s` - [ "$old_sheep_usage" != "$sheep_usage" ] && unset sheep_nousage_starttime && cat ${dir}sheep_usage 2>/dev/null > ${dir}old_sheep_usage - [ ! -z "$sheep_nousage_starttime" ] && [ "$((`date +%s`-$sheep_nousage_starttime))" -ge "300" ] && unset err_mac - fi - [ -z "$err_mac" ] -} - -# 无人值守任务 -function unattended(){ - [ -z "$err_enable" ] || [ "$err_enable" -ne "1" ] && return - [ ! -z "$err_sheep_enable" ] && [ "$err_sheep_enable" -eq "1" ] && [ -z "$sheep_starttime" ] && return - geterrdevicealiases;[ $? -eq "1" ] && return - - if [ ! -z "$system_time_event" ]; then - local interfaceuptime=`getinterfaceuptime` - if [ ! -z "$autoreboot_time" ] && [ `cat /proc/uptime|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$autoreboot_time" ] && [ "$system_time_event" -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重启路由器咯" >> ${logfile} - cat ${logfile} > /usr/bin/pushbot/errlog - sleep 2 && reboot && exit - elif [ ! -z "$network_restart_time" ] && [ ! -z "$interfaceuptime" ] && [ `echo "$interfaceuptime"|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$network_restart_time" ] && [ "$system_time_event" -eq "2" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重新拨号咯" >> ${logfile} - ifup wan >/dev/null 2>&1 - sleep 60 - fi - fi - - [ -z "$public_ip_today" ] && public_ip_today=`date +"%d"` - [ -z "$public_ip_count" ] && public_ip_count="0" - [ $public_ip_today -ne `date +"%d"` ] && public_ip_today=`date +"%d"` && public_ip_count=1 - if [ ! -z "$public_ip_event" ] && [ ! -z "$public_ip_retry_count" ] && [ "$public_ip_count" -le "$public_ip_retry_count" ]; then - public_ip_count=`expr $public_ip_count + 1` - local wanIP=`getip wanipv4` - local hostIP=`getip hostipv4` - if [ ! -z "$wanIP" ] && [ ! -z "$hostIP" ] && ( ! echo "$wanIP"|grep -q -w ${hostIP} );then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重拨尝试获取公网 ip,当前第 $public_ip_count 次 " >> ${logfile} - ifup wan >/dev/null 2>&1 - sleep 60 - local wanIP=`getip wanipv4` && local hostIP=`getip hostipv4` - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -eq "1" ] && local IPv4=${wanIP} - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -eq "2" ] && local IPv4=${hostIP} - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -eq "1" ] && local IPv6=`getip wanipv6` - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -eq "2" ] && local IPv6=`getip hostipv6` - [ ! -z "$wanIP" ] && [ ! -z "$hostIP" ] && ( ! echo "$wanIP"|grep -q -w ${hostIP} ) && echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $last_IPv6 >> ${dir}ip - fi - fi -} - -# 检测网络状态 -function rand_geturl(){ - function getcheck(){ - local urllist="https://www.163.com https://www.qq.com https://www.baidu.com https://www.qidian.com https://www.douban.com" - local url_number=`expr $(echo "$urllist"|grep -o ' '|wc -l) + 1` - local url_str=`echo "$urllist"|awk -v i=$(rand 1 $url_number) '{print $i}'` - echo `curl -k -s -w "%{http_code}" -m 5 ${url_str} -A "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" -o /dev/null` - } - local check=`getcheck` - while [ -z "$check" ] || [ "$check" -ne "200" ]; do - local check=`getcheck` - if [ ! -z "$check" ] && [ "$check" -eq "200" ]; then - [ ! -z "$network_enable" ] && [ "$network_enable" -eq "404" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【网络状态】网络恢复正常.." >> ${logfile} - local network_enable="200" - else - [ -z "$network_enable" ] || [ "$network_enable" -eq "200" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】当前网络不通!停止检测! " >> ${logfile} - local network_enable="404" - [ -z "$network_err_time" ] && network_err_time=`date +%s` - if [ ! -z "$network_err_event" ] && [ "$((`date +%s`-$network_err_time))" -ge "600" ]; then - > ${dir}send_enable.lock && pushbot_first && deltemp - geterrdevicealiases - if [ "$?" -eq "0" ]; then - [ -f /usr/bin/pushbot/autoreboot_count ] && retry_count=`cat /usr/bin/pushbot/autoreboot_count` && rm -f /usr/bin/pushbot/autoreboot_count >/dev/null 2>&1 - [ ! -z ${retry_count} ] && retry_count=0;retry_count=`expr $retry_count + 1` - if [ "$network_err_event" -eq "1" ] ;then - if [ "$retry_count" -lt "3" ] ;then - echo "$retry_count" > /usr/bin/pushbot/autoreboot_count - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启路由,当前第 $retry_count 次 " >> ${logfile} - cat ${logfile} > /usr/bin/pushbot/errlog - sleep 2 && reboot && exit - fi - [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启路由2次,修复失败,请主人自行修复哦" >> ${logfile} - elif [ "$network_err_event" -eq "2" ] ;then - [ "$retry_count" -lt "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启网络,当前第 $retry_count 次 " >> ${logfile} && ifup wan >/dev/null 2>&1 - [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启网络2次,修复失败,请主人自行修复哦 " >> ${logfile} - elif [ "$network_err_event" -eq "3" ] ;then - if [ "$retry_count" -eq "1" ] ;then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试修复网络,当前第 1 次,重启网络服务中 " >> ${logfile} && network_restart - elif [ "$retry_count" -eq "2" ] ;then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试修复网络,当前第 2 次,关闭可能造成网络断开的软件" >> ${logfile} - [ `uci get koolproxy.@global[0].enabled 2>/dev/null` -eq "1" ] && [ `uci get koolproxy.@global[0].koolproxy_mode 2>/dev/null` -eq "1" ] && /etc/init.d/koolproxy stop >/dev/null 2>&1 - [ `uci get adbyby.@adbyby[0].enable 2>/dev/null` -eq "1" ] && [ `uci get adbyby.@adbyby[0].wan_mode 2>/dev/null` -eq "0" ] && /etc/init.d/adbyby stop >/dev/null 2>&1 - [ `uci get passwall.@global[0].enabled 2>/dev/null` -eq "1" ] && [ `uci get passwall.@global[0].proxy_mode 2>/dev/null|grep global` ] && /etc/init.d/koolproxy stop >/dev/null 2>&1 - local shadowsocksr_enabled=`uci get shadowsocksr.@global[0].global_server 2>/dev/null|grep nil` - local shadowsocksr_run_mode=`uci get shadowsocksr.@global[0].run_mode 2>/dev/null|grep all` - [ -z "$shadowsocksr_enabled" ] && [ ! -z "$shadowsocksr_run_mode" ] && /etc/init.d/shadowsocksr stop >/dev/null 2>&1 - sleep 60 && network_restart - elif [ "$retry_count" -eq "3" ] ;then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试修复网络,当前第 3 次,备份设置项,并修改相关设置" >> ${logfile} - mkdir -p /usr/bin/pushbot/configbak - cp -p -f /etc/config/network /usr/bin/pushbot/configbak/network - cp -p -f /etc/config/dhcp /usr/bin/pushbot/configbak/dhcp - cp -p -f /etc/config/firewall /usr/bin/pushbot/configbak/firewall - cp -p -f /etc/firewall.user /usr/bin/pushbot/configbak/firewall.user - uci set network.wan.peerdns='0' - uci delete network.wan.dns - uci add_list network.wan.dns='223.5.5.5' - uci add_list network.wan.dns='119.29.29.29' - uci delete network.wan.mtu - uci commit network - uci set dhcp.@dnsmasq[0].port='53' - uci set dhcp.@dnsmasq[0].resolvfile='/tmp/resolv.conf.auto' - uci delete dhcp.@dnsmasq[0].server - uci delete dhcp.@dnsmasq[0].noresolv - uci commit dhcp - uci delete firewall.redirect - >/etc/firewall.user - uci commit firewall - sleep 60 && network_restart - elif [ "$retry_count" -eq "4" ] ;then - echo "$retry_count" > /usr/bin/pushbot/autoreboot_count - cat ${logfile} > /usr/bin/pushbot/errlog - sleep 2 && reboot && exit - elif [ "$retry_count" -eq "5" ] ;then - echo "$retry_count" > /usr/bin/pushbot/autoreboot_count - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】修复失败,还原设置中,请自行检查网络设置" >> ${logfile} - cp -p -f /usr/bin/pushbot/configbak/network /etc/config/network - cp -p -f /usr/bin/pushbot/configbak/dhcp /etc/config/dhcp - cp -p -f /usr/bin/pushbot/configbak/firewall /etc/config/firewall - cp -p -f /usr/bin/pushbot/configbak/firewall.user /etc/firewall.user - cat ${logfile} > /usr/bin/pushbot/errlog - sleep 2 && reboot && exit - fi - fi - fi - elif [ -f /usr/bin/pushbot/autoreboot_count ]; then - network_err_time=`expr $network_err_time - 600` && sleep 60 - fi - enable_detection - sleep $sleeptime - fi - continue - done - rm -f /usr/bin/pushbot/autoreboot_count >/dev/null 2>&1 -} - -# 检测 ip 状况 -function ip_changes(){ - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -eq "1" ] && local IPv4=`getip wanipv4` - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -eq "2" ] && local IPv4=`getip hostipv4` - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -eq "1" ] && local IPv6=`getip wanipv6` - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -eq "2" ] && local IPv6=`getip hostipv6` - - if [ -f ${dir}ip ]; then - local last_IPv4=$(cat "${dir}ip"|grep IPv4|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) - local last_IPv6=$(cat "${dir}ip"|grep IPv6|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) - if [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -ne "0" ] && [ ! -z "$IPv4" ] && ( ! echo ${IPv4}|grep -w -q ${last_IPv4} ); then - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前IP:${IPv4}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $last_IPv6 >> ${dir}ip - title="IP 地址变化" - content="${content}${str_splitline}${str_title_start}${font_green} IP 地址变化${font_end}${str_title_end}${str_linefeed}${str_tab}当前 IP:${IPv4}" - elif [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -ne "0" ] && [ -z "$IPv4" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】获取 IPv4 地址失败" >> ${logfile} - fi - - if [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -ne "0" ] && [ ! -z "$IPv6" ] && ( ! echo "$IPv6"|grep -w -q ${last_IPv6} ); then - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前IPv6:${IPv6}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip - [ -z "$title" ] && title="IPv6 地址变化" - [ ! -z "$title" ] && title="IP 地址变化" - content="${content}${str_splitline}${str_title_start}${font_green} IPv6 地址变化${font_end}${str_title_end}${str_linefeed}${str_tab}当前 IPv6:${IPv6}" - elif [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -ne "0" ] && [ -z "$IPv6" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】获取 IPv6 地址失败" >> ${logfile} - fi - - else - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}路由器已经重启!" >> ${logfile} - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 当前IP: ${IPv4}" >> ${logfile} - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 当前IPv6: ${IPv6}" >> ${logfile} - echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip - title="路由器重新启动" - content="${content}${str_splitline}${str_title_start}${font_green} 路由器重新启动${font_end}${str_title_end}" - [ ! -z "$pushbot_ipv4" ] && [ "$pushbot_ipv4" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IP:${IPv4}" - [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IPv6:${IPv6}" - fi - - if [ ! -z "$content" ] ;then - [ -z "$ddns_enabled" ] && ddns_enabled=$(uci show ddns|grep "enabled"|grep "1") - [ -z "$ddns_enabled" ] && ddns_logrow=0 || ddns_logrow=$(echo "$ddns_enabled"|wc -l) - if [ $ddns_logrow -ge 1 ]; then - /etc/init.d/ddns restart >/dev/null 2>&1 - fi - [ -z "$zerotier_enabled" ] && zerotier_enabled=$(uci get zerotier.sample_config.enabled) - if [ ! -z "$zerotier_enabled" ] && [ $zerotier_enabled -eq "1" ] ; then - /etc/init.d/zerotier restart >/dev/null 2>&1 - fi - fi -} - -# 检测设备上线 -function up(){ - [ -f ${dir}ipAddress ] && ( cat ${dir}ipAddress|grep -q -w $1 ) && return - local ip_mac=`getmac $1` - local ip_name=`getname ${1} ${ip_mac}` - local ip_interface=`getinterface ${ip_mac}` - getping ${1} ${up_timeout} "1";local ping_online=$? - if [ "$ping_online" -eq "0" ]; then - LockFile lock - [ ! -z "$pushbot_blacklist" ] && local tmp_mac=`echo "${pushbot_blacklist}"|grep -w -i ${ip_mac}` - [ ! -z "$pushbot_whitelist" ] && local tmp_mac=`echo "${pushbot_whitelist}"|grep -w -i ${ip_mac}` - if [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ); then - usage down $1 - echo "$1 ${ip_mac} ${ip_name} `date +%s` ${ip_interface}" >> ${dir}ipAddress - LockFile unlock && return - elif [ ! -z "$tmp_mac" ] && [ -f "${dir}tmp_downlist" ] && ( cat ${dir}tmp_downip|grep -q -w -i ${tmp_mac} ); then - local tmp_downip=`cat ${dir}tmp_downlist|grep -w -i ${tmp_mac}|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - usage down $tmp_downip - sed -i "/^${tmp_downip} /d" ${dir}tmp_downlist - LockFile unlock && return - fi - [ -f "${dir}tmp_downlist" ] && local tmp_downip=`cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1` - if [ ! -z "$tmp_downip" ]; then - cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}ipAddress - sed -i "/^${1} /d" ${dir}tmp_downlist - else - usage down $1 - echo "$1 ${ip_mac} ${ip_name} `date +%s` ${ip_interface}" >> ${dir}ipAddress - blackwhitelist ${ip_mac};local ip_blackwhite=$? - [ -f "${dir}send_enable.lock" ] || [ -z "$pushbot_up" ] || [ -z "$ip_blackwhite" ] && LockFile unlock && return - [ ! -z "$pushbot_up" ] && [ "$pushbot_up" -ne "1" ] && LockFile unlock && return - [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && LockFile unlock && return - [ -f "${dir}title" ] && local title=`cat ${dir}title` - [ -f "${dir}content" ] && local content=`cat ${dir}content` - if [ -z "$title" ]; then - local title="$ip_name 连接了你的路由器" - local content="${str_splitline}${str_title_start}${font_green} 新设备连接${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" - elif ( echo ${title}|grep -q "连接了你的路由器" ); then - local title="${ip_name} ${title}" - local content="${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" - else - local title="设备状态变化" - local content="${str_splitline}${str_title_start}${font_green} 新设备连接${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" - fi - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}新设备 ${ip_name} ${1} 连接了">> ${logfile} - #[ ! -z "$pushbot_blacklist" ] && local title="你偷偷关注的设备上线了" - [ ! -z "$title" ] && echo "$title" >${dir}title - [ ! -z "$content" ] && echo -n "$content" >>${dir}content - fi - fi - LockFile unlock -} - -# 检测设备离线 -function down(){ - local ip_mac=`getmac $1` - local ip_name=`getname ${1} ${ip_mac}` - local ip_interface=`getinterface ${ip_mac}` - getping ${1} ${down_timeout} ${timeout_retry_count};local ping_online=$? - if [ "$ping_online" -eq "1" ]; then - LockFile lock - [ ! -f "${dir}send_enable.lock" ] && cat ${dir}ipAddress|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}tmp_downlist - sed -i "/^${1} /d" ${dir}ipAddress - LockFile unlock - else - local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - if [ "$ip_name" != "$tmp_name" ]; then - LockFile lock - local tmp_str=$(echo "$1 ${ip_mac} ${ip_name} `cat ${dir}ipAddress|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` ${ip_interface}") - sed -i "/^${1} /d" ${dir}ipAddress - echo "$tmp_str" >> ${dir}ipAddress - LockFile unlock - fi - fi -} - -# 设备离线通知 -function down_send(){ - [ ! -f "${dir}tmp_downlist" ] && return - local IPLIST=`cat ${dir}tmp_downlist|awk '{print $1}'` - for ip in $IPLIST; do - local ip_mac=`getmac ${ip}` - blackwhitelist ${ip_mac};local ip_blackwhite=$? - [ -z "$pushbot_down" ] || [ -z "$ip_blackwhite" ] && continue - [ ! -z "$pushbot_down" ] && [ "$pushbot_down" -ne "1" ] && continue - [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && continue - [ ! -z "$pushbot_blacklist" ] && local tmp_mac=`echo "${pushbot_blacklist}"|grep -w -i ${ip_mac}` - [ ! -z "$pushbot_whitelist" ] && local tmp_mac=`echo "${pushbot_whitelist}"|grep -w -i ${ip_mac}` - [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ) && continue - local ip_name=`getname ${ip} ${ip_mac}` - local time_up=`cat ${dir}tmp_downlist|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local ip_total=`usage get $ip_mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) - if [ -z "$title" ]; then - title="${ip_name} 断开连接" - content="${content}${str_splitline}${str_title_start}${font_red} 设备断开连接${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - elif ( echo "$title"|grep -q "断开连接" ); then - title="${ip_name} ${title}" - content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_red} 设备断开连接${font_end}${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" - fi - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}设备 ${ip_name} ${ip} 断开连接 " >> ${logfile} - done - rm -f ${dir}tmp_downlist >/dev/null 2>&1 -} - -# 当前设备列表 -function current_device(){ - ( echo "$lite_enable"|grep -q "content" ) || ( echo "$lite_enable"|grep -q "device" ) && return - [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return - [ -f ${dir}usage.db ] && local ip_total_db="总计流量${str_space}${str_space}${str_space}${str_space}" - content="${content}${str_splitline}${str_title_start}${font_blue} 现有在线设备 ${logrow} 台,具体如下${font_end}${str_title_end}${str_linefeed}${str_tab}IP 地址${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${ip_total_db}${boldstar}客户端名${boldstar}" - local IPLIST=`cat ${dir}ipAddress|awk '{print $1}'` - for ip in $IPLIST; do - local ip_mac=`getmac ${ip}` - local ip_total=`usage get ${ip_mac}` - local ip_name=`getname ${ip} ${ip_mac}` - local ip_name=`cut_str $ip_name 15` - if [ "${#ip}" -lt "15" ]; then - local n=`expr 15 - ${#ip}` - for i in `seq 1 $n`; do - local ip="${ip}${str_space}" - done - unset i n - fi - if [ ! -z "$ip_total" ]; then - local n=`expr 11 - ${#ip_total}` - for i in `seq 1 $n`; do - local ip_total="${ip_total}${str_space}" - done - fi - content="${content}${str_linefeed}${str_tab}${ip}${ip_total}${boldstar}${font_green2}${ip_name}${font_end2}${boldstar}" - unset i n ip_total ip_mac ip_name - done -} - -# 检测 cpu 状态 -function cpu_load(){ - if [ ! -z "$temperature_enable" ] && [ "$temperature_enable" -eq "1" ] && [ ! -z "$temperature" ]; then - [ -z "$temperature_time" ] && temperature_time=`date +%s` - local cpu_wendu=`soc_temp`; - [ -z "$cpu_wendu" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备温度,请检查命令" >> ${logfile} - - if [ `expr $cpu_wendu \> $temperature` -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 温度过高: ${cpu_wendu}" >> ${logfile} - else - temperature_time=`date +%s` - fi - - if [ "$((`date +%s`-$temperature_time))" -ge "300" ] && [ -z "$temperaturecd_time" ]; then - title="CPU 温度过高!" - temperaturecd_time=`date +%s` - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 温度过高: ${cpu_wendu}" >> ${logfile} - content="${content}${str_splitline}${str_title_start}${font_red} CPU 温度过高${font_end}${str_title_end}${str_linefeed}${str_tab}CPU 温度已连续五分钟超过预设${str_linefeed}${str_tab}接下来一小时不再提示${str_linefeed}${str_tab}当前温度:${cpu_wendu}℃" - elif [ ! -z "$temperaturecd_time" ] && [ "$((`date +%s`-$temperaturecd_time))" -ge "3300" ] ;then - unset temperaturecd_time - fi - fi - - if [ ! -z "$cpuload_enable" ] && [ "$cpuload_enable" -eq "1" ] && [ ! -z "$cpuload" ]; then - [ -z "$cpuload_time" ] && cpuload_time=`date +%s` - local cpu_fuzai=`cat /proc/loadavg|awk '{print $1}'` 2>/dev/null - [ -z "$cpu_fuzai" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备负载,请检查命令" >> ${logfile} - - if [ `expr $cpu_fuzai \> $cpuload` -eq "1" ]; then - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 负载过高: ${cpu_fuzai}" >> ${logfile} - cputop log - else - cpuload_time=`date +%s` - fi - - if [ "$((`date +%s`-$cpuload_time))" -ge "300" ] && [ -z "$cpucd_time" ]; then - unset getlogtop - if [ ! -z "$title" ] && ( echo "$title"|grep -q "过高" ); then - title="设备报警!" - else - title="CPU 负载过高!" - fi - cpucd_time=`date +%s` - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 负 载过高: ${cpu_fuzai}" >> ${logfile} - content="${content}${str_splitline}${font_red}CPU 负载过高${font_end}${str_linefeed}${str_tab}CPU 负载已连续五分钟超过预设${str_linefeed}${str_tab}接下来一小时不再提示${str_linefeed}${str_tab}当前负载:${cpu_fuzai}" - cputop - elif [ ! -z "$cpucd_time" ] && [ "$((`date +%s`-$cpucd_time))" -ge "3300" ] ;then - unset cpucd_time - fi - fi -} - -function cputop(){ - [ -z "$1" ] && content="${content}${str_splitline}${str_title_start} 当前 CPU 占用前三的进程${str_title_end}" - local gettop=`top -bn 1|grep -v "top -bn 1"` - for i in `seq 5 7`; do - local top_name=`echo "${gettop}"|awk 'NR=='${i}|awk '{print ($8 ~ /\/bin\/sh|\/bin\/bash/) ? $9 : $8}'` - local top_load=`echo "${gettop}"|awk 'NR=='${i}|awk '{print $7}'` - local temp_top="${top_name} ${top_load}" - [ ! -z "$1" ] && local logtop="$logtop $temp_top" - [ -z "$1" ] && content="${content}${str_linefeed}${str_tab}${temp_top}" - done - unset i - [ ! -z "$1" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 占用前三: ${logtop}" >> ${logfile} -} - -# 生成日志监控文件,避免后台影响 wait 语句 -function get_syslog(){ - kill -9 `pgrep -f "logread -f -p notice"` 2>/dev/null - [ -z "$web_logged" ] && [ -z "$ssh_logged" ] && [ -z "$web_login_failed" ] && [ -z "$ssh_login_failed" ] && return - rm -f ${dir}login_monitor >/dev/null 2>&1 - -cat>${dir}get_syslog<> ${dir}login_monitor & -EOF - chmod 0755 ${dir}get_syslog && ${dir}get_syslog - rm -f ${dir}get_syslog >/dev/null 2>&1 -} - -# 登录提醒通知 -function login_send(){ - [ -z "$web_logged" ] && [ -z "$ssh_logged" ] && [ -z "$web_login_failed" ] && [ -z "$ssh_login_failed" ] && return - [ ! -f ${dir}login_monitor ] && return - cat ${dir}login_monitor|grep -i "accepted login"|awk '{print $4" "$NF}' >> ${dir}web_login - cat ${dir}login_monitor|grep -i "Password auth succeeded\|Pubkey auth succeeded"|grep -Eo "[0-9]{2}:[0-9]{2}:[0-9]{2}.*[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"|awk '{print $1" "$NF" "$5}' >> ${dir}ssh_login - cat ${dir}login_monitor|grep -i "failed login"|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" >> ${dir}web_failed - cat ${dir}login_monitor|grep -i "Bad password attempt\|Login attempt for nonexistent user from"|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" >> ${dir}ssh_failed - echo "" > ${dir}login_monitor - add_ip_black - - local login_ip_list=`cat ${dir}web_login|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - for login_ip in $login_ip_list; do - [ -z "$login_ip" ] && continue - echo "$ip_white_list"|grep -w -q "$login_ip" && continue - local web_login_time=`cat ${dir}web_login|grep -w ${login_ip}|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - local web_login_mode=`cat ${dir}web_login|grep -w ${login_ip}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - if [ ! -z "$web_logged" ] && [ "$web_logged" -eq "1" ]; then - if [ -z "$title" ]; then - title="${login_ip} 通过 Web 登录了路由器" - content="${content}${str_splitline}${str_title_start}${font_green} 登录信息${font_end}${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${web_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - elif ( echo "$title"|grep -q "登录了路由器" ); then - title="${login_ip} ${title}" - content="${content}${str_splitline}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${web_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_green} 登录成功来源${font_end}${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${web_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - fi - fi - echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}设备 ${login_ip} 通过 web ${web_login_mode} 登录了路由器 " >> ${logfile} - done - echo "" > ${dir}web_login - unset login_ip login_ip_list - - local login_ip_list=`cat ${dir}ssh_login|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` - for login_ip in $login_ip_list; do - [ -z "$login_ip" ] && continue - echo "$ip_white_list"|grep -w -q "$login_ip" && continue - local ssh_login_time=`cat ${dir}ssh_login|grep -w ${login_ip}|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - local ssh_login_mode=`cat ${dir}ssh_login|grep -w ${login_ip}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` - [ ! -z "$ssh_login_mode" ] && local content_mode="${str_linefeed}${str_tab}登录方式: ${str_space}${str_space}${str_space}${str_space}${ssh_login_mode}" - if [ ! -z "$ssh_logged" ] && [ "$ssh_logged" -eq "1" ]; then - if [ -z "$title" ]; then - title="${login_ip} 通过 SSH 登录了路由器" - content="${content}${str_splitline}${str_title_start}${font_green} 登录成功来源${font_end}${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${ssh_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - elif ( echo "$title"|grep -q "登录了路由器" ); then - title="${login_ip} ${title}" - content="${content}${str_splitline}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${ssh_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_green} 登录成功来源${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${ssh_login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_mode}" - fi - fi - echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】设备 ${login_ip} 通过 SSH ${ssh_login_mode} 登录了路由器 " >> ${logfile} - done - echo "" > ${dir}ssh_login - unset login_ip login_ip_list - - local login_ip_list=`cat ${dir}web_failed|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - for login_ip in $login_ip_list; do - [ -z "$login_ip" ] && continue - echo "$ip_white_list"|grep -w -q "$login_ip" && continue - local web_login_sum=`cat ${dir}web_failed|grep -w "${login_ip}"|wc -l` - if [ "$web_login_sum" -ge "$login_max_num" ] ;then - if [ ! -z "$web_login_failed" ] && [ "$web_login_failed" -eq "1" ]; then - if [ -z "$title" ]; then - title="${login_ip} 通过 Web 频繁尝试登录" - content="${content}${str_splitline}${str_title_start}${font_red} 登录失败来源${font_end}${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - elif ( echo "$title"|grep -q "频繁尝试登录" ); then - title="${login_ip} ${title}" - content="${content}${str_splitline}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_red} 登录失败来源${font_end}${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - fi - fi - sed -i "/^${login_ip}$/d" ${dir}web_failed - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 ${login_ip} 通过 Web 频繁尝试登录" >> ${logfile} - add_ip_black $login_ip - fi - done - unset login_ip - - local login_ip_list=`cat ${dir}ssh_failed|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` - for login_ip in $login_ip_list; do - [ -z "$login_ip" ] && continue - echo "$ip_white_list"|grep -w -q "$login_ip" && continue - local ssh_login_sum=`cat ${dir}ssh_failed|grep -w "${login_ip}"|wc -l` - if [ "$ssh_login_sum" -ge "$login_max_num" ] ;then - if [ ! -z "$ssh_login_failed" ] && [ "$ssh_login_failed" -eq "1" ]; then - if [ -z "$title" ]; then - title="${login_ip} 通过 SSH 频繁尝试登录" - content="${content}${str_splitline}${str_title_start}${font_red} 登录失败来源${font_end}${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - elif ( echo "$title"|grep -q "频繁尝试登录" ); then - title="${login_ip} ${title}" - content="${content}${str_splitline}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - else - title="设备状态变化" - content="${content}${str_splitline}${str_title_start}${font_red} 登录失败来源${font_end}${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}" - fi - fi - sed -i "/^${login_ip}$/d" ${dir}ssh_failed - echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 ${login_ip} 通过 SSH 频繁尝试登录" >> ${logfile} - add_ip_black $login_ip - fi - done - unset login_ip - -} - -# 添加黑名单 -function add_ip_black(){ - [ -f "${ip_blacklist_path}" ] && local logrow=$(grep -c "" ${ip_blacklist_path}) || local logrow="0" - [ ! -f "${ip_blacklist_path}" ] && local logrow="0" - [ ! -z "$web_login_black" ] && [ "$web_login_black" -eq "0" ] || [ -z "$web_login_black" ] && local logrow="0" - ipset flush ip_blacklist >/dev/null 2>&1 - - if [ $logrow -le "0" ]; then - iptables -D INPUT -m set --match-set ip_blacklist src -j DROP >/dev/null 2>&1 - ipset destroy ip_blacklist >/dev/null 2>&1 - return - fi - - ipset list ip_blacklist >/dev/null 2>&1 || ipset create ip_blacklist hash:ip timeout ${ip_black_timeout} >/dev/null 2>&1 - iptables -C INPUT -m set --match-set ip_blacklist src -j DROP >/dev/null 2>&1 || iptables -I INPUT -m set --match-set ip_blacklist src -j DROP >/dev/null 2>&1 - echo "$1" >> ${ip_blacklist_path} - for ip_black in `cat ${ip_blacklist_path}`; do - ip_black=`echo "$ip_black"|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"` - ipset -! add ip_blacklist $ip_black >/dev/null 2>&1 - done - ipset list ip_blacklist|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" > ${ip_blacklist_path} -} - -# 发送定时数据 -function send(){ - echo "`date "+%Y-%m-%d %H:%M:%S"` 【定时数据】创建定时任务" >> ${logfile} - pushbot_disturb;local send_disturb=$? - get_config "send_title" "router_status" "router_temp" "router_wan" "client_list" "google_check_timeout" - - [ -z "$send_title" ] && local send_title="路由状态:" - [ -z "$google_check_timeout" ] && local google_check_timeout="10" - [ ! -z "$1" ] && local send_title="发送测试:" && local send_content="${str_splitline}${str_title_start}内容1${str_title_end}${str_linefeed}${str_tab}设备1${str_linefeed}${str_tab}设备2${str_splitline}${str_title_start}内容2${str_title_end}${str_linefeed}${str_tab}设备3${str_linefeed}${str_tab}设备4" - [ -z "$1" ] && [ ! -z "$client_list" ] && [ "$client_list" -eq "1" ] && > ${dir}send_enable.lock && pushbot_first & - - if [ -z "$1" ] && [ ! -z "$router_status" ] && [ "$router_status" -eq "1" ]; then - local systemload=`cat /proc/loadavg|awk '{print $1" "$2" "$3}'` - local cpuload=`getcpu` - local ramload=`free -m|sed -n '2p'|awk '{printf "%.2f%%\n",($3/$2)*100}'` - local Qwai=`curl -o /dev/null --connect-timeout ${google_check_timeout} -s -w %{http_code} www.google.com` - if [[ "$Qwai" -eq "200" ]] || [[ "$Qwai" -eq "301" ]] || [[ "$Qwai" -eq "302" ]]; then - local Qwai_status="已连通!" - else - local Qwai_status="已断开!" - fi - local systemstatustime=`cat /proc/uptime|awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("运行时间:%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}'`;unset run_days run_hour run_minute run_second - local send_content="${send_content}${str_splitline}${str_title_start}${font_blue} 系统运行状态${font_end}${str_title_end}" - local send_content="${send_content}${str_linefeed}${str_tab}平均负载:${systemload}" - local send_content="${send_content}${str_linefeed}${str_tab}CPU占用:${cpuload}${percentsym}" - local send_content="${send_content}${str_linefeed}${str_tab}内存占用:${ramload}${percentsym}" - local send_content="${send_content}${str_linefeed}${str_tab}全球互联:${Qwai_status}" - local send_content="${send_content}${str_linefeed}${str_tab}${systemstatustime}" - fi - - if [ -z "$1" ] && [ ! -z "$router_temp" ] && [ "$router_temp" -eq "1" ]; then - local cputemp=`soc_temp` - [ ! -z "$cputemp" ] && local send_content="${send_content}${str_splitline}${str_title_start}${font_blue} 设备温度${font_end}${str_title_end}${str_linefeed}${str_tab}CPU:${cputemp}℃" - [ -z "$cputemp" ] && local send_content="${send_content}${str_splitline}${str_title_start}${font_red} 设备温度${font_end}${str_title_end}${str_linefeed}${str_tab}无法获取设备温度" - fi - - if [ -z "$1" ] && [ ! -z "$router_wan" ] && [ "$router_wan" -eq "1" ]; then - local send_wanIP=`getip wanipv4`;local send_hostIP=`getip hostipv4` - local send_content="${send_content}${str_splitline}${str_title_start}${font_blue} WAN 口信息${font_end}${str_title_end}${str_linefeed}${str_tab}接口ip:${send_wanIP}" - local send_content="${send_content}${str_linefeed}${str_tab}外网ip:${send_hostIP}" - if [ ! -z "$pushbot_ipv6" ] && [ "$pushbot_ipv6" -ne "0" ]; then - local send_wanIPv6=`getip wanipv6`;local send_hostIPv6=`getip hostipv6` - local send_content="${send_content}${str_linefeed}${str_tab}ipv6 :${send_wanIPv6}" - local send_content="${send_content}${str_linefeed}${str_tab}外网v6:${send_hostIPv6}" - fi - ( ! echo "$send_wanIP"|grep -q -w ${send_hostIP} ) && local send_content="${send_content}${str_linefeed}${str_tab}外网 ip 与接口 ip 不一致,你的 ip 不是公网 ip" - local interfaceuptime=`getinterfaceuptime` - [ ! -z "$interfaceuptime" ] && local wanstatustime=`getinterfaceuptime|awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("在线时间:%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}'` && unset run_days run_hour run_minute run_second - local send_content="${send_content}${str_linefeed}${str_tab}${wanstatustime}" - fi - - if [ -z "$1" ] && [ ! -z "$client_list" ] && [ "$client_list" -eq "1" ]; then - wait - local IPLIST=`cat ${dir}ipAddress 2>/dev/null|awk '{print $1}'` - [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0" - [ "$logrow" -eq "0" ] && local send_content="${send_content}${str_splitline}${str_title_start}${font_red} 当前无在线设备${font_end}${str_title_end}" || local send_content="${send_content}${str_splitline}${str_title_start}${font_blue} 现有在线设备 ${logrow} 台${font_end}${str_title_end}" - for ip in $IPLIST; do - local time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` - local time1=`date +%s` - local time1=$(time_for_humans `expr ${time1} - ${time_up}`) - local ip_mac=`getmac ${ip}` - local ip_name=`getname ${ip} ${ip_mac}` - local ip_total=`usage get ${ip_mac}`;[ ! -z "$ip_total" ] && local ip_total="总计流量:${ip_total} " - local ip_name=`cut_str $ip_name 18` - local send_content="${send_content}${str_linefeed}${str_tab}${font_green2}【${ip_name}】${font_end2} ${ip}${str_linefeed}${str_tab}${ip_total}在线 ${time1}" - unset ip_total time_down time_up time1 ip_mac ip_name - done - fi - - [ ! -z "$device_name" ] && local send_title="【$device_name】${send_title}" - [ -z "$send_content" ] && local send_content="${str_splitline}${str_title_start} 我遇到了一个难题${str_title_end}${str_linefeed}${str_tab}定时发送选项错误,你没有选择需要发送的项目,该怎么办呢${str_splitline}" - [ "$send_disturb" -eq "0" ] && diy_send "${send_title}" "${send_content}" "${jsonpath}" >/dev/null 2>&1 - [ $? -eq 1 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】定时推送失败,请检查网络或设置信息" >> ${logfile} || echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}定时推送任务完成" >> ${logfile} - deltemp -} - -# 初始化 -read_config -deltemp -pushbot_cron - -# 限制并发进程 -[ -z "$thread_num" ] || [ "$thread_num" -eq "0" ] && thread_num=5 -[ -e ${dir}fd1 ] || mkfifo ${dir}fd1 -exec 5<>${dir}fd1 -rm -f ${dir}fd1 >/dev/null 2>&1 -for i in `seq 1 $thread_num`; do - echo >&5 -done -unset i - -# 启动参数 -if [ "$1" ] ;then - [ $1 == "send" ] && send - [ $1 == "soc" ] && echo `soc_temp` > ${dir}soc_tmp - [ $1 == "client" ] && get_client - [ $1 == "test" ] && send test - exit -fi - -# 载入在线设备 -pushbot_init;[ $? -eq 1 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】读取设置出错,请检查设置项 " >> ${logfile} && exit -echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】载入在线设备" >> ${logfile} -> ${dir}send_enable.lock && pushbot_first && deltemp -echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】初始化完成" >> ${logfile} - -# 循环 -while [ "$pushbot_enable" -eq "1" ]; do - deltemp - usage update - pushbot_disturb;disturb=$? - - # 外网IP变化检测 - [ -f ${dir}ipAddress ] && ipAddress_logrow=$(grep -c "" ${dir}ipAddress) || ipAddress_logrow="0"; - if [ $ipAddress_logrow -ne "0" ]; then - online_list=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` - for online_mac in $online_list; do - [ ! -z "$online_mac" ] && mac_online_status="`echo "$mark_mac_list"|grep -i $online_mac|grep -v "^$"|sort -u|head -n1`${mac_online_status}" - done - fi - - if [ "$pushbot_ipv4" -ne "0" ] || [ "$pushbot_ipv6" -ne "0" ]; then - rand_geturl - ip_changes - fi - - # 设备列表 - if [ ! -f "${dir}send_enable.lock" ]; then - [ ! -z "$title" ] && echo "$title" > ${dir}title - [ ! -z "$content" ] && echo "$content" > ${dir}content - pushbot_first - [ -f "${dir}title" ] && title=`cat ${dir}title` && rm -f ${dir}title >/dev/null 2>&1 - [ -f "${dir}content" ] && content=`cat ${dir}content` && rm -f ${dir}content >/dev/null 2>&1 - fi - - # 离线缓存区推送 - [ ! -f "${dir}send_enable.lock" ] && down_send - - # 当前设备列表 - [ ! -z "$content" ] && [ ! -f "${dir}send_enable.lock" ] && current_device - - # 无人值守任务 - [ ! -f "${dir}send_enable.lock" ] && unattended - - # CPU 检测 - [ ! -f "${dir}send_enable.lock" ] && cpu_load - - # 异常流量检测 - [ ! -f "${dir}send_enable.lock" ] && get_client_usage - - # 登录提醒通知 - [ ! -f "${dir}send_enable.lock" ] && login_send - - # 通知 - if [ ! -f "${dir}send_enable.lock" ] && [ ! -z "$title" ] && [ ! -z "$content" ]; then - [ ! -z "$device_name" ] && title="【$device_name】$title" - ( echo "$lite_enable"|grep -q "content" ) && content="$title" - [ "$disturb" -eq "0" ] && diy_send "${title}" "${content}" "${jsonpath}" >/dev/null 2>&1 - [ $? -eq 1 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】推送失败,请检查网络或设置信息 " >> ${logfile} - fi - - while [ -f "${dir}send_enable.lock" ]; do - sleep $sleeptime - done - sleep $sleeptime -done diff --git a/luci-app-pushbot/root/usr/share/rpcd/acl.d/luci-app-pushbot.json b/luci-app-pushbot/root/usr/share/rpcd/acl.d/luci-app-pushbot.json deleted file mode 100644 index 473cdcd1..00000000 --- a/luci-app-pushbot/root/usr/share/rpcd/acl.d/luci-app-pushbot.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "luci-app-pushbot": { - "description": "Grant UCI access for luci-app-pushbot", - "read": { - "uci": [ "pushbot" ] - }, - "write": { - "uci": [ "pushbot" ] - } - } -} diff --git a/luci-app-wechatpush/Makefile b/luci-app-wechatpush/Makefile new file mode 100755 index 00000000..b6d99733 --- /dev/null +++ b/luci-app-wechatpush/Makefile @@ -0,0 +1,24 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-wechatpush +PKG_VERSION:=3.3.2 +PKG_RELEASE:=12 + +PKG_MAINTAINER:=tty228 + +LUCI_TITLE:=LuCI support for wechatpush +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+iputils-arping +curl +jq +bash + +define Package/$(PKG_NAME)/conffiles +/etc/config/wechatpush +/usr/share/wechatpush/api/diy.json +/usr/share/wechatpush/api/logo.jpg +/usr/share/wechatpush/api/ipv4.list +/usr/share/wechatpush/api/ipv6.list +/usr/share/wechatpush/api/device_aliases.list +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-wechatpush/README.md b/luci-app-wechatpush/README.md new file mode 100644 index 00000000..5e14f2f4 --- /dev/null +++ b/luci-app-wechatpush/README.md @@ -0,0 +1,92 @@ +## 简介 + +[![Lastest Release](https://img.shields.io/github/release/tty228/luci-app-wechatpush.svg?style=flat)](https://github.com/tty228/luci-app-wechatpush/releases) +[![GitHub All Releases](https://img.shields.io/github/downloads/tty228/luci-app-wechatpush/total)](https://github.com/tty228/luci-app-wechatpush/releases) + +[中文文档](README.md) | [English](README_en.md) + +这是一款用于 OpenWRT 路由器上进行 微信/Telegram 推送的插件 + +推送服务支持列表: + +| 推送应用 | 方式 | 接口说明 | +| :-------- | :----- | :----- | +| 微信 | Server酱 | https://sct.ftqq.com/ +| 微信 | 推送加 | http://www.pushplus.plus/ +| 微信 | WxPusher | https://wxpusher.zjiecode.com/docs +| 企业微信 | 应用推送 | https://work.weixin.qq.com/api/doc/90000/90135/90248 +| Telegram | bot | https://t.me/BotFather + +精力有限,如需要钉钉推送、飞书推送、Bark 推送等请尝试另一个分支 https://github.com/zzsj0928/luci-app-pushbot ,或使用自定义 API 设置 + +因插件一开始只支持 Server酱,故此插件命名为 luci-app-serverchan,但后续已经越来越臃肿,想改很久了,趁这次升级 js, 插件更名为 luci-app-wechatpush + +v3.0.0 安装时会自动从 luci-app-serverchan 移植配置信息,并删除原插件,如有缺漏,请在 /etc/config/serverchan.bak 查看,安装后可能需刷新浏览器页面,否则 luci 页面入口不会更换为新插件 URL(此功能择期移除) + +v2.06.2 之后的版本不再支持 LuCI 18.06,如需编译,请使用 openwrt-18.06 分支,拉取源码时请使用 `git clone -b openwrt-18.06 https://github.com/tty228/luci-app-wechatpush.git` 命令 + + +## 主要功能 + +- [x] 路由 IP、IPv6 变动推送 +- [x] 设备 上线、离线 推送 +- [x] 设备在线列表及流量使用情况 +- [x] CPU 负载、温度监视、PVE 宿主机温度监控 +- [x] 路由运行状态定时推送 +- [x] 路由 Web、SSH 登录提示,自动拉黑、端口敲门 +- [x] 无人值守任务 + + +## 说明 + +**关于安装:** + +插件依赖 iputils-arping + curl + jq + bash,对于内存有限的路由器,请酌情安装,**在安装之前,请先运行 `opkg update` 命令,以便在安装过程中安装依赖。** + +基于 X86 OpenWrt v23.05.0 制作,不同系统不同设备,可能会遇到各种问题,**如获取到错误的温度信息、页面显示错误、报错等,自行适配** + +**关于主机名:** + +对于设备未宣告主机名、光猫拨号上网、OpenWrt 作为旁路网关等各类情况导致的获取主机名失败,可以通过以下方式设置主机名 + +- 使用设备名备注 +- 在高级设置处配置从光猫获取 +- 开启 MAC 设备数据库 + + +**关于设备在线状态:** + +默认使用 ping/arping 来主动探测设备在线状态,以对抗 Wi-Fi 休眠机制,主动探测较为耗时但可以获得较为精准的设备在线状态 + +- 如遇设备休眠频繁,请在高级设置处自行调整超时设置 +- 如果不需要太过精准的设备在线信息,只需要其余功能,可以在高级设置中关闭主动探测 + + +**关于流量统计信息:** + +流量统计功能依赖 wrtbwmon ,需自行选装或编译,**该插件与 Routing/NAT 、Flow Offloading 、代理上网等插件冲突,开启后将会无法获取流量,请自行选择** + + +**关于 bug 提交:** + +提交 bug 时请尽量带上以下信息 + +- 设备信息及插件版本号 +- 执行 `/usr/share/wechatpush/wechatpush` 后的提示信息 +- 报错后的日志信息、`/tmp/wechatpush/` 目录下的文件信息 +- `bash -x /usr/share/wechatpush/wechatpush t1` 的详细运行信息 + + +## 下载 + +| 支持的 OpenWrt 版本 | 下载地址 | +| :-------- | :----- | +| openwrt-19.07.0 ... latest | [![Lastest Release](https://img.shields.io/github/release/tty228/luci-app-wechatpush.svg?style=flat)](https://github.com/tty228/luci-app-wechatpush/releases) +| openwrt-18.06 | [![Release v2.06.2](https://img.shields.io/badge/release-v2.06.2-lightgrey.svg)](https://github.com/tty228/luci-app-wechatpush/releases/tag/v2.06.2) + + +## 捐赠 + +如果你觉得此项目对你有帮助,请捐助我们,使项目能持续发展和更加完善。 + +![image](https://github.com/tty228/Python-100-Days/blob/master/res/WX.jpg) diff --git a/luci-app-wechatpush/README_en.md b/luci-app-wechatpush/README_en.md new file mode 100644 index 00000000..722537e9 --- /dev/null +++ b/luci-app-wechatpush/README_en.md @@ -0,0 +1,81 @@ +# Introduction + +[![Lastest Release](https://img.shields.io/github/release/tty228/luci-app-wechatpush.svg?style=flat)](https://github.com/tty228/luci-app-wechatpush/releases) +[![GitHub All Releases](https://img.shields.io/github/downloads/tty228/luci-app-wechatpush/total)](https://github.com/tty228/luci-app-wechatpush/releases) + +[中文文档](README.md) | [English](README_en.md) + +A plugin for OpenWRT routers to push various information to a mobile phone via WeChat or Telegram. +Supported services: +| Push application | Method | description | +| :-------- | :----- | :----- | +| WeChat | Server Chan | https://sct.ftqq.com/ +| WeChat | PushPlus | http://www.pushplus.plus/ +| WeChat | WxPusher | https://wxpusher.zjiecode.com/docs +| WeChat for Enterprise | Application Push | https://work.weixin.qq.com/api/doc/90000/90135/90248 +| Telegram | bot | https://t.me/BotFather + +Limited resources are available. If you need services such as DingTalk push, Feishu push, Bark push, etc., please try another branch at https://github.com/zzsj0928/luci-app-pushbot, or use custom API settings. + +## Main Features + +- [x] Push notifications for changes in router IP and IPv6. +- [x] Push notifications for device online/offline status. +- [x] Device online list and traffic usage. +- [x] CPU load and temperature monitoring, PVE host temperature monitoring. +- [x] Periodic push notifications for router status. +- [x] Web and SSH login prompts for the router, automatic blacklist and port knocking. +- [x] Unattended tasks. + +## Instructions + +**Regarding Installation:** + +The plugin requires dependencies on iputils-arping + curl + jq + bash. For routers with limited memory, please consider the installation carefully. **Before installing, please run the opkg update command to install dependencies during the installation process.** + +Developed based on X86 OpenWrt v23.05.0, different systems and devices may encounter various issues. **If you encounter errors in temperature information retrieval, display errors, or other issues, please adapt accordingly.** + +**Regarding Hostnames:** + +For devices that do not declare hostnames, devices connected via optical modem dial-up, OpenWrt used as a bypass gateway, and other scenarios where hostname retrieval fails, you can set the hostname using the following methods: + +- Use device name remarks. +- Configure to obtain the hostname from the optical modem in advanced settings. +- Enable MAC device database. + + +**Regarding Device Online Status:** + +By default, ping/arping is used to actively detect device online status to counter Wi-Fi sleep mechanism. Active detection takes more time but provides more accurate device online status. + +- If devices frequently go into sleep mode, please adjust the timeout settings in advanced settings. +- If you don't require highly precise device online information and only need other features, you can disable active detection in advanced settings. + + +**Regarding Traffic Statistics:** + +Traffic statistics functionality depends on `wrtbwmon`. Please install or compile it yourself. **Enabling this plugin will conflict with Routing/NAT, Flow Offloading, proxy internet access, and other plugins, resulting in the inability to obtain traffic statistics. Please choose accordingly.** + + +**Regarding Bug Submissions:** + +When submitting a bug, please provide the following information if possible: + +- Device information and plugin version number. +- Prompt information after executing `/usr/share/wechatpush/wechatpush`. +- Log information and file information in the `/tmp/wechatpush/` directory after encountering an error. +- Detailed execution information of `bash -x /usr/share/wechatpush/wechatpush t1`. + +## DownLoad + +| Supported OpenWrt Versions | Download Link | +| :-------- | :----- | +| openwrt-19.07.0 ... latest | [![Lastest Release](https://img.shields.io/github/release/tty228/luci-app-wechatpush.svg?style=flat)](https://github.com/tty228/luci-app-wechatpush/releases) +| openwrt-18.06 | [![Release v2.06.2](https://img.shields.io/badge/release-v2.06.2-lightgrey.svg)](https://github.com/tty228/luci-app-wechatpush/releases/tag/v2.06.2) + +## Donate + +If you feel that this project is helpful to you, please donate to us so that the project can continue to develop and be more perfect. + +![image](https://github.com/tty228/Python-100-Days/blob/master/res/WX.jpg) + diff --git a/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js new file mode 100644 index 00000000..5f278469 --- /dev/null +++ b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js @@ -0,0 +1,200 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; +'require uci'; +'require rpc'; +'require form'; +'require tools.widgets as widgets'; +'require tools.firewall as fwtool'; + +return view.extend({ + render: function (data) { + var m, s, o; + var programPath = '/usr/share/wechatpush/wechatpush'; + + m = new form.Map('wechatpush', _('')) + m.description = _("If you are not familiar with the meanings of these options, please do not modify them.

") + + s = m.section(form.NamedSection, 'config', 'wechatpush', _('')); + s.anonymous = true + s.addremove = false + + o = s.option(form.Value, 'up_timeout', _('Device online detection timeout (s)')); + o.placeholder = "2" + o.optional = false + o.datatype = "uinteger" + o.rmempty = false; + + o = s.option(form.Value, "down_timeout", _('Device offline detection timeout (s)')) + o.placeholder = "10" + o.optional = false + o.datatype = "uinteger" + o.rmempty = false; + + o = s.option(form.Value, "timeout_retry_count", _('Offline detection count')) + o.placeholder = "2" + o.optional = false + o.datatype = "uinteger" + o.rmempty = false; + o.description = _("If the device has good signal strength and no Wi-Fi sleep issues, you can reduce the above values.
Due to the mysterious nature of Wi-Fi sleep during the night, if you encounter frequent disconnections, please adjust the parameters accordingly.
..╮(╯_╰)╭..") + + o = s.option(form.Flag, "only_timeout_push", _("Offline timeout applies only to the devices that receive push notifications")) + o.default = 0 + o.rmempty = true + o.description = _("When this option is selected, the offline timeout and offline detection count apply only to the devices that require push notifications. Other devices will use default values, which can significantly reduce the time required for detection. However, it may result in inaccurate online time displayed in the online devices list. It is recommended to enable this option only when there are many devices and frequent offline occurrences are observed for specific devices of interest.") + + o = s.option(form.Flag, "passive_mode", _("Disable active detection")) + o.default = 0 + o.rmempty = true + o.description = _("Disable active detection of client online status. Enabling this feature will no longer prompt device online/offline events.
Suitable for users who are not sensitive to online devices but need other features.") + + o = s.option(form.Value, "thread_num", _('Maximum concurrent processes')) + o.placeholder = "3" + o.datatype = "uinteger" + o.rmempty = false; + o.description = _("Do not change the setting value for low-performance devices, or reduce the parameters as appropriate.") + + o = s.option(form.Value, "soc_code", _('Custom temperature reading command')) + o.rmempty = true + o.value("", _("Default")) + o.value("pve", _("Proxmox Virtual Environment")) + o.description = _("If you need to use special symbols such as quotes, $, !, etc. in custom commands, you need to escape them yourself.
You can use the command eval echo $(uci get wechatpush.wechatpush.soc_code) to view command output and error information.
The execution result should be a pure number (including decimals) for temperature comparison.
Here is an example that does not require escaping:
cat /sys/class/thermal/thermal_zone0/temp|sort -nr|head -n1|cut -c-2") + + o = s.option(form.Value, "server_host", _("Host machine address")) + o.rmempty = true + o.default = "10.0.0.2" + o.depends('soc_code', 'pve'); + + o = s.option(form.Value, "server_port", _("Host machine SSH port")) + o.rmempty = true + o.default = "22" + o.description = _('The default SSH port is 22. If you have a custom port, please fill in the custom SSH port.
Please make sure you have set up key-based login, otherwise it may cause script errors.
Install the sensors command on PVE by searching on the internet.
Example for key-based login (modify the address and port number accordingly):
opkg update # Update package list
opkg install openssh-client openssh-keygen # Install openssh client
echo -e \"\\n\" | ssh-keygen -t rsa # Generate key file (no passphrase)
pve_host=`uci get wechatpush.config.server_host` || pve_host=\"10.0.0.3\" # Read the PVE host address from the configuration file, If not saved, please fill in by yourself.
pve_port=`uci get wechatpush.config.server_port` || pve_host=\"22\" # Read the PVE host SSH port number from the configuration file, If not saved, please fill in by yourself.
ssh -o StrictHostKeyChecking=yes root@${pve_host} -p ${pve_port} \"tee -a ~/.ssh/OpenWrt_id_rsa.pub\" < ~/.ssh/id_rsa.pub # Transfer public key to PVE
ssh root@${pve_host} -p ${pve_port} \"cat ~/.ssh/OpenWrt_id_rsa.pub >> ~/.ssh/authorized_keys\" # Write public key to PVE
ssh -i /root/.ssh/id_rsa root@${pve_host} -p ${pve_port} sensors # To avoid script errors during the initial connection, please use a private key to connect to PVE and test the temperature command for its proper functioning.
For users who frequently flash firmware, please add /root/.ssh/ to the backup list to avoid duplicate operations.'); + o.depends('soc_code', 'pve'); + + o = s.option(form.Button, '_soc', _('Test temperature command'), _('You may need to save the configuration before sending.')); + o.inputstyle = 'action'; + o.onclick = function () { + var _this = this; + return fs.exec(programPath, ['soc']).then(function (res) { + if (!res.stdout) { + throw new Error(_('Returned temperature value is empty')); + } + _this.description = res.stdout.trim(); + return _this.map.reset(); + }).catch(function (err) { + _this.description = _('Fetch failed:') + err.message; + return _this.map.reset(); + }); + }; + + o = s.option(form.MultiValue, 'device_info_helper', _('Assist in obtaining device information')); + o.value('gateway_info', _('Retrieve hostname list from modem')); + o.value('scan_local_ip', _('Scan local IP')); + o.modalonly = true; + o.description = _('When OpenWrt is used as a bypass gateway and cannot obtain device hostnames or a complete list of local network devices.
the \"Retrieve hostname list from modem\" option has only been tested with HG5143F/HN8145V China Telecom gateways and may not be universally applicable.
The \"Scan local IP\" option may not retrieve hostnames, so please use device name annotations in conjunction with it.'); + + o = s.option(form.Value, "gateway_host_url", _('Optical modem login URL')); + o.rmempty = true; + o.default = "http://192.168.1.1/cgi-bin/luci"; + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_info_url", _('Device list JSON URL')); + o.rmempty = true; + o.default = "http://192.168.1.1/cgi-bin/luci/admin/allInfo"; + o.description = _('Use F12 console to capture
ip, devName, model are mandatory fields. Example JSON file information:
{\"pc1\":{\"devName\":\"RouterOS\",\"model\":\"\",\"type\":\"pc\",\"ip\":\"192.168.1.7\"}}'); + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_logout_url", _('Optical modem logout URL')) + o.rmempty = true + o.default = "http://192.168.1.1/cgi-bin/luci/admin/logout" + o.description = _("Not a mandatory field, but it may affect other users logging into the web management page, e.g., HG5143F") + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_username_id", _('Login page account input box ID')) + o.rmempty = true + o.default = "username" + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_password_id", _('Login page password input box ID')) + o.rmempty = true + o.default = "psd" + o.description = _("Right-click in the browser and select 'Inspect Element'") + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_username", _('Optical modem login account')) + o.rmempty = true + o.default = "useradmin" + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "gateway_password", _('Optical modem login password')) + o.rmempty = true + o.description = _("Use a regular account, no need for super password") + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + + o = s.option(form.Value, "scan_ip_range", _('IP range to be scanned')) + o.rmempty = true + o.placeholder = _('192.168.1.1-100'); + o.depends({ device_info_helper: "scan_local_ip", '!contains': true }); + + o = s.option(form.Value, 'device_info_helper_sleeptime', _('Interval for capturing info')); + o.rmempty = false; + o.placeholder = '600'; + o.datatype = 'and(uinteger,min(60))' + o.description = _("Generally, frequent capturing is not necessary. Adjust it as needed.") + o.depends({ device_info_helper: "gateway_info", '!contains': true }); + o.depends({ device_info_helper: "scan_local_ip", '!contains': true }); + + o = s.option(form.Flag, "unattended_enable", _("Unattended tasks")) + o.default = 0 + o.rmempty = true + o.description = _("Please make sure the script can run properly, otherwise it may cause frequent restarts and other errors!") + + o = s.option(form.Flag, 'zerotier_helper', _('Restart zerotier after IP change')); + o.description = _('An old issue with zerotier
Cannot reconnect after disconnection, emmm, I don\'t know if it has been fixed now.'); + o.depends('unattended_enable', '1'); + + o = s.option(form.Flag, "unattended_only_on_disturb_time", _("Redial only during Do-Not-Disturb period")) + o.default = 0 + o.rmempty = true + o.description = _("Avoid redialing network during the day to prevent waiting for DDNS domain resolution. This feature does not affect disconnection detection.
Due to the issue of certain apps consuming excessive data at night, this feature may be unstable.") + o.depends('unattended_enable', '1'); + + o = s.option(form.DynamicList, "unattended_device_aliases", _("Followed device list")) + o.rmempty = true + o.description = _("Will only be executed when none of the devices in the list are online.
After an hour of Do-Not-Disturb period, if the devices in the focus list have low traffic (around 100kb/m) for five minutes, they will be considered offline.") + //nt.mac_hints(function(mac, name) o :value(mac, "%s (%s)" %{ mac, name }) end) + o.depends('unattended_enable', '1'); + + o = s.option(form.ListValue, "network_disconnect_event", _("When the network is disconnected")) + o.default = "" + o.value("", _("No operation")) + o.value("1", _("Restart the router")) + o.value("2", _("Redialing network")) + o.description = _("The restart operation will occur ten minutes after the network disconnection and will be attempted a maximum of two times. If the option to log in to the optical modem is available, this operation will attempt to restart the optical modem.
【!!This feature cannot guarantee compatibility!!】") + o.depends('unattended_enable', '1'); + + o = s.option(form.ListValue, "unattended_autoreboot_mode", _("Scheduled reboot")) + o.default = "" + o.value("", _("No operation")) + o.value("1", _("Restart the router")) + o.value("2", _("Redialing network")) + o.depends('unattended_enable', '1'); + + o = s.option(form.Value, "autoreboot_system_uptime", _("System uptime greater than")) + o.rmempty = true + o.default = "24" + o.datatype = "uinteger" + o.description = _("Unit: hours") + o.depends('unattended_autoreboot_mode', '1'); + + o = s.option(form.Value, "autoreboot_network_uptime", _("Network uptime greater than")) + o.rmempty = true + o.default = "24" + o.datatype = "uinteger" + o.description = _("Unit: hours") + o.depends('unattended_autoreboot_mode', '2'); + + return m.render(); + } +}); diff --git a/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/client.js b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/client.js new file mode 100644 index 00000000..5d707492 --- /dev/null +++ b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/client.js @@ -0,0 +1,264 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; + +return view.extend({ + load: function () { + return L.resolveDefault(fs.exec_direct('/usr/libexec/wechatpush-call', ['get_client'], 'json'), { devices: [] }); + }, + render: function (data) { + + var devices = data.devices; + var totalDevices = devices.length; + var headers = [_('Hostname'), _('IPv4 address'), _('MAC address'), _('Interfaces'), _('Online time'), _('Details')]; + var columns = ['name', 'ip', 'mac', 'interface', 'uptime', 'usage']; + var visibleColumns = []; + var hasData = false; + + for (var i = 0; i < columns.length; i++) { + var column = columns[i]; + var hasColumnData = false; + + for (var j = 0; j < devices.length; j++) { + if (devices[j][column] !== '') { + hasColumnData = true; + hasData = true; + break; + } + } + + if (hasColumnData) { + visibleColumns.push(i); + } + } + + var style = ` + .device-table { + width: 80%; + border-collapse: collapse; + } + .device-table th, + .device-table td { + padding: 0.5rem; + text-align: center; + border: 1px solid #ccc; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .device-table td:first-child { + word-wrap: break-word; + } + .device-table th { + background-color: #f2f2f2; + font-weight: bold; + color: #666; + cursor: pointer; + } + .device-table tbody tr:nth-of-type(even) { + background-color: #f9f9f9; + } + .sortable { + cursor: pointer; + position: relative; + } + .sortable::after { + content: ''; + position: absolute; + right: -10px; + top: 50%; + transform: translateY(-50%); + width: 0; + height: 0; + border-style: solid; + border-width: 5px 5px 0 5px; + border-color: #aaa transparent transparent transparent; + opacity: 0.6; + } + .sortable.asc::after { + border-color: #666 transparent transparent transparent; + } + .sortable.desc::after { + border-color: transparent transparent #666 transparent; + } + .device-table .hide { + display: none; + } + @media (max-width: 767px) { + .device-table th:nth-of-type(4), + .device-table td:nth-of-type(4) { + display: none; + } + .device-table th, + .device-table td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .device-table td:first-child { + max-width: 150px; + } + } + `; + + function createTable() { + var table = document.createElement('table'); + table.classList.add('device-table'); + + var thead = document.createElement('thead'); + var tr = document.createElement('tr'); + + for (var i = 0; i < headers.length; i++) { + var th = document.createElement('th'); + th.textContent = headers[i]; + + if (visibleColumns.includes(i)) { + th.classList.add('sortable'); + th.dataset.column = columns[i]; + } else { + th.classList.add('hide'); + } + + tr.appendChild(th); + } + + thead.appendChild(tr); + table.appendChild(thead); + + var tbody = document.createElement('tbody'); + devices.forEach(function (device) { + var row = document.createElement('tr'); + for (var i = 0; i < columns.length; i++) { + if (visibleColumns.includes(i)) { + var cell = document.createElement('td'); + cell.textContent = device[columns[i]]; + row.appendChild(cell); + } + } + tbody.appendChild(row); + }); + + table.appendChild(tbody); + + return table; + } + + var container = document.createElement('div'); + container.appendChild(document.createElement('h2')).textContent = _('当前共 ') + totalDevices + _(' 台设备在线'); + container.appendChild(createTable()); + container.appendChild(document.createElement('style')).textContent = style; + + function sortTable(column) { + var table = container.querySelector('.device-table'); + var tbody = table.querySelector('tbody'); + var rows = Array.from(tbody.querySelectorAll('tr')); + + var isAscending = true; + + if (table.classList.contains('sorted') && table.dataset.sortColumn === column) { + isAscending = !table.classList.contains('asc'); + } + + rows.sort(function (row1, row2) { + var value1 = row1.querySelector('td:nth-of-type(' + (visibleColumns.indexOf(column) + 1) + ')').textContent.toLowerCase(); + var value2 = row2.querySelector('td:nth-of-type(' + (visibleColumns.indexOf(column) + 1) + ')').textContent.toLowerCase(); + + if (value1 < value2) { + return isAscending ? -1 : 1; + } else if (value1 > value2) { + return isAscending ? 1 : -1; + } + + return 0; + }); + + tbody.innerHTML = ''; + + rows.forEach(function (row) { + tbody.appendChild(row); + }); + + table.classList.remove('sorted', 'asc', 'desc'); + if (isAscending) { + table.classList.add('sorted', 'asc'); + } else { + table.classList.add('sorted', 'desc'); + } + table.dataset.sortColumn = column; + } + + container.addEventListener('click', function (event) { + if ( + event.target.tagName === 'TH' && + event.target.parentNode.rowIndex === 0 + ) { + var columnIndex = event.target.cellIndex; + var table = container.querySelector('.device-table'); + var tbody = table.querySelector('tbody'); + var rows = Array.from(tbody.querySelectorAll('tr')); + + rows.sort(function (row1, row2) { + var value1 = row1.cells[columnIndex].textContent.trim(); + var value2 = row2.cells[columnIndex].textContent.trim(); + + if (columnIndex === 0) { + return value1.length - value2.length; + } else if (columnIndex === 1) { + value1 = ipToNumber(value1); + value2 = ipToNumber(value2); + } else if (columnIndex === 4) { + value1 = parseOnlineTime(value1); + value2 = parseOnlineTime(value2); + } + + if (value1 < value2) { + return -1; + } else if (value1 > value2) { + return 1; + } + + return 0; + }); + + rows.forEach(function (row) { + tbody.appendChild(row); + }); + } + }); + + function ipToNumber(ipAddress) { + var parts = ipAddress.split('.'); + var number = 0; + + for (var i = 0; i < parts.length; i++) { + number = number * 256 + parseInt(parts[i]); + } + + return number; + } + + function parseOnlineTime(time) { + var regex = /(\d+)\s+(小时|分钟|秒)/g; + var matches = time.matchAll(regex); + var minutes = 0; + + for (var match of matches) { + var value = parseInt(match[1]); + var unit = match[2]; + + if (unit === '小时') { + minutes += value * 60; + } else if (unit === '分钟') { + minutes += value; + } + } + + return minutes; + } + return container; + }, + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js new file mode 100644 index 00000000..a962982f --- /dev/null +++ b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js @@ -0,0 +1,696 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; +'require uci'; +'require rpc'; +'require form'; + +'require poll'; +'require tools.widgets as widgets'; +'require tools.firewall as fwtool'; + +var callServiceList = rpc.declare({ + object: 'service', + method: 'list', + params: ['name'], + expect: { '': {} } +}); + +function getServiceStatus() { + return L.resolveDefault(callServiceList('wechatpush'), {}).then(function (res) { + console.log(res); + var isRunning = false; + try { + isRunning = res['wechatpush']['instances']['instance1']['running']; + } catch (e) { } + return isRunning; + }); +} + +function renderStatus(isRunning) { + var spanTemp = '%s %s'; + var renderHTML; + if (isRunning) { + renderHTML = String.format(spanTemp, 'green', _('wechatpush'), _('RUNNING')); + } else { + renderHTML = String.format(spanTemp, 'red', _('wechatpush'), _('NOT RUNNING')); + } + + return renderHTML; +} + +var cbiRichListValue = form.ListValue.extend({ + renderWidget: function(section_id, option_index, cfgvalue) { + var choices = this.transformChoices(); + var widget = new ui.Dropdown((cfgvalue != null) ? cfgvalue : this.default, choices, { + id: this.cbid(section_id), + sort: this.keylist, + optional: true, + select_placeholder: this.select_placeholder || this.placeholder, + custom_placeholder: this.custom_placeholder || this.placeholder, + validate: L.bind(this.validate, this, section_id), + disabled: (this.readonly != null) ? this.readonly : this.map.readonly + }); + + return widget.render(); + }, + + value: function(value, title, description) { + if (description) { + form.ListValue.prototype.value.call(this, value, E([], [ + E('span', { 'class': 'hide-open' }, [ title ]), + E('div', { 'class': 'hide-close', 'style': 'min-width:25vw' }, [ + E('strong', [ title ]), + E('br'), + E('span', { 'style': 'white-space:normal' }, description) + ]) + ])); + } + else { + form.ListValue.prototype.value.call(this, value, title); + } + } +}); + +return view.extend({ + callHostHints: rpc.declare({ + object: 'luci-rpc', + method: 'getHostHints', + expect: { '': {} } + }), + + load: function () { + return Promise.all([ + this.callHostHints() + ]); + }, + + render: function (data) { + if (fwtool.checkLegacySNAT()) + return fwtool.renderMigration(); + else + return this.renderForwards(data); + }, + + renderForwards: function (data) { + var hosts = data[0], + m, s, o, + programPath = '/usr/share/wechatpush/wechatpush'; + + m = new form.Map('wechatpush', _('WeChat push'), _('A tool that can push device messages from OpenWrt to a mobile phone via WeChat or Telegram.

If you encounter any issues while using it, please submit them here:') + '' + _('GitHub Project Address') + ''); + + s = m.section(form.TypedSection); + s.anonymous = true; + s.render = function () { + var statusView = E('p', { id: 'service_status' }, _('Collecting data ...')); + poll.add(function () { + return L.resolveDefault(getServiceStatus()).then(function (res) { + statusView.innerHTML = renderStatus(res); + }); + }); + + setTimeout(function () { + poll.start(); + }, 100); + + return E('div', { class: 'cbi-section', id: 'status_bar' }, [ + statusView + ]); + } + + s = m.section(form.NamedSection, 'config', 'wechatpush', _('')); + s.tab('basic', _('Basic Settings')); + s.tab('content', _('Push Content')); + s.tab('ipset', _('Auto Ban')); + s.tab('crontab', _('Scheduled Push')); + s.tab('disturb', _('Do Not Disturb')); + s.addremove = false; + s.anonymous = true; + + // 基本设置 + o = s.taboption('basic', form.Flag, 'enable', _('Enabled')); + + o = s.taboption('basic', cbiRichListValue, 'jsonpath', _('Push Mode')); + o.value('/usr/share/wechatpush/api/serverchan.json', _('WeChat serverchan'), + _('Using serverchan API, simple configuration, supports multiple push methods')); + o.value('/usr/share/wechatpush/api/qywx_mpnews.json', _('WeChat Work Image Message'), + _('Using WeChat Work application message, more complex configuration, and starting from June 20, 2022, additional configuration for trusted IP is required. Trusted IP cannot be shared. This channel is no longer recommended.')); + o.value('/usr/share/wechatpush/api/qywx_markdown.json', _('WeChat Work Markdown Version'), + _('WeChat Work application message in plain text format, no need to click the title to view the content, same as above')); + o.value('/usr/share/wechatpush/api/wxpusher.json', _('wxpusher'), + _('Another channel for WeChat push, the configuration is relatively simple, and only supports official accounts')); + o.value('/usr/share/wechatpush/api/pushplus.json', _('pushplus'), + _('Another channel for WeChat push, the configuration is relatively simple, and it supports multiple push methods')); + o.value('/usr/share/wechatpush/api/telegram.json', _('Telegram'), + _('Telegram Bot Push')); + o.value('/usr/share/wechatpush/api/diy.json', _('Custom Push'), + _('By modifying the JSON file, you can use a custom API')); + + o = s.taboption('basic', form.Value, 'sckey', _('「wechatpush」sendkey')); + o.description = _('Get Instructions') + ' ' + _('Click here') + ''; + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/serverchan.json'); + + o = s.taboption('basic', form.Value, 'corpid', _('corpid')); + o.description = _('Get Instructions') + ' ' + _('Click here') + ''; + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_mpnews.json'); + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_markdown.json'); + + o = s.taboption('basic', form.Value, 'userid', _('userid')); + o.rmempty = false; + o.description = _('Send to All App Users, enter @all'); + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_mpnews.json'); + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_markdown.json'); + + o = s.taboption('basic', form.Value, 'agentid', _('agentid')); + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_mpnews.json'); + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_markdown.json'); + + o = s.taboption('basic', form.Value, 'corpsecret', _('Secret')); + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_mpnews.json'); + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_markdown.json'); + + o = s.taboption('basic', form.Value, 'mediapath', _('Thumbnail Image File Path')) + o.rmempty = false; + o.default = '/usr/share/wechatpush/api/logo.jpg'; + o.depends('jsonpath', '/usr/share/wechatpush/api/qywx_mpnews.json'); + o.description = _('Supports JPG and PNG formats within 2MB
Optimal size: 900383 or 2.35:1'); + + o = s.taboption('basic', form.Value, 'wxpusher_apptoken', _('appToken')); + o.description = _('Get Instructions') + ' ' + _('Click here') + ''; + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/wxpusher.json'); + + o = s.taboption('basic', form.Value, 'wxpusher_uids', _('uids')); + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/wxpusher.json'); + + o = s.taboption('basic', form.Value, 'wxpusher_topicIds', _('topicIds(Mass sending)')); + o.description = _('Get Instructions') + ' ' + _('Click here') + ''; + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/wxpusher.json'); + + o = s.taboption('basic', form.Value, 'pushplus_token', _('pushplus_token')); + o.description = _('Get Instructions') + ' ' + _('Click here') + ''; + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/pushplus.json'); + + o = s.taboption('basic', form.Value, 'tg_token', _('TG_token')); + o.description = _('Get Bot') + ' ' + _('Click here') + '' + _('
Send a message to the created bot to initiate a conversation.'); + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/telegram.json'); + + o = s.taboption('basic', form.Value, 'chat_id', _('TG_chatid')); + o.description = _('Get chat_id') + ' ' + _('Click here') + '' + _('
If you want to send to a group/channel, please create a non-Chinese group/channel (for easier chatid lookup, you can rename it later).
Add the bot to the group, send a message, and use https://api.telegram.org/bot token /getUpdates to obtain the chatid.'); + o.rmempty = false; + o.depends('jsonpath', '/usr/share/wechatpush/api/telegram.json'); + + o = s.taboption('basic', form.TextValue, 'diy_json', _('Custom Push')); + o.rows = 28; + o.wrap = 'oft'; + o.cfgvalue = function (section_id) { + return fs.trimmed('/usr/share/wechatpush/api/diy.json'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return + } + return fs.write('/usr/share/wechatpush/api/diy.json', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); + }); + }; + o.description = _('Please refer to the comments and other interface files for modifications. Limited resources, no longer supporting more interfaces, please debug on your own.
You can use a similar website to check the JSON file format: https://www.google.com/search?q=JSON+Parser+Online
Please use the 「Save」 button in the text box.'); + o.depends('jsonpath', '/usr/share/wechatpush/api/diy.json'); + + + o = s.taboption('basic', form.Button, '_test', _('Send Test'), _('You may need to save the configuration before sending.')); + o.inputstyle = 'add'; + o.onclick = function () { + var _this = this; + return fs.exec(programPath, ['test']).then(function (res) { + if (res.code === 0) + _this.description = _('Message sent successfully. If you don\'t receive the message, please check the logs for manual processing.'); + else if (res.code === 1) + _this.description = _('Sending failed'); + + return _this.map.reset(); + }).catch(function (err) { + ui.addNotification(null, E('p', [_('Unknown error: %s.').format(err)])); + _this.description = _('Sending failed'); + return _this.map.reset(); + }); + } + + o = s.taboption('basic', form.Value, 'device_name', _('Device Name')); + o.description = _('The device name will be displayed in the push message title to identify the source device of the message.'); + + o = s.taboption('basic', form.Value, 'sleeptime', _('Check Interval (s)')); + o.rmempty = false; + o.placeholder = '60'; + o.datatype = 'and(uinteger,min(10))'; + o.description = _('Shorter intervals provide quicker response but consume more system resources.'); + + o = s.taboption('basic', cbiRichListValue, 'oui_data', _('MAC Device Database')); + o.value('', _('Close'), + _('Do not use MAC device database')); + o.value('1', _('Simplified Version'), + _('Includes common device manufacturers, occupies approximately 200Kb of space')); + o.value('2', _('Full Version'), + _('Download the complete database, processed size is approximately 1.3Mb')); + o.value('3', _('Network Query'), + _('Enables network query, slower response. Only use if space is limited.')); + + o = s.taboption('basic', form.Button, '_update_oui', _('Update MAC Device Database')); + o.inputstyle = 'add'; + o.onclick = function () { + var _this = this; + return fs.exec('/usr/libexec/wechatpush-call', ['down_oui']).then(function (res) { + if (res.code === 2) { + _this.description = _('Database is up to date, skipping update'); + } + return _this.map.reset(); + }).catch(function (err) { + ui.addNotification(null, E('p', [_('Unknown error: %s.').format(err)])); + _this.description = _('Browser timeout or unknown error. If the log shows that the update process has been established, please ignore this error: %s.'); + return _this.map.reset(); + }); + }; + o.depends('oui_data', '1'); + o.depends('oui_data', '2'); + + o = s.taboption('basic', form.Flag, 'reset_regularly', _('Reset Traffic Data Every Day at Midnight')); + + o = s.taboption('basic', form.Flag, 'debuglevel', _('Enable Logging')); + + o = s.taboption('basic', form.TextValue, '_device_aliases', _('Device Alias')); + o.rows = 20; + o.wrap = 'oft'; + o.cfgvalue = function (section_id) { + return fs.trimmed('/usr/share/wechatpush/api/device_aliases.list'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return + } + return fs.write('/usr/share/wechatpush/api/device_aliases.list', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); + }); + }; + o.description = _('Please enter the device MAC and device alias separated by a space, such as:
XX:XX:XX:XX:XX:XX My Phone
192.168.1.2 My PC
Please use the 「Save」 button in the text box.'); + + // 推送内容 + o = s.taboption('content', cbiRichListValue, 'get_ipv4_mode', _('IPv4 Dynamic Notification')); + o.value('', _('Close'), + _(' ')); + o.value('1', _('Obtain through interface'), + _(' ')); + o.value('2', _('Obtain through URL'), + _('May fail due to server stability and frequent connections.
If the interface can obtain the IP address properly, it is not recommended to use this method.')); + + o = s.taboption('content', widgets.DeviceSelect, 'ipv4_interface', _("Device")); + o.description = _('Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly.'); + o.modalonly = true; + o.multiple = false; + o.default = 'WAN'; + o.depends('get_ipv4_mode', '1'); + + o = s.taboption('content', form.TextValue, 'ipv4_list', _('IPv4 API List')); + o.depends('get_ipv4_mode', '2'); + o.optional = false; + o.rows = 8; + o.wrap = 'oft'; + o.cfgvalue = function (section_id) { + return fs.trimmed('/usr/share/wechatpush/api/ipv4.list'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return + } + return fs.write('/usr/share/wechatpush/api/ipv4.list', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); + }); + }; + o.description = _('Access a random address from the list above,URLs in the list are specific to Chinese websites. If you need to use this feature, please replace the URLs with the ones available to you.
Please use the 「Save」 button in the text box.'); + + o = s.taboption('content', form.Button, '_update_ipv4_list', _('Update IPv4 list')); + o.inputstyle = 'add'; + o.onclick = function () { + var _this = this; + return fs.exec('/usr/libexec/wechatpush-call', ['update_ip_list', 'ipv4']).then(function (res) { + if (res.code === 0) + _this.description = _('Update successful'); + else if (res.code === 1) + _this.description = _('Update failed'); + return _this.map.reset(); + }).catch(function (err) { + ui.addNotification(null, E('p', [_('Unknown error: %s.').format(err)])); + _this.description = _('Update failed'); + return _this.map.reset(); + }); + } + o.depends('get_ipv4_mode', '2'); + + o = s.taboption('content', cbiRichListValue, 'get_ipv6_mode', _('IPv6 Dynamic Notification')); + o.value('', _('Close'), + _(' ')); + o.value('1', _('Obtain through interface'), + _(' ')); + o.value('2', _('Obtain through URL'), + _('May fail due to server stability and frequent connections.
If the interface can obtain the IP address properly, it is not recommended to use this method.')); + + o = s.taboption('content', widgets.DeviceSelect, 'ipv6_interface', _("Device")); + o.description = _('Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly.'); + o.modalonly = true; + o.multiple = false; + o.default = 'WAN'; + o.depends('get_ipv6_mode', '1'); + + o = s.taboption('content', form.TextValue, 'ipv6_list', _('IPv6 API List')); + o.depends('get_ipv6_mode', '2') + o.optional = false; + o.rows = 8; + o.wrap = 'oft'; + o.cfgvalue = function (section_id) { + return fs.trimmed('/usr/share/wechatpush/api/ipv6.list'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return + } + return fs.write('/usr/share/wechatpush/api/ipv6.list', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); + }); + }; + o.description = _('Access a random address from the list above,URLs in the list are specific to Chinese websites. If you need to use this feature, please replace the URLs with the ones available to you.
Please use the 「Save」 button in the text box.'); + + o = s.taboption('content', form.Button, '_update_ipv6_list', _('Update IPv6 list')); + o.inputstyle = 'add'; + o.onclick = function () { + var _this = this; + return fs.exec('/usr/libexec/wechatpush-call', ['update_ip_list', 'ipv6']).then(function (res) { + if (res.code === 0) + _this.description = _('Update successful'); + else if (res.code === 1) + _this.description = _('Update failed'); + return _this.map.reset(); + }).catch(function (err) { + ui.addNotification(null, E('p', [_('Unknown error: %s.').format(err)])); + _this.description = _('Update failed'); + return _this.map.reset(); + }); + } + o.depends('get_ipv6_mode', '2'); + + o = s.taboption('content', form.Flag, 'auto_update_ip_list', _('Automatically update API list')); + o.description = _('When multiple IP retrieval attempts fail, try to automatically update the list file from GitHub'); + o.depends('get_ipv4_mode', '2'); + o.depends('get_ipv6_mode', '2'); + + o = s.taboption('content', form.MultiValue, 'device_notification', _('Device Online/Offline Notification')); + o.value('online', _('Online Notification')); + o.value('offline', _('Offline Notification')); + o.modalonly = true; + + o = s.taboption('content', form.MultiValue, 'cpu_notification', _('CPU Alert')); + o.value('load', _('Load Alert')); + o.value('temp', _('Temperature Alert')); + o.modalonly = true; + o.description = _('Device alert will be triggered only if it exceeds the set value continuously for five minutes, and there won\'t be a second reminder within an hour.'); + + o = s.taboption('content', form.Value, 'cpu_load_threshold', _('Load alert threshold')); + o.rmempty = false; + o.placeholder = '2'; + o.depends({ cpu_notification: "load", '!contains': true }); + o.validate = function (section_id, value) { + var floatValue = parseFloat(value); + if (!isNaN(floatValue) && floatValue.toString() === value) { + return true; + } + return 'Please enter a numeric value only'; + }; + + o = s.taboption('content', form.Value, 'temperature_threshold', _('Temperature alert threshold')); + o.rmempty = false; + o.placeholder = '80'; + o.datatype = 'and(uinteger,min(1))'; + o.depends({ cpu_notification: "temp", '!contains': true }); + o.description = _('Please confirm that the device can retrieve temperature. If you need to modify the command, please go to advanced settings.'); + + o = s.taboption('content', form.MultiValue, 'login_notification', _('Login Notification')); + o.value('web_logged', _('Web Login')); + o.value('ssh_logged', _('SSH Login')); + o.value('web_login_failed', _('Frequent Web Login Errors')); + o.value('ssh_login_failed', _('Frequent SSH Login Errors')); + o.modalonly = true; + + o = s.taboption('content', form.Value, 'login_max_num', _('Login failure count')); + o.default = '3'; + o.rmempty = false; + o.datatype = 'and(uinteger,min(1))'; + o.depends({ login_notification: "web_login_failed", '!contains': true }); + o.depends({ login_notification: "ssh_login_failed", '!contains': true }); + o.description = _('Send notification after exceeding the count, and optionally auto-ban IP'); + + o = s.taboption('content', form.Flag, 'client_usage', _('Device abnormal traffic alert')); + o.description = _('Please ensure that you can retrieve device traffic information correctly, otherwise this feature will not work properly'); + o.default = '0'; + + o = s.taboption('content', form.Value, 'client_usage_max', _('Per-minute traffic limit')); + o.placeholder = '10M'; + o.rmempty = false; + o.depends('client_usage', '1'); + o.description = _('Abnormal traffic alert (byte), you can append K or M'); + + o = s.taboption('content', form.Flag, 'client_usage_disturb', _('Abnormal traffic do not disturb')); + o.default = '0'; + o.depends('client_usage', '1'); + + o = fwtool.addMACOption(s, 'content', 'client_usage_whitelist', _('Abnormal traffic monitoring list'), + _('Please select device MAC'), hosts); + o.rmempty = true; + o.datatype = 'list(neg(macaddr))'; + o.depends('client_usage_disturb', '1'); + + // 自动封禁 + o = s.taboption('ipset', form.Flag, 'login_web_black', _('Auto-ban unauthorized login devices')); + o.default = '0'; + o.depends({ login_notification: "web_login_failed", '!contains': true }); + o.depends({ login_notification: "ssh_login_failed", '!contains': true }); + + o = s.taboption('ipset', form.Value, 'login_ip_black_timeout', _('Blacklisting time (s)')); + o.default = '86400'; + o.rmempty = false; + o.datatype = 'and(uinteger,min(0))'; + o.depends('login_web_black', '1'); + o.description = _('\"0\" in ipset means permanent blacklist, use with caution. If misconfigured, change the device IP and clear rules in LUCI.'); + + o = s.taboption('ipset', form.Flag, 'port_knocking_enable', _('Port knocking')); + o.default = '0'; + o.description = _('If you have disabled LAN port inbound and forwarding in Firewall - Zone Settings, it won\'t work.'); + o.depends({ login_notification: "web_login_failed", '!contains': true }); + o.depends({ login_notification: "ssh_login_failed", '!contains': true }); + + o = s.taboption('ipset', form.Value, 'login_port_white', _('Port')); + o.default = ''; + o.description = _('Open port after successful login
example:\"22\"、\"21:25\"、\"21:25,135:139\"'); + o.depends('port_knocking_enable', '1'); + + o = s.taboption('ipset', form.DynamicList, 'login_port_forward_list', _('Port Forwards')); + o.default = ''; + o.description = _('Example: Forward port 13389 of this device (IPv4:10.0.0.1 / IPv6:fe80::10:0:0:2) to port 3389 of (IPv4:10.0.0.2 / IPv6:fe80::10:0:0:8)
\"10.0.0.1,13389,10.0.0.2,3389\"
\"fe80::10:0:0:1,13389,fe80::10:0:0:2,3389\"'); + o.depends('port_knocking_enable', '1'); + + o = s.taboption('ipset', form.Value, 'login_ip_white_timeout', _('Release time (s)')); + o.default = '86400'; + o.datatype = 'and(uinteger,min(0))'; + o.description = _('\"0\" in ipset means permanent release, use with caution'); + o.depends('port_knocking_enable', '1'); + + o = s.taboption('ipset', form.TextValue, 'ip_black_list', _('IP blacklist')); + o.rows = 8; + o.wrap = 'soft'; + o.cfgvalue = function (section_id) { + return fs.trimmed('/usr/share/wechatpush/api/ip_blacklist'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return + } + return fs.write('/usr/share/wechatpush/api/ip_blacklist', formvalue.trim().replace(/\r\n/g, '\n') + '\n'); + }); + }; + o.depends('login_web_black', '1'); + o.description = _('You can add or delete here, the numbers after represent the remaining time. When adding, only the IP needs to be entered.
When clearing, please leave a blank line, otherwise it cannot be saved ╮(╯_╰)╭
Please use the 「Save」 button in the text box.'); + + // 定时推送 + o = s.taboption('crontab', cbiRichListValue, 'crontab_mode', _('Scheduled Tasks')); + o.value('', _('Close'), + _(' ')); + o.value('1', _('Scheduled sending'), + _('Send at the same time every day')); + o.value('2', _('Interval sending'), + _('Starting from 00:00, send every * hours')); + + o = s.taboption('crontab', form.MultiValue, 'crontab_regular_time', _('Sending time')); + for (var t = 0; t <= 23; t++) { + o.value(t, _('Every day') + t + _('clock')); + } + o.modalonly = true; + o.depends("crontab_mode", "1"); + + o = s.taboption('crontab', form.ListValue, 'crontab_interval_time', _('Interval sending')); + o.default = "6" + for (var t = 0; t <= 12; t++) { + o.value(t, _("") + t + _("Hour")); + } + o.default = ''; + o.datatype = "uinteger"; + o.depends('crontab_mode', '2'); + o.description = _('Starting from 00:00, send every * hours'); + + o = s.taboption('crontab', form.Value, 'send_title', _('Push title')); + o.depends('crontab_mode', '1'); + o.depends('crontab_mode', '2'); + o.placeholder = _('OpenWrt Router Status:'); + o.description = _('Using special characters may cause sending failure'); + + o = s.taboption('crontab', form.MultiValue, 'send_notification', _('Push content')); + o.value('router_status', _('System running status')); + o.value('router_temp', _('Device temperature')); + o.value('wan_info', _('WAN info')); + o.value('client_list', _('Client list')); + o.modalonly = true; + o.depends('crontab_mode', '1'); + o.depends('crontab_mode', '2'); + + o = s.taboption('crontab', form.Button, '_send', _('Manual sending'), _('You may need to save the configuration before sending.
Due to browser timeout limitations, if the program is not running, it may exit due to timeout during device list initialization')); + o.inputstyle = 'add'; + o.onclick = function () { + var _this = this; + return fs.exec(programPath, ['send']).then(function (res) { + if (res.code === 0) + _this.description = _('Message sent successfully. If you don\'t receive the message, please check the logs for manual processing.'); + else if (res.code === 1) + _this.description = _('Sending failed'); + + return _this.map.reset(); + }).catch(function (err) { + ui.addNotification(null, E('p', [_('Unknown error: %s.').format(err)])); + _this.description = _('Sending failed'); + return _this.map.reset(); + }); + } + + // 免打扰 + o = s.taboption('disturb', form.MultiValue, 'lite_enable', _('Simplified mode')); + o.value('device', _('Simplify the current device list')); + o.value('nowtime', _('Simplify the current time')); + o.value('content', _('Push only the title')); + + o = s.taboption('disturb', cbiRichListValue, 'do_not_disturb_mode', _('Do Not Disturb time setting')); + o.value('', _('Close'), + _(' ')); + o.value('1', _('Mode 1: Script Suspension'), + _('Suspend all script actions, including unattended tasks, until the end of the time period')); + o.value('2', _('Mode 2: Silent Mode'), + _('Stop sending notifications, but log normally')); + + o = s.taboption('disturb', form.ListValue, 'do_not_disturb_starttime', _('Do Not Disturb start time')); + for (var t = 0; t <= 23; t++) { + o.value(t, _('Every day') + t + _('clock')); + } + o.default = '0'; + o.datatype = "uinteger" + o.depends('do_not_disturb_mode', '1'); + o.depends('do_not_disturb_mode', '2'); + + o = s.taboption('disturb', form.ListValue, 'do_not_disturb_endtime', _('Do Not Disturb end time')); + for (var t = 0; t <= 23; t++) { + o.value(t, _('Every day') + t + _('clock')); + } + o.default = 8 + o.datatype = "uinteger" + o.depends('do_not_disturb_mode', '1'); + o.depends('do_not_disturb_mode', '2'); + + o = s.taboption('disturb', cbiRichListValue, 'mac_filtering_mode_1', _('MAC Filtering Mode 1')); + o.value('', _('Close'), + _(' ')); + o.value('allow', _('Ignore devices in the list'), + _('Ignored devices will not receive notifications or be logged')); + o.value('block', _('Notify only devices in the list'), + _('Ignored devices will not receive notifications or be logged')); + o.value('interface', _('Notify only devices using this interface'), + _('Multiple selections are not supported at the moment')); + + o = fwtool.addMACOption(s, 'disturb', 'up_down_push_whitelist', _('Ignored device list'), + _('Please select device MAC'), hosts); + o.datatype = 'list(neg(macaddr))'; + o.depends('mac_filtering_mode_1', 'allow'); + + o = fwtool.addMACOption(s, 'disturb', 'up_down_push_blacklist', _('Followed device list'), + _('Please select device MAC'), hosts); + o.datatype = 'list(neg(macaddr))'; + o.depends('mac_filtering_mode_1', 'block'); + o.description = _('AA:AA:AA:AA:AA:AA\\|BB:BB:BB:BB:BB:BB Multiple MAC addresses can be treated as the same user.
Notifications will not be sent once any device is online, and notifications will only be sent when all devices are offline to avoid frequent notifications in dual Wi-Fi scenarios.'); // 有点问题,待修复 + + o = s.taboption('disturb', widgets.DeviceSelect, 'up_down_push_interface', _("Device")); + o.description = _('Notify only devices using this interface'); + o.modalonly = true; + o.multiple = false; + o.depends('mac_filtering_mode_1', 'interface'); + + o = s.taboption('disturb', cbiRichListValue, 'mac_filtering_mode_2', _('MAC Filtering Mode 2')); + o.value('', _('Close'), + _(' ')); + o.value('mac_online', _('Do Not Disturb when devices are online'), + _('No notifications will be sent when any device in the list is online')); + o.value('mac_offline', _('Do Not Disturb when devices are offline'), + _('No notifications will be sent when any device in the list is offline')); + + o = fwtool.addMACOption(s, 'disturb', 'mac_online_list', _('Do Not Disturb device online list'), + _('Please select device MAC'), hosts); + o.datatype = 'list(neg(macaddr))'; + o.depends('mac_filtering_mode_2', 'mac_online'); + + o = fwtool.addMACOption(s, 'disturb', 'mac_offline_list', _('Do Not Disturb device offline list'), + _('Please select device MAC'), hosts); + o.datatype = 'list(neg(macaddr))'; + o.depends('mac_filtering_mode_2', 'mac_offline'); + + o = s.taboption('disturb', cbiRichListValue, 'login_disturb', _('Do Not Disturb for Login Reminders')); + o.value('', _('Close'), + _(' ')); + o.value('1', _('Only record in the log'), + _('Ignore all login reminders and only record in the log')); + o.value('2', _('Send notification only on the first login'), + _('Send notification only once within the specified time interval')); + + o = s.taboption('disturb', form.Value, 'login_notification_delay', _('Login reminder do not disturb time (s)')); + o.rmempty = false; + o.placeholder = '3600'; + o.datatype = 'and(uinteger,min(10))'; + o.description = _('Send notification after the first login and do not repeat within the specified time
Take a shortcut and read the login time from the log'); + o.depends('login_disturb', '2'); + + o = fwtool.addIPOption(s, 'disturb', 'login_ip_white_list', _('Login reminder whitelist'), null, 'ipv4', hosts, true); + o.datatype = 'ipaddr'; + o.depends({ login_notification: "web_logged", '!contains': true }); + o.depends({ login_notification: "ssh_logged", '!contains': true }); + o.depends({ login_notification: "web_login_failed", '!contains': true }); + o.depends({ login_notification: "ssh_login_failed", '!contains': true }); + o.description = _('Add the IP addresses in the list to the whitelist for the blocking function (if available), and ignore automatic blocking and login event notifications. Only record in the log. Mask notation is currently not supported.'); + + o = s.taboption('disturb', form.Flag, 'login_log_enable', _('Login reminder log anti-flooding')); + o.description = _('Users in the whitelist or during the undisturbed time period after their first login IP will be exempt from log recording, preventing log flooding.'); + o.depends('login_disturb', '1'); + o.depends('login_disturb', '2'); + + return m.render(); + } +}); diff --git a/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js new file mode 100644 index 00000000..52fc89a4 --- /dev/null +++ b/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js @@ -0,0 +1,133 @@ +'use strict'; +'require dom'; +'require fs'; +'require poll'; +'require uci'; +'require view'; +'require form'; + +return view.extend({ + render: function () { + var css = ` + #log_textarea { + margin-top: 10px; + } + #log_textarea pre { + background-color: #f7f7f7; + color: #333; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-family: Consolas, Menlo, Monaco, monospace; + font-size: 14px; + line-height: 1.5; + white-space: pre-wrap; + word-wrap: break-word; + overflow-y: auto; + max-height: 400px; + } + #.description { + background-color: #33ccff; + } + .cbi-button-danger { + background-color: #fff; + color: #f00; + border: 1px solid #f00; + border-radius: 4px; + padding: 4px 8px; + font-size: 14px; + cursor: pointer; + margin-top: 10px; + } + .cbi-button-danger:hover { + background-color: #f00; + color: #fff; + } + .cbi-section small { + margin-left: 10px; + } + .cbi-section .cbi-section-actions { + margin-top: 10px; + } + .cbi-section .cbi-section-actions-right { + text-align: right; + } + `; + + + var log_textarea = E('div', { 'id': 'log_textarea' }, + E('img', { + 'src': L.resource(['icons/loading.gif']), + 'alt': _('Loading...'), + 'style': 'vertical-align:middle' + }, _('Collecting data ...')) + ); + + var log_path = '/tmp/wechatpush/wechatpush.log'; + + var clear_log_button = E('div', {}, [ + E('button', { + 'class': 'cbi-button cbi-button-danger', + 'click': function (ev) { + ev.preventDefault(); + var button = ev.target; + button.disabled = true; + button.textContent = _('Clear Logs...'); + fs.exec_direct('/usr/libexec/wechatpush-call', ['clear_log']) + .then(function () { + button.textContent = _('Logs cleared successfully!'); + setTimeout(function () { + button.disabled = false; + button.textContent = _('Clear Logs'); + }, 5000); + // 立即刷新日志显示框 + var log = E('pre', { 'wrap': 'pre' }, [_('Log is clean.')]); + dom.content(log_textarea, log); + }) + .catch(function () { + button.textContent = _('Failed to clear log.'); + setTimeout(function () { + button.disabled = false; + button.textContent = _('Clear Logs'); + }, 5000); + }); + } + }, _('Clear Logs')) + ]); + + + poll.add(L.bind(function () { + return fs.read_direct(log_path, 'text') + .then(function (res) { + var log = E('pre', { 'wrap': 'pre' }, [res.trim() || _('Log is clean.')]); + + dom.content(log_textarea, log); + log.scrollTop = log.scrollHeight; + }).catch(function (err) { + var log; + + if (err.toString().includes('NotFoundError')) { + log = E('pre', { 'wrap': 'pre' }, [_('Log file does not exist.')]); + } else { + log = E('pre', { 'wrap': 'pre' }, [_('Unknown error: %s').format(err)]); + } + + dom.content(log_textarea, log); + }); + })); + + return E('div', { 'class': 'cbi-map' }, [ + E('style', [css]), + E('div', { 'class': 'cbi-section' }, [ + clear_log_button, + log_textarea, + E('small', {}, _('Refresh every 5 seconds.').format(L.env.pollinterval)), + E('div', { 'class': 'cbi-section-actions cbi-section-actions-right' }) + ]) + ]); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/luci-app-wechatpush/po/zh-cn b/luci-app-wechatpush/po/zh-cn new file mode 120000 index 00000000..8d69574d --- /dev/null +++ b/luci-app-wechatpush/po/zh-cn @@ -0,0 +1 @@ +zh_Hans \ No newline at end of file diff --git a/luci-app-wechatpush/po/zh_Hans/wechatpush.po b/luci-app-wechatpush/po/zh_Hans/wechatpush.po new file mode 100644 index 00000000..cf2cad7c --- /dev/null +++ b/luci-app-wechatpush/po/zh_Hans/wechatpush.po @@ -0,0 +1,976 @@ +msgid "" +msgstr "" +"PO-Revision-Date: 2023-06-01 20:00+0000\n" +"Last-Translator: https://github.com/tty228/luci-app-wechatpush \n" +"Language-Team: \n" +"Language: zh_Hans\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Weblate 4.18-dev\n" + +#: applications/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json:3 +msgid "WeChat push" +msgstr "微信推送" + +#: applications/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json:15 +msgid "Basic Settings" +msgstr "基本设置" + +#: applications/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json:24 +msgid "Advance Setting" +msgstr "高级设置" + +#: applications/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json:33 +msgid "Online Devices" +msgstr "在线设备" + +#: applications/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json:33 +msgid "Log" +msgstr "日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:101 +msgid "A tool that can push device messages from OpenWrt to a mobile phone via WeChat or Telegram.

If you encounter any issues while using it, please submit them here:" +msgstr "一个通过微信或 Telegram,从 OpenWrt 上推送设备消息到手机的工具

如果你在使用中遇到问题,请到这里提交:" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:101 +msgid "GitHub Project Address" +msgstr "GitHub 项目地址" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:124 +msgid "Push Content" +msgstr "推送内容" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:125 +msgid "Auto Ban" +msgstr "自动封禁" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:126 +msgid "Scheduled Push" +msgstr "定时推送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:127 +msgid "Do Not Disturb" +msgstr "免打扰" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:134 +msgid "Push Mode" +msgstr "推送模式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:135 +msgid "WeChat serverchan" +msgstr "微信 Server 酱" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:136 +msgid "Using serverchan API, simple configuration, supports multiple push methods" +msgstr "使用 Server酱 接口,配置较简单,支持多项推送方式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:137 +msgid "WeChat Work Image Message" +msgstr "企业微信 图文消息" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:138 +msgid "Using WeChat Work application message, more complex configuration, and starting from June 20, 2022, additional configuration for trusted IP is required. Trusted IP cannot be shared. This channel is no longer recommended." +msgstr "使用 企业微信应用消息,配置较为麻烦,且 2022 年 6 月 20 日之后新创建的应用,需要额外配置可信 IP,可信 IP 不可公用,不再推荐此通道" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:139 +msgid "WeChat Work Markdown Version" +msgstr "企业微信 markdown 版" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:140 +msgid "WeChat Work application message in plain text format, no need to click the title to view the content, same as above" +msgstr "企业微信应用消息 纯文字版,无需点击标题查看内容,其他同上" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:142 +msgid "Another channel for WeChat push, the configuration is relatively simple, and only supports official accounts" +msgstr "微信推送的另一个通道,配置较简单,只支持公众号" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:144 +msgid "Another channel for WeChat push, the configuration is relatively simple, and it supports multiple push methods" +msgstr "微信推送的另一个通道,配置较简单,支持多项推送方式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:146 +msgid "Telegram Bot Push" +msgstr "Telegram bot 推送,若无梯子,请勿使用" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:147 +msgid "Custom Push" +msgstr "自定义推送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:148 +msgid "By modifying the JSON file, you can use a custom API" +msgstr "通过修改 JSON 文件,使用自定义接口" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:150 +msgid "「wechatpush」sendkey" +msgstr "「Server酱」sendkey" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:151 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:156 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:184 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:193 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:198 +msgid "Get Instructions" +msgstr "获取说明" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:151 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:156 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:184 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:193 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:198 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:203 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:208 +msgid "Click here" +msgstr "点击这里" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:155 +msgid "corpid" +msgstr "企业ID(corpid)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:161 +msgid "userid" +msgstr "帐号(userid)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:163 +msgid "Send to All App Users, enter @all" +msgstr "群发到应用请填入 @all" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:167 +msgid "agentid" +msgstr "应用id(agentid)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:172 +msgid "Secret" +msgstr "应用密钥(Secret)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:177 +msgid "Thumbnail Image File Path" +msgstr "图片缩略图文件路径" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:181 +msgid "Supports JPG and PNG formats within 2MB
Optimal size: 900383 or 2.35:1" +msgstr "只支持 2MB 以内 JPG、PNG 格式
最佳尺寸:900383 或 2.35:1" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:192 +msgid "topicIds(Mass sending)" +msgstr "topicIds(群发)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:203 +msgid "Get Bot" +msgstr "获取机器人" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:203 +msgid "
Send a message to the created bot to initiate a conversation." +msgstr "
与创建的机器人发一条消息,开启对话" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:208 +msgid "Get chat_id" +msgstr "获取 chat_id" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:208 +msgid "
If you want to send to a group/channel, please create a non-Chinese group/channel (for easier chatid lookup, you can rename it later).
Add the bot to the group, send a message, and use https://api.telegram.org/bot token /getUpdates to obtain the chatid." +msgstr "
如需通过群组/频道推送,请创建一个非中文的群组或频道(便于查找 chatid,后续再更名)
将机器人添加至群组,发送信息后通过 https://api.telegram.org/bot token /getUpdates 获取 chatid" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:226 +msgid "Please refer to the comments and other interface files for modifications. Limited resources, no longer supporting more interfaces, please debug on your own.
You can use a similar website to check the JSON file format: https://www.google.com/search?q=JSON+Parser+Online
Please use the 「Save」 button in the text box." +msgstr "请参照注释和其他接口文件修改,精力有限,不再支持更多接口,自行调试
请参考类似网站检查 JSON 文件格式:https://www.baidu.com/s?wd=json在线解析
文本框请使用「保存」按钮" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:230 +msgid "Send Test" +msgstr "发送测试" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:230 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:67 +msgid "You may need to save the configuration before sending." +msgstr "你可能需要先保存配置再进行发送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:236 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:578 +msgid "Message sent successfully. If you don't receive the message, please check the logs for manual processing." +msgstr "发送成功,如果收不到信息,请查看日志手动处理" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:238 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:243 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:579 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:584 +msgid "Sending failed" +msgstr "发送失败" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:242 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:277 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:350 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:401 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:583 +msgid "Unknown error: %s." +msgstr "未知错误:%s" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:249 +msgid "The device name will be displayed in the push message title to identify the source device of the message." +msgstr "在推送信息标题中会标识本设备名称,用于区分推送信息的来源设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:251 +msgid "Check Interval (s)" +msgstr "检测时间间隔(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:255 +msgid "Shorter intervals provide quicker response but consume more system resources." +msgstr "越短的时间间隔响应越及时,但会占用更多的系统资源" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:257 +msgid "MAC Device Database" +msgstr "MAC 设备数据库" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:259 +msgid "Do not use MAC device database" +msgstr "不使用 MAC 设备数据库" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:260 +msgid "Simplified Version" +msgstr "简化版" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:261 +msgid "Includes common device manufacturers, occupies approximately 200Kb of space" +msgstr "仅包含常见设备厂商,约占用 200Kb 空间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:262 +msgid "Full Version" +msgstr "完整版" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:263 +msgid "Download the complete database, processed size is approximately 1.3Mb" +msgstr "下载完整数据,处理后约占用 1.3Mb 空间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:264 +msgid "Network Query" +msgstr "网络查询" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:265 +msgid "Enables network query, slower response. Only use if space is limited." +msgstr "网络查询,查询速度较慢且需要梯子,除非空间紧缺,否则请勿使用" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:267 +msgid "Update MAC Device Database" +msgstr "更新 MAC 设备数据库" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:273 +msgid "Database is up to date, skipping update" +msgstr "已是最新,跳过更新" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:278 +msgid "Browser timeout or unknown error. If the log shows that the update process has been established, please ignore this error: %s." +msgstr "浏览器超时强制退出或未知错误,若日志显示已建立更新进程,请忽略此错误:%s" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:285 +msgid "Reset Traffic Data Every Day at Midnight" +msgstr "每天零点重置流量数据" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:287 +msgid "Enable Logging" +msgstr "开启日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:289 +msgid "Device Alias" +msgstr "设备别名" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:303 +msgid "Please enter the device MAC and device alias separated by a space, such as:
XX:XX:XX:XX:XX:XX My Phone
192.168.1.2 My PC
Please use the 「Save」 button in the text box." +msgstr "请输入设备 MAC 和设备别名,用空格隔开,例如:
XX:XX:XX:XX:XX:XX 我的手机
192.168.1.2 我的电脑
文本框请使用「保存」按钮" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:306 +msgid "IPv4 Dynamic Notification" +msgstr "IPv4 变动通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:309 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:360 +msgid "Obtain through interface" +msgstr "通过网络接口获取" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:311 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:362 +msgid "Obtain through URL" +msgstr "通过 URL 获取" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:312 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:363 +msgid "May fail due to server stability and frequent connections.
If the interface can obtain the IP address properly, it is not recommended to use this method." +msgstr "会因服务器稳定性、连接频繁等原因导致获取失败。
如接口可以正常获取 IP,不推荐使用此方法。" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:337 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:388 +msgid "Access a random address from the list above,URLs in the list are specific to Chinese websites. If you need to use this feature, please replace the URLs with the ones available to you.
Please use the 「Save」 button in the text box." +msgstr "从以上列表中随机访问地址
文本框请使用「保存」按钮" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:315 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:366 +msgid "Typically, it should be WAN or br-lan interface. For multi-wan environments, please choose accordingly." +msgstr "一般应为 WAN 或 br-lan 接口。多拨环境请自行选择。" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:321 +msgid "IPv4 API List" +msgstr "IPv4 API 列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:339 +msgid "Update IPv4 list" +msgstr "更新 IPv4 list" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:345 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:396 +msgid "Update successful" +msgstr "更新成功" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:347 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:351 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:398 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:402 +msgid "Update failed" +msgstr "更新失败" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:357 +msgid "IPv6 Dynamic Notification" +msgstr "IPv6 变动通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:372 +msgid "IPv6 API List" +msgstr "IPv6 API 列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:390 +msgid "Update IPv6 list" +msgstr "更新 IPv6 list" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:408 +msgid "Automatically update API list" +msgstr "API 列表自动更新" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:409 +msgid "When multiple IP retrieval attempts fail, try to automatically update the list file from GitHub" +msgstr "当多次获取 IP 失败时,尝试自动从 GitHub 上更新列表文件
因为懒得做外链,所以请确保你可以链接 raw.githubusercontent.com" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:413 +msgid "Device Online/Offline Notification" +msgstr "设备上下线通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:414 +msgid "Online Notification" +msgstr "上线通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:415 +msgid "Offline Notification" +msgstr "下线通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:418 +msgid "CPU Alert" +msgstr "CPU 报警" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:419 +msgid "Load Alert" +msgstr "负载报警" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:420 +msgid "Temperature Alert" +msgstr "温度报警" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:422 +msgid "Device alert will be triggered only if it exceeds the set value continuously for five minutes, and there won't be a second reminder within an hour." +msgstr "设备报警只会在连续五分钟超过设定值时才会推送,一个小时内不会再提醒第二次。" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:424 +msgid "Load alert threshold" +msgstr "负载报警阈值" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:433 +msgid "Please enter a numeric value only" +msgstr "请输入纯数字" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:436 +msgid "Temperature alert threshold" +msgstr "温度报警阈值" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:441 +msgid "Please confirm that the device can retrieve temperature. If you need to modify the command, please go to advanced settings." +msgstr "请确认设备可以获取温度。如果需修改命令,请移步高级设置。" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:443 +msgid "Login Notification" +msgstr "登录提醒" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:444 +msgid "Web Login" +msgstr "Web 登录" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:445 +msgid "SSH Login" +msgstr "SSH 登录" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:446 +msgid "Frequent Web Login Errors" +msgstr "Web 频繁错误登录" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:447 +msgid "Frequent SSH Login Errors" +msgstr "SSH 频繁错误登录" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:450 +msgid "Login failure count" +msgstr "登录失败次数" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:456 +msgid "Send notification after exceeding the count, and optionally auto-ban IP" +msgstr "超过次数后推送提醒,并可选自动封禁 IP" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:458 +msgid "Device abnormal traffic alert" +msgstr "设备异常流量警报" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:459 +msgid "Please ensure that you can retrieve device traffic information correctly, otherwise this feature will not work properly" +msgstr "请确保可以正确获取设备流量情况,否则此功能无法正常工作" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:462 +msgid "Per-minute traffic limit" +msgstr "每分钟流量限制" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:466 +msgid "Abnormal traffic alert (byte), you can append K or M" +msgstr "设备异常流量警报(byte),你可以追加 K 或者 M" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:468 +msgid "Abnormal traffic do not disturb" +msgstr "异常流量免打扰" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:472 +msgid "Abnormal traffic monitoring list" +msgstr "异常流量关注列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:473 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:633 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:639 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:659 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:664 +msgid "Please select device MAC" +msgstr "请选择设备 MAC" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:479 +msgid "Auto-ban unauthorized login devices" +msgstr "自动封禁非法登录设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:484 +msgid "Blacklisting time (s)" +msgstr "拉黑时间(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:489 +msgid "\"0\" in ipset means permanent blacklist, use with caution. If misconfigured, change the device IP and clear rules in LUCI." +msgstr "ipset 中, \"0\" 为永久拉黑,慎用。如不幸误操作,请更改设备 IP 进入 LUCI 界面清空规则" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:491 +msgid "Port knocking" +msgstr "端口敲门" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:497 +msgid "Port" +msgstr "端口" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:499 +msgid "Open port after successful login
example:\"22\"、\"21:25\"、\"21:25,135:139\"" +msgstr "登录成功后开放端口
例:\"22\"、\"21:25\"、\"21:25,135:139\"" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:494 +msgid "If you have disabled LAN port inbound and forwarding in Firewall - Zone Settings, it won't work." +msgstr "如在 防火墙 - 区域设置 中禁用了 LAN 口入站和转发,该功能将不起作用" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:505 +msgid "Example: Forward port 13389 of this device (IPv4:10.0.0.1 / IPv6:fe80::10:0:0:2) to port 3389 of (IPv4:10.0.0.2 / IPv6:fe80::10:0:0:8)
\"10.0.0.1,13389,10.0.0.2,3389\"
\"fe80::10:0:0:1,13389,fe80::10:0:0:2,3389\"" +msgstr "例:将本机 (IPv4:10.0.0.1 / IPv6:fe80::10:0:0:2) 的 13389 端口转发到 (IPv4:10.0.0.2 / IPv6:fe80::10:0:0:8) 的 3389 端口:
\"10.0.0.1,13389,10.0.0.2,3389\"
\"fe80::10:0:0:2,13389,fe80::10:0:0:8,3389\"" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:508 +msgid "Release time (s)" +msgstr "放行时间(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:494 +msgid "\"0\" in ipset means permanent release, use with caution" +msgstr "ipset 中, \"0\" 为永久放行,慎用" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:514 +msgid "IP blacklist" +msgstr "IP 黑名单列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:529 +msgid "You can add or delete here, the numbers after represent the remaining time. When adding, only the IP needs to be entered.
When clearing, please leave a blank line, otherwise it cannot be saved ╮(╯_╰)╭
Please use the 「Save」 button in the text box." +msgstr "可在此处添加或删除,后面的数字为剩余时间,添加时只需要输入 IP
清空时请保留一个回车,否则无法保存 ╮(╯_╰)╭
文本框请使用「保存」按钮" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:535 +msgid "Scheduled sending" +msgstr "定时发送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:536 +msgid "Send at the same time every day" +msgstr "每天固定时间发送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:537 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:547 +msgid "Interval sending" +msgstr "间隔发送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:538 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:555 +msgid "Starting from 00:00, send every * hours" +msgstr "从 00:00 开始,每 * 小时发送一次" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:540 +msgid "Sending time" +msgstr "发送时间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:542 +msgid "Every day" +msgstr "每天" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:542 +msgid "clock" +msgstr "点" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:557 +msgid "Push title" +msgstr "推送标题" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:560 +msgid "OpenWrt Router Status:" +msgstr "OpenWrt 路由状态:" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:561 +msgid "Using special characters may cause sending failure" +msgstr "使用特殊符号可能会造成发送失败" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:563 +msgid "Push content" +msgstr "推送内容" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:564 +msgid "System running status" +msgstr "系统运行情况" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:565 +msgid "Device temperature" +msgstr "设备温度" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:566 +msgid "WAN info" +msgstr "WAN 信息" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:567 +msgid "Client list" +msgstr "客户端列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:572 +msgid "Manual sending" +msgstr "手动发送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:572 +msgid "You may need to save the configuration before sending.
Due to browser timeout limitations, if the program is not running, it may exit due to timeout during device list initialization" +msgstr "你可能需要先保存配置再进行发送
由于浏览器超时限制,若程序未在运行,可能会因初始化设备列表超时退出" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:591 +msgid "Simplified mode" +msgstr "精简模式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:592 +msgid "Simplify the current device list" +msgstr "精简当前设备列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:593 +msgid "Simplify the current time" +msgstr "精简当前时间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:594 +msgid "Push only the title" +msgstr "只推送标题" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:596 +msgid "Do Not Disturb time setting" +msgstr "免打扰时段设置" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:599 +msgid "Mode 1: Script Suspension" +msgstr "模式一:脚本挂起" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:600 +msgid "Suspend all script actions, including unattended tasks, until the end of the time period" +msgstr "暂停脚本任何动作,包括无人值守任务,直到时段结束" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:601 +msgid "Mode 2: Silent Mode" +msgstr "模式二:静默模式" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:602 +msgid "Stop sending notifications, but log normally" +msgstr "停止信息推送,但正常记录日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:604 +msgid "Do Not Disturb start time" +msgstr "免打扰开始时间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:613 +msgid "Do Not Disturb end time" +msgstr "免打扰结束时间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:622 +msgid "MAC Filtering Mode 1" +msgstr "MAC 过滤模式一" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:625 +msgid "Ignore devices in the list" +msgstr "忽略列表内设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:626 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:628 +msgid "Ignored devices will not receive notifications or be logged" +msgstr "被忽略设备不做推送和日志记录" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:627 +msgid "Notify only devices in the list" +msgstr "仅通知列表内设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:629 +msgid "Notify only devices using this interface" +msgstr "仅通知此接口设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:630 +msgid "Multiple selections are not supported at the moment" +msgstr "暂不支持多选" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:632 +msgid "Ignored device list" +msgstr "忽略设备列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:637 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:148 +msgid "Followed device list" +msgstr "关注设备列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:641 +msgid "AA:AA:AA:AA:AA:AA\\|BB:BB:BB:BB:BB:BB Multiple MAC addresses can be treated as the same user.
Notifications will not be sent once any device is online, and notifications will only be sent when all devices are offline to avoid frequent notifications in dual Wi-Fi scenarios." +msgstr "AA:AA:AA:AA:AA:AA\\|BB:BB:BB:BB:BB:BB 可以将多个 MAC 视为同一用户
任一设备在线后不再推送,设备全部离线时才会推送,避免双 wifi 频繁推送" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:644 +msgid "Notify only devices using this interface" +msgstr "仅通知此接口设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:649 +msgid "MAC Filtering Mode 2" +msgstr "MAC 过滤模式二" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:652 +msgid "Do Not Disturb when devices are online" +msgstr "设备在线时免打扰" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:653 +msgid "No notifications will be sent when any device in the list is online" +msgstr "当列表内任意设备在线时,不推送信息" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:654 +msgid "Do Not Disturb when devices are offline" +msgstr "设备离线后免打扰" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:655 +msgid "No notifications will be sent when any device in the list is offline" +msgstr "当列表内任意设备离线后,不推送信息" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:657 +msgid "Do Not Disturb device online list" +msgstr "设备在线免打扰列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:662 +msgid "Do Not Disturb device offline list" +msgstr "设备离线免打扰列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:667 +msgid "Do Not Disturb for Login Reminders" +msgstr "登录提醒免打扰" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:670 +msgid "Only record in the log" +msgstr "仅记录到日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:671 +msgid "Ignore all login reminders and only record in the log" +msgstr "忽略所有登录提醒,仅记录到日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:672 +msgid "Send notification only on the first login" +msgstr "仅在首次登录时推送通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:673 +msgid "Send notification only once within the specified time interval" +msgstr "在设定时间间隔内,仅首次发送推送通知" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:675 +msgid "Login reminder do not disturb time (s)" +msgstr "登录提醒免打扰时间(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:679 +msgid "Send notification after the first login and do not repeat within the specified time
Take a shortcut and read the login time from the log" +msgstr "首次登录后推送通知,在设定时间内不再重复提醒
偷懒一下,登录时间从日志中读取,请确保开启日志功能" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:682 +msgid "Login reminder whitelist" +msgstr "登录提醒白名单" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:689 +msgid "Login reminder log anti-flooding" +msgstr "登录提醒日志免刷屏" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:690 +msgid "Users in the whitelist or during the undisturbed time period after their first login IP will be exempt from log recording, preventing log flooding." +msgstr "白名单列表内的用户,或者登录 IP 处于首次登录后的免打扰时段,不做日志记录,避免日志刷屏" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/config.js:688 +msgid "Add the IP addresses in the list to the whitelist for the blocking function (if available), and ignore automatic blocking and login event notifications. Only record in the log. Mask notation is currently not supported." +msgstr "列表内 IP 加入封禁功能白名单(如果可用),并忽略自动封禁和登录事件推送,仅在日志中记录,暂不支持掩码位表示" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:17 +msgid "If you are not familiar with the meanings of these options, please do not modify them.

" +msgstr "如果你不了解这些选项的含义,请不要修改这些选项

" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:19 +msgid "Advanced Settings" +msgstr "高级设置" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:23 +msgid "Device online detection timeout (s)" +msgstr "设备上线检测超时(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:28 +msgid "Device offline detection timeout (s)" +msgstr "设备离线检测超时(秒)" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:33 +msgid "Offline detection count" +msgstr "离线检测次数" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:37 +msgid "If the device has good signal strength and no Wi-Fi sleep issues, you can reduce the above values.
Due to the mysterious nature of Wi-Fi sleep during the night, if you encounter frequent disconnections, please adjust the parameters accordingly.
..╮(╯_╰)╭.." +msgstr "若设备信号强度良好,无息屏 WiFi 休眠问题,可以减少以上数值
因夜间 WiFi 息屏休眠较为玄学,遇到设备频繁推送断开,烦请自行调整参数
..╮(╯_╰)╭.." + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:42 +msgid "Offline timeout applies only to the devices that receive push notifications" +msgstr "离线超时只针对推送设备" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:45 +msgid "When this option is selected, the offline timeout and offline detection count apply only to the devices that require push notifications. Other devices will use default values, which can significantly reduce the time required for detection. However, it may result in inaccurate online time displayed in the online devices list. It is recommended to enable this option only when there are many devices and frequent offline occurrences are observed for specific devices of interest." +msgstr "选中此项后,离线超时时间和离线检测次数只针对需要推送的设备,其余设备使用默认值,可有效减少检测所需的时间,但会造成在线设备列表中的在线时间不准确,仅建议设备较多且关注的设备离线频繁时使用" + + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:47 +msgid "Disable active detection" +msgstr "关闭主动探测" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:50 +msgid "Disable active detection of client online status. Enabling this feature will no longer prompt device online/offline events.
Suitable for users who are not sensitive to online devices but need other features." +msgstr "关闭客户端在线状态的主动探测,因误报较为严重,启用此功能后设备上下线将不再提示
适用于对在线设备不敏感,但需要其他功能的用户" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:52 +msgid "Maximum concurrent processes" +msgstr "最大并发进程数" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:56 +msgid "Do not change the setting value for low-performance devices, or reduce the parameters as appropriate." +msgstr "低性能设备请勿更改设置值,或酌情减少参数" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:58 +msgid "Custom temperature reading command" +msgstr "自定义温度读取命令" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:60 +msgid "Default" +msgstr "默认" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:61 +msgid "Proxmox Virtual Environment" +msgstr "PVE 虚拟机" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:62 +msgid "If you need to use special symbols such as quotes, $, !, etc. in custom commands, you need to escape them yourself.
You can use the command eval echo $(uci get wechatpush.wechatpush.soc_code) to view command output and error information.
The execution result should be a pure number (including decimals) for temperature comparison.
Here is an example that does not require escaping:
cat /sys/class/thermal/thermal_zone0/temp|sort -nr|head -n1|cut -c-2" +msgstr "自定义命令如需使用特殊符号,如引号、$、!等,则需要自行转义
可以使用 eval echo $(uci get wechatpush.wechatpush.soc_code) 命令查看命令输出及错误信息
执行结果需为纯数字(可带小数),用于温度对比
一个无需转义的例子:
cat /sys/class/thermal/thermal_zone0/temp|sort -nr|head -n1|cut -c-2" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:64 +msgid "Host machine address" +msgstr "宿主机地址" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:70 +msgid "Host machine SSH port" +msgstr "宿主机 SSH 端口" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:73 +msgid "The default SSH port is 22. If you have a custom port, please fill in the custom SSH port.
Please make sure you have set up key-based login, otherwise it may cause script errors.
Install the sensors command on PVE by searching on the internet.
Example for key-based login (modify the address and port number accordingly):
opkg update # Update package list
opkg install openssh-client openssh-keygen # Install openssh client
echo -e \"\\n\" | ssh-keygen -t rsa # Generate key file (no passphrase)
pve_host=`uci get wechatpush.config.server_host` || pve_host=\"10.0.0.3\" # Read the PVE host address from the configuration file, If not saved, please fill in by yourself.
pve_port=`uci get wechatpush.config.server_port` || pve_host=\"22\" # Read the PVE host SSH port number from the configuration file, If not saved, please fill in by yourself.
ssh -o StrictHostKeyChecking=yes root@${pve_host} -p ${pve_port} \"tee -a ~/.ssh/OpenWrt_id_rsa.pub\" < ~/.ssh/id_rsa.pub # Transfer public key to PVE
ssh root@${pve_host} -p ${pve_port} \"cat ~/.ssh/OpenWrt_id_rsa.pub >> ~/.ssh/authorized_keys\" # Write public key to PVE
ssh -i /root/.ssh/id_rsa root@${pve_host} -p ${pve_port} sensors # To avoid script errors during the initial connection, please use a private key to connect to PVE and test the temperature command for its proper functioning.
For users who frequently flash firmware, please add /root/.ssh/ to the backup list to avoid duplicate operations." +msgstr "SSH 端口默认为 22,如有自定义,请填写自定义 SSH 端口
请确认已经设置好密钥登陆,否则会引起脚本无法运行等错误!
PVE 安装 sensors 命令自行百度
密钥登陆例(自行修改地址与端口号):
opkg update #更新列表
opkg install openssh-client openssh-keygen #安装openssh客户端
echo -e \"\\n\" | ssh-keygen -t rsa # 生成密钥文件(空密码)
pve_host=`uci get wechatpush.config.server_host` || pve_host=\"10.0.0.3\" # 读取配置文件中的 pve 主机地址,如果未保存设置,请自行填写
pve_port=`uci get wechatpush.config.server_port` || pve_host=\"22\" # 读取配置文件中的 pve 主机 ssh 端口号,如果未保存设置,请自行填写
ssh -o StrictHostKeyChecking=yes root@${pve_host} -p ${pve_port} \"tee -a ~/.ssh/OpenWrt_id_rsa.pub\" < ~/.ssh/id_rsa.pub # 传送公钥到 PVE
ssh root@${pve_host} -p ${pve_port} \"cat ~/.ssh/OpenWrt_id_rsa.pub >> ~/.ssh/authorized_keys\" # 写入公钥到 PVE
ssh -i /root/.ssh/id_rsa root@${pve_host} -p ${pve_port} sensors # 因首次连接时需要确认,为避免脚本错误,请使用私钥连接 PVE 并测试温度命令是否正常运行。
刷机党自行将 /root/.ssh/ 加入备份列表,避免重复操作" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:76 +msgid "Test temperature command" +msgstr "测试温度命令" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:82 +msgid "Returned temperature value is empty" +msgstr "返回的温度值为空" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:87 +msgid "Fetch failed:" +msgstr "获取失败:" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:91 +msgid "Assist in obtaining device information" +msgstr "辅助获取设备列表" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:92 +msgid "Retrieve hostname list from modem" +msgstr "从光猫获取主机名等信息" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:93 +msgid "Scan local IP" +msgstr "扫描局域网 IP" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:95 +msgid "When OpenWrt is used as a bypass gateway and cannot obtain device hostnames or a complete list of local network devices.
the \"Retrieve hostname list from modem\" option has only been tested with HG5143F/HN8145V China Telecom gateways and may not be universally applicable.
The \"Scan local IP\" option may not retrieve hostnames, so please use device name annotations in conjunction with it." +msgstr "适用于 OpenWrt 作为旁路网关,无法获取设备主机名及完整的局域网设备列表时
\"从光猫获取主机名列表\"选项仅测试通过 HG5143F/HN8145V 天翼网关,不保证通用性
\"定期扫描局域网 IP\"选项可能无法获取主机名,请配合设备名备注使用" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:97 +msgid "Optical modem login URL" +msgstr "光猫登录地址 URL" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:102 +msgid "Device list JSON URL" +msgstr "设备列表 JSON URL" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:105 +msgid "Use F12 console to capture
ip, devName, model are mandatory fields. Example JSON file information:
{\"pc1\":{\"devName\":\"RouterOS\",\"model\":\"\",\"type\":\"pc\",\"ip\":\"192.168.1.7\"}}" +msgstr "使用 F12 控制台自行抓取
ip、devName、model 为必须项,JSON 文件信息范例:
{\"pc1\":{\"devName\":\"RouterOS\",\"model\":\"\",\"type\":\"pc\",\"ip\":\"192.168.1.7\"}}" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:108 +msgid "Optical modem logout URL" +msgstr "光猫注销登录 URL" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:111 +msgid "Not a mandatory field, but it may affect other users logging into the web management page, e.g., HG5143F" +msgstr "非必须项,但可能会影响其他用户登录 Web 管理页面,如 HG5143F" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:114 +msgid "Login page account input box ID" +msgstr "登录页面帐号输入框 ID" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:119 +msgid "Login page password input box ID" +msgstr "登录页面密码输入框 ID" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:122 +msgid "Right-click in the browser and select 'Inspect Element'" +msgstr "浏览器右键-检查元素" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:125 +msgid "Optical modem login account" +msgstr "光猫登录帐号" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:130 +msgid "Optical modem login password" +msgstr "光猫登录密码" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:132 +msgid "Use a regular account, no need for super password" +msgstr "使用普通账号即可,不需要超密" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:135 +msgid "IP range to be scanned" +msgstr "需要扫描的 IP 段" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:140 +msgid "Interval for capturing info" +msgstr "抓取信息时间间隔" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:144 +msgid "Generally, frequent capturing is not necessary. Adjust it as needed." +msgstr "一般不需要频繁抓取,酌情设置" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:148 +msgid "Unattended tasks" +msgstr "无人值守任务" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:151 +msgid "Please make sure the script can run properly, otherwise it may cause frequent restarts and other errors!" +msgstr "请确认脚本可以正常运行,否则可能造成频繁重启等错误!" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:153 +msgid "Restart zerotier after IP change" +msgstr "IP 变化后重启 zerotier" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:154 +msgid "An old issue with zerotier
Cannot reconnect after disconnection, emmm, I don't know if it has been fixed now." +msgstr "zerotier 的陈年老问题
断网后不能重新打洞,emmm,我也不知道修了没有" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:157 +msgid "Redial only during Do-Not-Disturb period" +msgstr "仅在免打扰时段重拨" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:160 +msgid "Avoid redialing network during the day to prevent waiting for DDNS domain resolution. This feature does not affect disconnection detection.
Due to the issue of certain apps consuming excessive data at night, this feature may be unstable." +msgstr "避免在白天重拨网络造成 DDNS 域名等待解析,此功能不影响断网检测
因夜间某些 APP 偷跑流量问题,该功能可能不稳定" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:165 +msgid "Will only be executed when none of the devices in the list are online.
After an hour of Do-Not-Disturb period, if the devices in the focus list have low traffic (around 100kb/m) for five minutes, they will be considered offline." +msgstr "只会在列表中设备都不在线时才会执行
免打扰时段一小时后,关注设备五分钟低流量(约100kb/m)将视为离线" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:169 +msgid "When the network is disconnected" +msgstr "网络断开时" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:171 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:179 +msgid "No operation" +msgstr "无操作" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:172 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:180 +msgid "Restart the router" +msgstr "重启路由器" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:173 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:181 +msgid "Redialing network" +msgstr "重新拨号" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:174 +msgid "The restart operation will occur ten minutes after the network disconnection and will be attempted a maximum of two times. If the option to log in to the optical modem is available, this operation will attempt to restart the optical modem.
【!!This feature cannot guarantee compatibility!!】" +msgstr "重启操作将在网络断开十分钟后进行,且最多尝试两次。如果光猫登录选项可用,此操作将会尝试重启光猫。
【!!此功能无法保证兼容性!!】" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:177 +msgid "Scheduled reboot" +msgstr "定时重启" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:184 +msgid "System uptime greater than" +msgstr "系统运行时间大于" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:188 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:195 +msgid "Unit: hours" +msgstr "单位为小时" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/advanced.js:191 +msgid "Network uptime greater than" +msgstr "网络在线时间大于" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/client.js:14 +msgid "Online time" +msgstr "在线时间" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:75 +msgid "Clear Logs..." +msgstr "清除日志..." + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:75 +msgid "Logs cleared successfully!" +msgstr "日志清除成功!" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:81 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:91 +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:95 +msgid "Clear Logs" +msgstr "清除日志" + +#: applications/luci-app-wechatpush/htdocs/luci-static/resources/view/wechatpush/log.js:124 +msgid "Refresh every 5 seconds." +msgstr "每 5 秒刷新" diff --git a/luci-app-wechatpush/root/etc/config/wechatpush b/luci-app-wechatpush/root/etc/config/wechatpush new file mode 100644 index 00000000..95f49c3b --- /dev/null +++ b/luci-app-wechatpush/root/etc/config/wechatpush @@ -0,0 +1,9 @@ + +config wechatpush "config" + option enable '0' + option sleeptime '60' + option debuglevel '1' + option up_timeout '2' + option down_timeout '10' + option timeout_retry_count '2' + option thread_num '3' diff --git a/luci-app-wechatpush/root/etc/init.d/wechatpush b/luci-app-wechatpush/root/etc/init.d/wechatpush new file mode 100755 index 00000000..5839d70e --- /dev/null +++ b/luci-app-wechatpush/root/etc/init.d/wechatpush @@ -0,0 +1,36 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=90 +USE_PROCD=1 + +dir="/tmp/wechatpush/" + +start_service() { + procd_open_instance + enable_value=$(uci get wechatpush.config.enable 2>/dev/null || echo "0") + [ "$enable_value" -ne "0" ] && procd_set_param command /usr/share/wechatpush/wechatpush && echo "wechatpush is starting now ..." + procd_close_instance +} + +reload_service() { + stop + sleep 1 + start +} + +stop_service() { + [ -f ${dir}child_pid ] && parent_pid=$(cat ${dir}child_pid) + [ -n "$parent_pid" ] && { + child_pids=$(pgrep -P $parent_pid) + echo "Terminating child processes of wechatpush..." + for child_pid in $child_pids; do + kill $child_pid + done + } + echo "Terminating wechatpush process..." +} + +service_triggers() { + procd_add_reload_trigger wechatpush +} diff --git a/luci-app-wechatpush/root/etc/uci-defaults/luci-wechatpush b/luci-app-wechatpush/root/etc/uci-defaults/luci-wechatpush new file mode 100755 index 00000000..8e29cc58 --- /dev/null +++ b/luci-app-wechatpush/root/etc/uci-defaults/luci-wechatpush @@ -0,0 +1,179 @@ +#!/bin/sh + +function get_config_wechatpush(){ + while [[ "$*" != "" ]]; do + eval ${1}='`uci get wechatpush.config.$1`' 2>/dev/null + uci del wechatpush.config.$1 2>/dev/null + uci commit wechatpush + shift + done +} + +function get_config_serverchan(){ + while [[ "$*" != "" ]]; do + eval ${1}='`uci get serverchan.serverchan.$1`' 2>/dev/null + uci del serverchan.serverchan.$1 2>/dev/null + uci commit serverchan 2>/dev/null + shift + done +} + +function serverchan_to_wechatpush_list(){ + [ $2 ] && config_name=$2 || config_name=$1 + config_list=`uci get serverchan.serverchan.$1` 2>/dev/null + [ -z "$config_list" ] && return + uci del serverchan.serverchan.$1 2>/dev/null + uci commit serverchan 2>/dev/null + for config in $config_list ;do + uci add_list wechatpush.config.$config_name="$config" + done + uci commit wechatpush +} + +function serverchan_to_wechatpush_change(){ + config=`uci get serverchan.serverchan.$1` 2>/dev/null + [ -z "$config" ] && return + uci del serverchan.serverchan.$1 2>/dev/null + uci commit serverchan 2>/dev/null + uci set wechatpush.config.$2=$config + uci commit wechatpush +} + +function serverchan_to_wechatpush_get(){ + while [[ "$*" != "" ]]; do + config=`uci get serverchan.serverchan.$1` 2>/dev/null + [ -z "$config" ] && shift && continue + uci del serverchan.serverchan.$1 2>/dev/null + uci commit serverchan 2>/dev/null + uci set wechatpush.config.${1}=${config} + uci commit wechatpush + shift + done +} + +if [ ! -f "/etc/config/wechatpush" ]; then +cat>/etc/config/wechatpush</dev/null + sed -i 's#/root/usr/bin/serverchan/api/#/usr/share/wechatpush/api/#g' "$serverchan_config" 2>/dev/null +fi + +get_config_serverchan "serverchan_up" "serverchan_down" +[ -n "$serverchan_up" ] && [ "$serverchan_up" -eq "1" ] && uci add_list wechatpush.config.device_notification='online' +[ -n "$serverchan_down" ] && [ "$serverchan_down" -eq "1" ] && uci add_list wechatpush.config.device_notification='offline' + +get_config_serverchan "cpuload_enable" "temperature_enable" +[ -n "$cpuload_enable" ] && [ "$cpuload_enable" -eq "1" ] && uci add_list wechatpush.config.cpu_notification='load' +[ -n "$temperature_enable" ] && [ "$temperature_enable" -eq "1" ] && uci add_list wechatpush.config.cpu_notification='temp' + +get_config_serverchan "web_logged" "ssh_logged" "web_login_failed" "ssh_login_failed" +[ -n "$web_logged" ] && [ "$web_logged" -eq "1" ] && uci add_list wechatpush.config.login_notification='web_logged' +[ -n "$ssh_logged" ] && [ "$ssh_logged" -eq "1" ] && uci add_list wechatpush.config.login_notification='ssh_logged' +[ -n "$web_login_failed" ] && [ "$web_login_failed" -eq "1" ] && uci add_list wechatpush.config.login_notification='web_login_failed' +[ -n "$ssh_login_failed" ] && [ "$ssh_login_failed" -eq "1" ] && uci add_list wechatpush.config.login_notification='ssh_login_failed' + +get_config_serverchan "router_status" "router_temp" "router_wan" "client_list" +[ -n "$router_status" ] && [ "$router_status" -eq "1" ] && uci add_list wechatpush.config.send_notification='router_status' +[ -n "$router_temp" ] && [ "$router_temp" -eq "1" ] && uci add_list wechatpush.config.send_notification='router_temp' +[ -n "$router_wan" ] && [ "$router_wan" -eq "1" ] && uci add_list wechatpush.config.send_notification='wan_info' +[ -n "$client_list" ] && [ "$client_list" -eq "1" ] && uci add_list wechatpush.config.send_notification='client_list' + +get_config_serverchan "regular_time" "regular_time2" "regular_time3" +[ -n "$regular_time" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time" +[ -n "$regular_time2" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time2" +[ -n "$regular_time3" ] && uci add_list wechatpush.config.crontab_regular_time="$regular_time3" + +get_config_wechatpush "gateway_info_enable" "gateway_sleeptime" +[ -n "$gateway_info_enable" ] && [ "$" -eq "1" ] && uci add_list wechatpush.config.device_info_helper='gateway_info' +[ -n "$gateway_sleeptime" ] && uci set wechatpush.config.device_info_helper_sleeptime="$gateway_sleeptime" + +serverchan_to_wechatpush_list "device_notification" +serverchan_to_wechatpush_list "cpu_notification" +serverchan_to_wechatpush_list "login_notification" +serverchan_to_wechatpush_list "send_notification" +serverchan_to_wechatpush_list "ip_white_list" "login_ip_white_list" +serverchan_to_wechatpush_list "port_forward_list" "login_port_forward_list" +serverchan_to_wechatpush_list "client_usage_whitelist" +serverchan_to_wechatpush_list "serverchan_whitelist" "up_down_push_whitelist" +serverchan_to_wechatpush_list "serverchan_blacklist" "up_down_push_blacklist" +serverchan_to_wechatpush_list "MAC_online_list" "mac_online_list" +serverchan_to_wechatpush_list "MAC_offline_list" "mac_offline_list" +serverchan_to_wechatpush_list "err_device_aliases" "unattended_device_aliases" + +get_config_serverchan "device_aliases" +mkdir -p /usr/share/wechatpush/api/ +[ -n "$device_aliases" ] && echo "$device_aliases"|sed 's/ /\n/g'|sed 's/-/ /' > /usr/share/wechatpush/api/device_aliases.list +serverchan_device_aliases_path="/usr/share/serverchan/api/device_aliases.list" +wechatpush_device_aliases_path="/usr/share/wechatpush/api/device_aliases.list" +[ -f "$wechatpush_device_aliases_path" ] && [ `tail -n1 "${wechatpush_device_aliases_path}" | wc -l` -eq "0" ] && echo -e >> ${wechatpush_device_aliases_path} +[ -f "$serverchan_device_aliases_path" ] && grep -v '^#' "$serverchan_device_aliases_path" | grep -v '^[[:space:]]*$' | sort -u > "$wechatpush_device_aliases_path.temp" && mv "$wechatpush_device_aliases_path.temp" "$wechatpush_device_aliases_path" +[ -f "$serverchan_device_aliases_path" ] && rm -rf "$serverchan_device_aliases_path" +cp -r "/usr/share/serverchan/api/." "/usr/share/wechatpush/api/" 2>/dev/null +rm -rf "/usr/share/serverchan/api" 2>/dev/null + +serverchan_to_wechatpush_change "serverchan_enable" "enable" +serverchan_to_wechatpush_change "serverchan_ipv4" "get_ipv4_mode" +serverchan_to_wechatpush_change "serverchan_ipv6" "get_ipv6_mode" +serverchan_to_wechatpush_change "update_list" "auto_update_ip_list" +serverchan_to_wechatpush_change "cpuload" "cpu_load_threshold" +serverchan_to_wechatpush_change "temperature" "temperature_threshold" +serverchan_to_wechatpush_change "web_login_black" "login_web_black" +serverchan_to_wechatpush_change "ip_black_timeout" "login_ip_black_timeout" +serverchan_to_wechatpush_change "port_knocking" "port_knocking_enable" +serverchan_to_wechatpush_change "ip_white_timeout" "login_ip_white_timeout" +serverchan_to_wechatpush_change "ip_port_white" "login_port_white" +serverchan_to_wechatpush_change "regular_time" "crontab_regular_time" +serverchan_to_wechatpush_change "interval_time" "crontab_interval_time" +serverchan_to_wechatpush_change "serverchan_sheep" "do_not_disturb_mode" +serverchan_to_wechatpush_change "starttime" "do_not_disturb_starttime" +serverchan_to_wechatpush_change "endtime" "do_not_disturb_endtime" +serverchan_to_wechatpush_change "serverchan_interface" "up_down_push_interface" +serverchan_to_wechatpush_change "passive_option" "passive_mode" +serverchan_to_wechatpush_change "err_enable" "unattended_enable" +serverchan_to_wechatpush_change "err_sheep_enable" "unattended_only_on_disturb_time" +serverchan_to_wechatpush_change "network_err_event" "network_disconnect_event" +serverchan_to_wechatpush_change "system_time_event" "unattended_autoreboot_mode" +serverchan_to_wechatpush_change "autoreboot_time" "autoreboot_system_uptime" +serverchan_to_wechatpush_change "network_restart_time autoreboot_network_uptime" +serverchan_to_wechatpush_change "crontab" "crontab_mode" +serverchan_to_wechatpush_change "macmechanism" "mac_filtering_mode_1" +serverchan_to_wechatpush_change "macmechanism2" "mac_filtering_mode_2" + +serverchan_to_wechatpush_get \ + "enable" "lite_enable" "device_name" "sleeptime" "oui_data" "reset_regularly" "debuglevel" \ + "jsonpath" "sckey" "corpid" "userid" "agentid" "corpsecret" "mediapath" "wxpusher_apptoken" "wxpusher_uids" "wxpusher_topicIds" "pushplus_token" "tg_token" "chat_id" \ + "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" "auto_update_ip_list" \ + "device_notification" "cpu_notification" "cpu_load_threshold" "temperature_threshold" \ + "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ + "login_notification" "login_max_num" "login_web_black" "login_ip_black_timeout" "login_ip_white_list" "port_knocking_enable" "login_ip_white_timeout" "login_port_white" "login_port_forward_list" \ + "crontab_regular_time" "crontab_interval_time" \ + "do_not_disturb_mode" "do_not_disturb_starttime" "do_not_disturb_endtime" "up_down_push_whitelist" "up_down_push_blacklist" "up_down_push_interface" "mac_online_list" "mac_offline_list" "login_disturb" "login_notification_delay" \ + "up_timeout" "down_timeout" "timeout_retry_count" "passive_mode" "thread_num" "soc_code" "server_host" "server_port" \ + "unattended_enable" "zerotier_helper" "unattended_only_on_disturb_time" "unattended_device_aliases" "network_disconnect_event" "unattended_autoreboot_mode" "autoreboot_system_uptime" "autoreboot_network_uptime" \ + "gateway_info_enable" "gateway_host_url" "gateway_info_url" "gateway_logout_url" "gateway_username_id" "gateway_password_id" "gateway_username" "gateway_password" "gateway_sleeptime" + +uci commit wechatpush +rm -rf "$serverchan_config" 2>/dev/null +dir="/tmp/wechatpush/" && mkdir -p ${dir} +echo "" > ${dir}web_login +echo "" > ${dir}ssh_login +echo "" > ${dir}web_failed +echo "" > ${dir}ssh_failed + +nohup sh -c 'while [ -f /var/lock/opkg.lock ]; do sleep 1; done; opkg remove luci-app-serverchan; rm -rf /tmp/luci-*' >/dev/null 2>&1 & diff --git a/luci-app-wechatpush/root/usr/libexec/wechatpush-call b/luci-app-wechatpush/root/usr/libexec/wechatpush-call new file mode 100755 index 00000000..3b1e1b72 --- /dev/null +++ b/luci-app-wechatpush/root/usr/libexec/wechatpush-call @@ -0,0 +1,48 @@ +#!/bin/sh + +logfile="/tmp/wechatpush/wechatpush.log" +dir="/tmp/wechatpush/" && mkdir -p ${dir} && mkdir -p ${dir}/client +oui_base=${dir}oui_base.txt +oui_data=$(uci get wechatpush.config.oui_data 2>/dev/null) +oui_url="https://standards-oui.ieee.org/oui/oui.txt" + +function file_date(){ + file_dir="$1" + [ -f ${file_dir} ] && filerow=$(grep -c "" ${file_dir}) || filerow="0" + [ "$filerow" -ne "0" ] && datetime=$(date -r ${file_dir} +%s 2>/dev/null) || datetime=0 + expr $(date +%s) - $datetime +} + +function down_oui(){ + if [ ! -z "$oui_data" ] && [ "$oui_data" -ne "3" ]; then + echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】开始后台更新 MAC 信息文件" >> ${logfile} + wget --no-check-certificate -t 10 -T 15 -O ${dir}oui.txt "$oui_url" >/dev/null 2>&1 + [ $? -ne "0"] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 MAC 信息文件下载失败,返回码为 $? " && return + echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】设备 MAC 信息文件下载成功,处理中" >> ${logfile} + if [ "$oui_data" -eq "1" ]; then + cat ${dir}oui.txt | grep -i -E ".*(base 16).*(apple|aruba|asus|autelan|belkin|bhu|buffalo|cctf|cisco|comba|datang|dell|dlink|dowell|ericsson|fast|feixun|fiberhome|fujitsu|grentech|h3c|hisense|hiwifi|honghai|honghao|hp|htc|huawei|intel|jinli|jse|lenovo|lg|liteon|malata|meizu|mercury|meru|moto|netcore|netgear|nokia|omron|oneplus|oppo|philips|router_unkown|samsung|shanzhai|sony|start_net|sunyuanda|tcl|tenda|texas|tianyu|tp-link|ubq|undefine|VMware|utstarcom|volans|xerox|xiaomi|zdc|zhongxing|smartisan).*" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' > ${oui_base} + elif [ "$oui_data" -eq "2" ]; then + cat ${dir}oui.txt | grep -i "(base 16)" | sed -E 's/( Electronic| Technology| Intelligence| TECHNOLOGIES| Device| Systems| TELECOMMUNICATIONS| Instruments| Electronics| Corporation| Telecommunication| Communications| Electrical| Technology| Corporate| Intelligent| Interactive| MOBILE| Solutions| Mobility| Meraki| ELECTRO| VISUAL| Limited| International| Information| LLC|Co$|Co\.|Ltd\.$|Inc\.|B\.V\.$|AB$|,).*$/ /I; s/[[:space:]]*$//; s/ +$//' > ${oui_base} + fi + rm -f ${dir}oui.txt >/dev/null 2>&1 + echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】设备 MAC 信息文件处理完成" >> ${logfile} + fi +} + +if [ "$1" == "get_client" ]; then + # 生成在线设备列表,沿用 LUA 版本生成的临时文件 + devices=$(find /tmp/wechatpush/client -type f -print0 | xargs -0 cat | sed "s/'/\"/g" | tr '\n' ',') + echo "{\"devices\": [${devices%,}]}" +elif [ "$1" == "clear_log" ]; then + # 清空日志 + > /tmp/wechatpush/wechatpush.log +elif [ "$1" == "update_list" ]; then + # 更新 API 列表 + echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】开始更新 $2 API 列表" >> ${logfile} + wget --no-check-certificate -t 3 -T 15 -O "/tmp/wechatpush/${2}.list" "https://raw.githubusercontent.com/tty228/luci-app-wechatpush/master/root/usr/share/wechatpush/api/${2}.list" >/dev/null 2>&1 + [ "$?" -eq "0" ] && mv -f "/tmp/wechatpush/${2}.list" "/usr/share/wechatpush/api/${2}.list" echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】 $2 API 列表更新成功" >> ${logfile} +elif [ "$1" == "down_oui" ]; then + # 更新 MAC 信息列表 + [ `file_date "$oui_base"` -lt "86400" ] && return 2 + down_oui > /dev/null 2>&1 & +fi \ No newline at end of file diff --git a/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json b/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json new file mode 100644 index 00000000..aaf0514e --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/luci/menu.d/luci-app-wechatpush.json @@ -0,0 +1,49 @@ +{ + "admin/services/wechatpush": { + "title": "WeChat push", + "order": 30, + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-wechatpush" ], + "uci": { "wechatpush": true } + } + }, + + "admin/services/wechatpush/config": { + "title": "Basic Settings", + "order": 10, + "action": { + "type": "view", + "path": "wechatpush/config" + } + }, + + "admin/services/wechatpush/advanced": { + "title": "Advance Setting", + "order": 20, + "action": { + "type": "view", + "path": "wechatpush/advanced" + } + }, + + "admin/services/wechatpush/client": { + "title": "Online Devices", + "order": 30, + "action": { + "type": "view", + "path": "wechatpush/client" + } + }, + "admin/services/wechatpush/log": { + "title": "Log", + "order": 40, + "action": { + "type": "view", + "path": "wechatpush/log" + } + } + +} diff --git a/luci-app-wechatpush/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json b/luci-app-wechatpush/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json new file mode 100644 index 00000000..c2530187 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/rpcd/acl.d/luci-app-wechatpush.json @@ -0,0 +1,25 @@ +{ + "luci-app-wechatpush": { + "description": "Grant UCI access for luci-app-wechatpush", + "read": { + "file": { + "/etc/init.d/wechatpush": [ "exec" ], + "/usr/share/wechatpush/wechatpush": [ "exec" ], + "/tmp/wechatpush/*": [ "read" ], + "/usr/share/wechatpush/api/*": [ "read" ], + "/usr/libexec/wechatpush-call": [ "exec" ] + }, + "ubus": { + "service": [ "list" ] + }, + "uci": [ "wechatpush" ] + }, + "write": { + "file": { + "/usr/share/wechatpush/api/*": [ "write" ], + "/tmp/wechatpush/*": [ "write" ] + }, + "uci": [ "wechatpush" ] + } + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/device_aliases.list b/luci-app-wechatpush/root/usr/share/wechatpush/api/device_aliases.list new file mode 100644 index 00000000..e8b033fd --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/device_aliases.list @@ -0,0 +1,3 @@ +# Examples : +#XX:XX:XX:XX:XX:XX My Phone +#192.168.1.2 My PC diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/diy.json b/luci-app-wechatpush/root/usr/share/wechatpush/api/diy.json new file mode 100644 index 00000000..9b46c97a --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/diy.json @@ -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": "", + "str_title_end": "", + "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\"" + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/ip_attribution.list b/luci-app-wechatpush/root/usr/share/wechatpush/api/ip_attribution.list new file mode 100644 index 00000000..ba9ec1e5 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/ip_attribution.list @@ -0,0 +1,2 @@ +curl -s cip.cc/${ip} | sed -n 's/地址[[:space:]]*:[[:space:]]*\(.*\)/\1/p' +curl -s https://ip.rss.ink/v1/qqwry?ip=${ip} | jq -r '.data.area' diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/ip_blacklist b/luci-app-wechatpush/root/usr/share/wechatpush/api/ip_blacklist new file mode 100644 index 00000000..e69de29b diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv4.list b/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv4.list new file mode 100644 index 00000000..d3ba8884 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv4.list @@ -0,0 +1,7 @@ +cip.cc +ddns.oray.com/checkip +www.net.cn/static/customercare/yourip.asp +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' diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv6.list b/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv6.list new file mode 100644 index 00000000..a996a67a --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/ipv6.list @@ -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' diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/logo.jpg b/luci-app-wechatpush/root/usr/share/wechatpush/api/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a89fa5cc291f3834ef76eb1e33c814ddabca6e14 GIT binary patch literal 33685 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

HCLuD)v~ZL!KsVcG~Iw$offG#-N6MWfLp#h1h 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 ", + "type": + { + "touser": "\"${userid}\"", + "msgtype": "\"markdown\"", + "agentid": "\"${agentid}\"", + "markdown": { + "content": "\"${1}${str_linefeed}${nowtime}${2}\"" + } + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/qywx_mpnews.json b/luci-app-wechatpush/root/usr/share/wechatpush/api/qywx_mpnews.json new file mode 100644 index 00000000..d278cac0 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/qywx_mpnews.json @@ -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": "", + "str_title_end": "", + "str_linefeed": "
", + "str_splitline": "


", + "str_space": " ", + "str_tab": "", + "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 + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/serverchan.json b/luci-app-wechatpush/root/usr/share/wechatpush/api/serverchan.json new file mode 100644 index 00000000..116e3f20 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/serverchan.json @@ -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": + { + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/telegram.json b/luci-app-wechatpush/root/usr/share/wechatpush/api/telegram.json new file mode 100644 index 00000000..dfb80d58 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/telegram.json @@ -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": "", + "str_title_end": "", + "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\"" + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/api/wxpusher.json b/luci-app-wechatpush/root/usr/share/wechatpush/api/wxpusher.json new file mode 100644 index 00000000..c2ea7a07 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/api/wxpusher.json @@ -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 + } +} diff --git a/luci-app-wechatpush/root/usr/share/wechatpush/wechatpush b/luci-app-wechatpush/root/usr/share/wechatpush/wechatpush new file mode 100755 index 00000000..0f38d782 --- /dev/null +++ b/luci-app-wechatpush/root/usr/share/wechatpush/wechatpush @@ -0,0 +1,1759 @@ +#!/bin/bash + +# 读取设置文件 +function get_config(){ + while [[ "$*" != "" ]]; do + eval ${1}='`uci get wechatpush.config.$1`' 2>/dev/null + shift + done +} + +# 后台读取 json 中的变量 +read_json() { + local json_key="$1" + local json_path="$2" + local output_file="$output_dir/${json_key}" + jq -r ."$json_key" "$json_path" > "$output_file" & +} + +# 遍历输出目录,将文件内容保存到对应的变量 +wait_and_cat() { + [ $(ls -A "$output_dir" | wc -l) -eq 0 ] && return + wait + for file in "$output_dir"/*; do + local variable_name=$(basename "$file") + local variable_value=$(cat "$file") + eval "${variable_name}='${variable_value}'" + done + rm "$output_dir"/* +} + +# 初始化设置信息 +function read_config(){ + get_config \ + "enable" "lite_enable" "device_name" "sleeptime" "oui_data" "reset_regularly" "debuglevel" \ + "jsonpath" "sckey" "corpid" "userid" "agentid" "corpsecret" "mediapath" "wxpusher_apptoken" "wxpusher_uids" "wxpusher_topicIds" "pushplus_token" "tg_token" "chat_id" \ + "get_ipv4_mode" "ipv4_interface" "get_ipv6_mode" "ipv6_interface" "auto_update_ip_list" \ + "device_notification" "cpu_notification" "cpu_load_threshold" "temperature_threshold" \ + "client_usage" "client_usage_max" "client_usage_disturb" "client_usage_whitelist" \ + "login_notification" "login_max_num" "login_web_black" "login_ip_black_timeout" "login_ip_white_list" "port_knocking_enable" "login_ip_white_timeout" "login_port_white" "login_port_forward_list" \ + "crontab_regular_time" "crontab_interval_time" \ + "do_not_disturb_mode" "do_not_disturb_starttime" "do_not_disturb_endtime" "up_down_push_whitelist" "up_down_push_blacklist" "up_down_push_interface" "mac_online_list" "mac_offline_list" "login_disturb" "login_notification_delay" "login_log_enable" \ + "up_timeout" "down_timeout" "timeout_retry_count" "only_timeout_push" "passive_mode" "thread_num" "soc_code" "server_host" "server_port" \ + "unattended_enable" "zerotier_helper" "unattended_only_on_disturb_time" "unattended_device_aliases" "network_disconnect_event" "unattended_autoreboot_mode" "autoreboot_system_uptime" "autoreboot_network_uptime" \ + "device_info_helper" "gateway_host_url" "gateway_info_url" "gateway_logout_url" "gateway_username_id" "gateway_password_id" "gateway_username" "gateway_password" "scan_ip_range" "device_info_helper_sleeptime" + + ( echo "$device_notification"|grep -q "online" ) && notification_online="true" + ( echo "$device_notification"|grep -q "offline" ) && notification_offline="true" + ( echo "$cpu_notification"|grep -q "load" ) && notification_load="true" + ( echo "$cpu_notification"|grep -q "temp" ) && notification_temp="true" + ( echo "$login_notification"|grep -q "web_logged" ) && web_logged="true" + ( echo "$login_notification"|grep -q "ssh_logged" ) && ssh_logged="true" + ( echo "$login_notification"|grep -q "web_login_failed" ) && web_login_failed="true" + ( echo "$login_notification"|grep -q "ssh_login_failed" ) && ssh_login_failed="true" + ( echo "$device_info_helper"|grep -q "gateway_info" ) && gateway_info_enable="true" + for str_version in "wrtbwmon" "iputils-arping" "curl" "iw"; do + eval `echo ${str_version:0:2}"_version"`=`opkg list-installed|grep -w ^${str_version}|awk '{print $3}'` 2>/dev/null + done + ( opkg list-installed|grep -w -q ^firewall4 ) && nftables_version="true" + dir="/tmp/wechatpush/" && mkdir -p ${dir} && mkdir -p ${dir}/client + tempjsonpath="${dir}temp.json" + ip_blacklist_path="/usr/share/wechatpush/api/ip_blacklist" + oui_base="${dir}oui_base.txt" + debuglevel=`echo "$debuglevel"` && [ -z "$debuglevel" ] && logfile="/dev/null" || logfile="${dir}wechatpush.log" + login_port_forward_list=`echo "$login_port_forward_list"|sed 's/ /\n/g'` 2>/dev/null + up_down_push_blacklist=`echo "$up_down_push_blacklist"|sed 's/ /\n/g'` 2>/dev/null + up_down_push_whitelist=`echo "$up_down_push_whitelist"|sed 's/ /\n/g'` 2>/dev/null + device_aliases=`cat /usr/share/wechatpush/api/device_aliases.list` 2>/dev/null + unattended_device_aliases=`echo "$unattended_device_aliases"|sed 's/ /\n/g'` 2>/dev/null + client_usage_whitelist=`echo "$client_usage_whitelist"|sed 's/ /\n/g'` 2>/dev/null + login_ip_white_list=`echo "$login_ip_white_list"|sed 's/ /\n/g'` 2>/dev/null + mark_mac_list="${mac_online_list} ${mac_offline_list}" + mark_mac_list=`echo "$mark_mac_list"|sed 's/ /\n/g'|sed 's/-/ /'` 2>/dev/null + ipv4_urllist=`cat /usr/share/wechatpush/api/ipv4.list` 2>/dev/null + ipv6_urllist=`cat /usr/share/wechatpush/api/ipv6.list` 2>/dev/null + User_Agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58" + [ -z "$get_ipv4_mode" ] && get_ipv4_mode=0 + [ -z "$get_ipv6_mode" ] && get_ipv6_mode=0 + [ -z "$sleeptime" ] && sleeptime="60" + [ -z "$login_ip_black_timeout" ] && login_ip_black_timeout="86400" + [ -z "$login_ip_white_timeout" ] && login_ip_white_timeout="600" + [ "$iw_version" ] && wlan_interface=`iw dev 2>/dev/null|grep Interface|awk '{print $2}'` >/dev/null 2>&1 + [ -z "$server_port" ] && server_port="22" + output_dir="${dir}json_output" + mkdir -p "$output_dir" + if ( echo "$lite_enable"|grep -q "content" ); then + str_title_start="" && str_title_end="" && str_splitline="" && str_linefeed="" && str_tab="" + else + read_json "str_title_start" "$jsonpath" + read_json "str_title_end" "$jsonpath" + read_json "str_linefeed" "$jsonpath" + read_json "str_splitline" "$jsonpath" + read_json "str_space" "$jsonpath" + read_json "str_tab" "$jsonpath" + read_json "_api" "$jsonpath" + fi + wait_and_cat + disturb_text=$_api + deltemp + cron +} + +# 初始化 +function init(){ + enable_detection + echo "---------------------------------------------------------------------------------------" >> ${logfile} + echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】start running..." >> ${logfile} + if [ -f "/usr/share/wechatpush/errlog" ]; then + cat /usr/share/wechatpush/errlog > ${logfile} + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】载入上次重启前日志" >> ${logfile} + fi + /usr/libexec/wechatpush-call "down_oui" + + rm -f ${dir}fd1 ${dir}sheep_usage ${dir}old_sheep_usage ${dir}client_usage_aliases ${dir}old_client_usage_aliases /usr/share/wechatpush/errlog >/dev/null 2>&1 + [ ! -f "/usr/sbin/wrtbwmon" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】未安装 wrtbwmon ,流量统计不可用" >> ${logfile} + [ -z "$ip_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 iputils-arping 版本号,请确认插件是否正常运行" >> ${logfile} + [ -z "$cu_version" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取依赖项 curl 版本号,请确认插件是否正常运行" >> ${logfile} + [ -z "${sckey}${tg_token}${pushplus_token}${corpid}${wxpusher_apptoken}${wxpusher_uids}${wxpusher_topicIds}" -a "${jsonpath}" != "/usr/share/wechatpush/api/diy.json" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】请填写正确的 key " >> ${logfile} && return 1 + local interfacelist=`getinterfacelist` && [ -z "$interfacelist" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法获取接口在线时间等信息,可能存在多个接口或配置错误,请确认插件是否正常运行" >> ${logfile} + [ -n "$notification_temp" ] && [ -n "$temperature_threshold" ] && local cpu_temp=`soc_temp` || local cpu_temp="null" + [ -z "$cpu_temp" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备温度,请检查命令" >> ${logfile} + [ -n "$notification_load" ] && [ -n "$cpu_load_threshold" ] && local cpu_fuzai=`cat /proc/loadavg|awk '{print $1}'` 2>/dev/null || local cpu_fuzai="null" + [ -z "$cpu_fuzai" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】无法读取设备负载,请检查命令" >> ${logfile} + + [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv4" + [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv6" + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv4" + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv6" + + tmp_ip_list=`echo "$login_ip_white_list"|grep -v "^$"|sort -u` + while IFS= read -r tmp_ip; do + [ -n "$tmp_ip" ] && add_ip_white "$tmp_ip" "0" + done <<< "$tmp_ip_list" + set_ip_black + return 0 +} + +# 推送 +function diy_send(){ + ( ! echo "$lite_enable"|grep -q "content" ) && ( ! echo "$lite_enable"|grep -q "nowtime" ) && local nowtime=`date "+%Y-%m-%d %H:%M:%S"` + ! jq -r '.' ${3} >/dev/null 2>&1 && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】json 文件格式错误,这不是一个标准的 json 文件,请检查 ${3} 文件是否有特殊符号未转义或语法错误" >> ${logfile} && return 1 + local diyurl=`jq -r .url ${3}` && local diyurl=`eval echo ${diyurl}` + local type=`jq -r '.type' ${3}` && local type=`eval echo ${type}` + local data=`jq -r '.data' ${3}` && local data=`eval echo ${data}` + local content_type=`jq -r '.content_type' ${3}` + ! jq "$type" ${3} > ${tempjsonpath} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】type:{ } 字段转义变量后格式错误,请检查 type:{ } 字段内是否有特殊符号未转义或语法错误" >> ${logfile} && return 1 + + [ $4 ] && echo '{"url":"'${diyurl}'","content_type":"'${content_type}'","type":'`jq "$type" ${3}`'}' > ${dir}debug_send_json + [ $4 ] && echo -e "${send_title}" "${send_content}" > ${dir}debug_send_content + [ $4 ] && cat ${tempjsonpath} > ${dir}debug_send_data + [ $4 ] && ! jq -r '.' ${dir}debug_send_json && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】转义变量后格式错误,请检查 ${dir}debug_send_json 字段内是否有特殊符号未转义或语法错误" >> ${logfile} + [ $4 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】json 文件已保存至:${dir}debug_send_json" >> ${logfile} + [ $4 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】推送内容预览文件保存至:${dir}debug_send_content" >> ${logfile} + [ $4 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】如果收不到信息,请检查 ${dir}debug_send_data 文件,或使用下列命令手动测试返回值 (可能需要关闭日志自动刷新方便选中)" >> ${logfile} + [ $4 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【debug】"'curl -X POST -H "'$content_type'" -d "@'${dir}debug_send_data'" "'${diyurl}'" ' >> ${logfile} + + curl --connect-timeout 30 -m 60 -X POST -H "$content_type" -d "$data" "${diyurl}" + local RETVAL=$? + [ $RETVAL -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】网络错误或 URL 错误,推送失败,curl 返回值为 ${RETVAL}" >> ${logfile} && return 1 || return 0 +} + +# 清理临时文件 +function deltemp(){ + unset title content ipAddress_logrow online_list online_mac mac_online_status gatewayinfo gateway_iplist + rm -f ${dir}title ${dir}content ${dir}tmp_downlist ${dir}send_enable.lock ${tempjsonpath} ${dir}cookies.txt ${dir}tmp_sort_file >/dev/null 2>&1 + [ ! -f ${dir}ipAddress ] && rm -f ${dir}client/* >/dev/null 2>&1 + LockFile unlock + [ -f ${logfile} ] && local logrow=$(grep -c "" ${logfile}) || local logrow="0" + [ $logrow -gt 500 ] && sed -i '1,100d' ${logfile} && echo "`date "+%Y-%m-%d %H:%M:%S"` 【清理】日志超出上限,删除前 100 条" >> ${logfile} +} + +# 检测程序开关 +function enable_detection(){ + [ ! "$1" ] && local time_n=1 + for i in `seq 1 $time_n`; do + get_config enable;[ -z "$enable" ] || [ "$enable" -eq "0" ] && exit || sleep 1 + done +} + +# 获取 ip +function getip(){ + [ ! "$1" ] && return + + # 从接口获取 IPv4 + if [ $1 == "wanipv4" ] ;then + [ ! -z "$ipv4_interface" ] && local wanIP=$(/sbin/ifconfig ${ipv4_interface}|awk '/inet addr/ {print $2}'|awk -F: '{print $2}'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + [ -z "$ipv4_interface" ] && local wanIP=$(getinterfacelist|grep '\"address\"'|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + echo "$wanIP" + + # 从 URL 获取 IPv4 + elif [ $1 == "hostipv4" ] ;then + local url_number=`echo "$ipv4_urllist"|wc -l` + local rand_number=`rand 1 $url_number` + function get_hostipv4() + { + local ipv4_URL=`echo "$ipv4_urllist"| sed -n "${1}p"|sed -e 's/\r//g'` + [ ! -z "$ipv4_interface" ] && local tmp_hostIP=$(eval "curl -k -s -4 --interface ${ipv4_interface} -m 5 ${ipv4_URL}") || local tmp_hostIP=$(eval "curl -k -s -4 -m 5 ${ipv4_URL}") + [ -z "$tmp_hostIP" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】IP 获取失败,当前使用的 API 为 $ipv4_URL,接口为:${ipv4_interface}" >> ${logfile} + local tmp_hostIP=`echo $tmp_hostIP|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|head -n1` + echo "{\"IP\":\"${tmp_hostIP}\", \"URL\":\"${ipv4_URL}\"}" + } + local hostIP=`get_hostipv4 ${rand_number}` + [ -z `echo $hostIP|jq -r '.IP'` ] && local rand_number=`expr $rand_number + 1` && [ $rand_number -gt $url_number ] && local rand_number=1;[ -z `echo $hostIP|jq -r '.IP'` ] && local hostIP=`get_hostipv4 ${rand_number}` + [ -z `echo $hostIP|jq -r '.IP'` ] && local rand_number=`expr $rand_number + 1` && [ $rand_number -gt $url_number ] && local rand_number=1;[ -z `echo $hostIP|jq -r '.IP'` ] && local hostIP=`get_hostipv4 ${rand_number}` + [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z `echo $hostIP|jq -r '.IP'` ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv4" + [ -z `echo $hostIP|jq -r '.IP'` ] && ipv4_urllist=`cat /usr/share/wechatpush/api/ipv4.list` 2>/dev/null + echo $hostIP + + # 从接口获取 IPv6 + elif [ $1 == "wanipv6" ] ;then + [ ! -z "$ipv6_interface" ] && local wanIPv6=$(ip addr show ${ipv6_interface}|grep -v deprecated|grep -A1 'inet6 [^f:]'|sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;ta'|sort -nr|head -n1|awk '{print $2}') + [ -z "$ipv6_interface" ] && local wanIPv6=$(ip addr show|grep -v deprecated|grep -A1 'inet6 [^f:]'|sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;ta'|sort -nr|head -n1|awk '{print $2}') + echo "$wanIPv6" + + # 从 URL 获取 IPv6 + elif [ $1 == "hostipv6" ] ;then + local urlv6_number=`echo "$ipv6_urllist"|wc -l` + local rand_numberv6=`rand 1 $urlv6_number` + function get_hostipv6() + { + local ipv6_URL=`echo "$ipv6_urllist"| sed -n "${1}p"|sed -e 's/\r//g'` + [ ! -z "$ipv6_interface" ] && local tmp_hostIPv6=$(eval "curl -k -s -6 --interface ${ipv6_interface} -m 5 ${ipv6_URL}") || local tmp_hostIPv6=$(eval "curl -k -s -6 -m 5 ${ipv6_URL}") + [ -z "$tmp_hostIPv6" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】IP 获取失败,当前使用的 API 为 $ipv6_URL,接口为:${ipv6_interface}" >> ${logfile} + local tmp_hostIPv6=`echo $tmp_hostIPv6|grep -oE "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}"|head -n1` + echo "{\"IP\":\"${tmp_hostIPv6}\", \"URL\":\"${ipv6_URL}\"}" + } + local hostIPv6=`get_hostipv6 ${rand_numberv6}` + [ -z `echo $hostIPv6|jq -r '.IP'` ] && local rand_numberv6=`expr $rand_numberv6 + 1` && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1;[ -z `echo $hostIPv6|jq -r '.IP'` ] && local hostIPv6=`get_hostipv6 ${rand_numberv6}` + [ -z `echo $hostIPv6|jq -r '.IP'` ] && local rand_numberv6=`expr $rand_numberv6 + 1` && [ $rand_numberv6 -gt $urlv6_number ] && local rand_numberv6=1;[ -z `echo $hostIPv6|jq -r '.IP'` ] && local hostIPv6=`get_hostipv6 ${rand_numberv6}` + [ -n "$list_auto_up" ] && [ "$list_auto_up" -eq "1" ] && [ -z `echo $hostIP|jq -r '.IP'` ] && /usr/libexec/wechatpush-call "auto_update_ip_list" "ipv6" + [ -z `echo $hostIPv6|jq -r '.IP'` ] && ipv4_urllist=`cat /usr/share/wechatpush/api/ipv6.list` 2>/dev/null + echo $hostIPv6 + fi +} + +# 获取接口信息 +function getinterfacelist(){ + [ `ubus list|grep -w -i "network.interface.wan"|wc -l` -ge "1" ] && ubus call network.interface.wan status && return + local ubuslist=`ubus list|grep -i "network.interface."|grep -v "loopback"|grep -v -i "wan6"|grep -v -i "lan6"|grep -v -i "ipsec.*"|grep -v -i "VPN.*"|grep -v -i "DOCKER.*"` + [ `echo "${ubuslist}" |wc -l` -eq "1" ] && ubus call ${ubuslist} status && return +} + +# 获取接口在线时间 +function getinterfaceuptime(){ + getinterfacelist|awk -F'\"uptime\": ' '/uptime/ { gsub(/,/, "", $2); print $2 }' +} + +# 查询 MAC 地址 +function getmac(){ + # 已保存的 MAC + [ -f "${dir}ipAddress" ] && local tmp_mac=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $2}'|grep -v "^$"` + ( echo "$tmp_mac"|grep -q "unknown" ) && unset tmp_mac # 为 unknown 时重新读取 + [ -n "$tmp_mac" ] && [ "$(grep -w -i "${tmp_mac}" "${dir}ipAddress" | wc -l)" -gt "1" ] && unset tmp_mac # 某些路由器中继模式,会进行 MAC 克隆,重复值重新读取 + [ -f "${dir}tmp_downlist" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` + # DHCP + ( echo "$tmp_mac"|grep -q "unknown" ) && unset tmp_mac # 为 unknown 时重新读取 + [ -f "/tmp/dhcp.leases" ] && [ -z "$tmp_mac" ] && local tmp_mac=`cat /tmp/dhcp.leases|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` + # arp + [ -z "$tmp_mac" ] && local tmp_mac=`cat /proc/net/arp|grep "0x2\|0x6"|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + + [ -z "$tmp_mac" ] && local tmp_mac="unknown" + echo $tmp_mac |tr -d '\n\r' +} + +# 查询主机名 +function getname(){ + # 自定义备注 + local tmp_name=`echo "$device_aliases"|grep -i -w "^${1}\|^${2}"|awk '{ for(i=2; i<=NF; i++) printf $i " "; print "" }'|grep -v "^$"|sort -u|head -n1` + [ -n "$tmp_name" ] && echo "$tmp_name" | tr -d '\n\r' | awk '$1=$1' | sed 's/_/ /g' | sort -u | head -n1 && return + echo "$2" | grep -q -w "unknown\|*" && echo "unknown" && return # 当 MAC 获取失败时先不要尝试获取主机名 + # 已保存的 主机名 + [ -f "${dir}ipAddress" ] && [ -z "$tmp_name" ] && local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` + echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 + [ -z "$tmp_name" ] && [ -f "${dir}tmp_downlist" ] && local tmp_name=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` + # 静态地址备注名 + echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 + [ -z "$tmp_name" ] && dhcp_config=`uci show dhcp|grep "ip\|mac\|name"` + if [ -n "$dhcp_config" ]; then + dhcp_ip_n=$(echo "$dhcp_config" | grep -w ^dhcp.@${dhcp_config_str}.*ip=.${1} | sed -nr 's#^dhcp.(.*).ip.*#\1#gp' 2>/dev/null) + [ ! -z "$dhcp_ip_n" ] && [ -z "$tmp_name" ] && tmp_name=$(uci get dhcp.${dhcp_ip_n}.name 2>/dev/null) + [ -z "$tmp_name" ] && dhcp_mac_n=$(echo "$dhcp_config" | grep -i ^dhcp.@${dhcp_config_str}.*mac=.${2} | sed -nr 's#^dhcp.(.*).mac.*#\1#gp' 2>/dev/null) + [ ! -z "$dhcp_mac_n" ] && [ -z "$tmp_name" ] && tmp_name=$(uci get dhcp.${dhcp_mac_n}.name 2>/dev/null) + fi + # DHCP + echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 + [ -z "$tmp_name" ] && [ -f "/tmp/dhcp.leases" ] && local tmp_name=`cat /tmp/dhcp.leases|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + # 光猫 + echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 + [ -z "$tmp_name" ] && local tmp_name=$(cut_str "$(echo "$gatewayinfo"|grep -w ${1}|awk '{print $2}'|sort -u|head -n1)" "30") + # MAC设备信息数据库 + echo "$tmp_name" | grep -q -w "unknown\|*" && unset tmp_name # 为unknown时重新读取 + [ -z "$tmp_name" ] && [ -f "$oui_base" ] && local tmp_name=$(cat $oui_base|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp') + [ -z "$tmp_name" ] && [ ! -z "$oui_data" ] && [ "$oui_data" -eq "4" ] && local tmp_name=$(curl -sS "https://standards-oui.ieee.org/oui/oui.txt"|grep -i $(echo "$2"|cut -c 1,2,4,5,7,8)|sed -nr 's#^.*16)..(.*)#\1#gp') + + [ -z "$tmp_name" ] && local tmp_name="unknown" + echo "$tmp_name" | tr -d '\n\r' | awk '$1=$1' | sed 's/_/ /g' | sort -u | head -n1 +} + +# 从光猫处获取设备信息 +function getgateway(){ + [ -z "$gateway_info_enable" ] && return + last_getgateway_time=$(date -r ${dir}gateway_info +%s 2>/dev/null) || last_getgateway_time=0 + [ "$1" ] && [ "$1" == "reboot" ] && last_getgateway_time=0 + if [ `expr $(date +%s) - $last_getgateway_time` -gt "$device_info_helper_sleeptime" ]; then + # 登录 + local loginfo=`curl -s -L "${gateway_host_url}" -c ${dir}cookies.txt -d "${gateway_username_id}=${gateway_username}&${gateway_password_id}=${gateway_password}"` 2>/dev/null + [ ! -z "$loginfo" ] && local mytoken=$(echo $loginfo |sed 's/{/\n/g' | grep token |awk '/realRestart/{print $2}'| sed $'s/\'//g') + # 获取 + [ ! -z "$mytoken" ] && local get_gateway=`curl -s -b ${dir}cookies.txt "${gateway_info_url}" -d 'token='$mytoken | jq '.[] | iterables| "\(.ip) \(.devName) \(.model)"'|sed 's/unknown//g'|sed 's/ / /g'|sed 's/ /_/g'|sed 's/_/ /'|sed 's/\"//g'` + # 重启 + [ "$1" ] && [ "$1" == "reboot" ] && curl -s -b ${dir}/cookies.txt "${gateway_host_url}/admin/reboot" -d "token=$mytoken" >/dev/null 2>&1 + # 注销 + [ ! -z "$get_gateway" ] && [ ! -z "$gateway_logout_url" ] && curl -s -b ${dir}cookies.txt "${gateway_logout_url}" -d 'token='$mytoken 2>/dev/null + [ -z "$get_gateway" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【info】获取光猫信息失败,可能当前用户未注销或设置错误" >> ${logfile} + fi + [ ! -z "$get_gateway" ] && echo "$get_gateway" > ${dir}gateway_info +} + +# 扫描范围内 IP +function scanlocalip(){ + [ -z "$scan_ip_range" ] && return + [ -z "$last_scan_ip_time" ] && last_scan_ip_time=0 + if [ `expr $(date +%s) - $last_scan_ip_time` -gt "$device_info_helper_sleeptime" ]; then + start_ip=$(echo "$scan_ip_range" | cut -d "-" -f 1) + end_ip=$(echo "$scan_ip_range" | cut -d "-" -f 2) + + i=$(echo "$start_ip" | awk -F '.' '{print $NF}') + end_i=$(echo "$end_ip" | awk -F '.' '{print $NF}') + while [ "$i" -le "$end_i" ]; do + ping "${start_ip%.*}.$i" -c 1 >> /dev/null 2>&1 & + i=$((i + 1)) + done + fi +} + +# 查询设备接口 +function getinterface(){ + [ -z "${1}" ] && return + [ "${1}" == "unknown" ] && return + + [ -f "${dir}ipAddress" ] && local ip_interface=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` + [ -f "${dir}tmp_downlist" ] && [ -z "$ip_interface" ] && local ip_interface=`cat ${dir}tmp_downlist|grep -w ${1}|awk '{print $5}'|grep -v "^$"|sort -u|head -n1` + if [ -z "$ip_interface" ] && [ ! -z "$wlan_interface" ]; then + for interface in $wlan_interface; do + local ip_interface=`iw dev $interface station dump 2>/dev/null|grep Station|grep -i -w ${1}|sed -nr 's#^.*on (.*))#\1#gp'` >/dev/null 2>&1 + [ ! -z "$ip_interface" ] && echo "$ip_interface" && return + done + fi + [ -z "$ip_interface" ] && local ip_interface=`cat /proc/net/arp|grep "0x2\|0x6"|grep -i -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1` + echo $ip_interface |tr -d '\n\r' +} + +# ping +function getping(){ + ip_interface=`getinterface ${2}` + [ "$iw_version" ] && [ "$ip_interface" ] && wlan_online=`iw dev ${ip_interface} station dump 2>/dev/null|grep -i -w ${2}|grep Station` >/dev/null 2>&1 + [ "$wlan_online" ] && return 0 + arplist=$(cat /proc/net/arp) + for i in `seq 1 ${4}`; do + [ "$(echo "$arplist"|grep -i -w "${2}"|wc -l)" -eq 1 ] && interface=$(echo "$arplist"|grep -w ${1}|awk '{print $6}'|grep -v "^$"|sort -u|head -n1) && [ ! -z "$interface" ] && ip_ms=`arping -I ${interface} -c 20 -f -w ${3} ${1}` 2>/dev/null + ( ! echo "$ip_ms"|grep -q "ms" ) && ip_ms=`ping -c 5 -w ${3} ${1}|grep -v '100% packet loss'` 2>/dev/null + ( ! echo "$ip_ms"|grep -q "ms" ) && sleep 1 + done + echo "$ip_ms"|grep -q "ms" +} + +# CPU 占用率 +function getcpu(){ + local AT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') + sleep 1 + local BT=$(cat /proc/stat|grep "^cpu "|awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}') + printf "%.01f%%" $(echo ${AT} ${BT}|awk '{print (($4-$2)/($3-$1))*100}') +} + +# 获取SOC温度 (取所有传感器温度最大值) +function soc_temp(){ + [ ! -z "$soc_code" ] && eval `echo "$soc_code"` 2>/dev/null && return 0 + getsensors() { + # Intel + local sensor_field1='["coretemp-isa-0000"]["Package id 0"]["temp1_input"]' + # AMD + local sensor_field2='["zenpower-pci-00c3"]["Tctl"]["temp1_input"]' + local sensor_field3='["k10temp-pci-00c3"]["Tctl"]["temp1_input"]' + eval "${1} sensors -j 2>/dev/null" | jq -r " + if .${sensor_field1} != null then + .${sensor_field1} + elif .${sensor_field2} != null then + .${sensor_field2} + elif .${sensor_field3} != null then + .${sensor_field3} + else + null + end // \"\" + " + } + + [ ! -z "$server_host" ] && ssh_command="ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -i /root/.ssh/id_rsa root@${server_host} -p ${server_port}" + temperature=$(getsensors "$ssh_command" 2>/dev/null) + # 通用(只能取最高温度,不一定是 CPU,特殊设备自行修改) + # 将 grep °C 改为温度所在行的特别字符串,如 grep Core 0 等,就可以指定设备了 + [ -z "$temperature" ] && temperature=$(sensors 2>/dev/null|grep °C|sed -nr 's#^.*:.*\+(.*)°C .*#\1#gp'|sort -nr|head -n1) + # 将 thermal_zone* 改为 thermal_zone0 thermal_zone1 等,就可以指定设备了 + [ -z "$temperature" ] && temperature=$(cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null|sort -nr|head -n1|cut -c-2) + printf "%.1f" "$temperature" +} +# 流量数据 +function usage(){ + [ ! -f "/usr/sbin/wrtbwmon" ] || [ ! "$1" ] && return + # 更新 + if [ $1 == "update" ] ;then + function version_le() { test "$(echo "$@"|tr " " "\n"|sort -n|head -n 1)" == "$1"; } + function version_ge() { test "$(echo "$@"|tr " " "\n"|sort -r|head -n 1)" == "$1"; } + [ ! -z "$wr_version" ] && ( version_ge "${wr_version}" "1.2.0" ) && wrtbwmon -f ${dir}usage.db 2>/dev/null && return + [ ! -z "$wr_version" ] && ( version_le "${wr_version}" "1.0.0" ) || [ -z "$wr_version" ] && wrtbwmon update ${dir}usage.db 2>/dev/null && return + # 获取 + elif [ $1 == "get" ] ;then + [ ! -f "${dir}usage.db" ] && [ ! "$3" ] && echo `bytes_for_humans 0` && return + [ ! -f "${dir}usage.db" ] && [ "$3" ] && echo 0 && return + [ -z "$total_n" ] && total_n=`cat ${dir}usage.db|head -n1|grep "total"|sed 's/,/\n/g'|awk '/total/{print NR}'` 2>/dev/null + [ -z "$total_n" ] && total_n="6" + [ "$2" ] && local tmptotal=`cat ${dir}usage.db|sed 's/,,,/,0,0,/g'|sed 's/,,/,0,/g'|sed 's/,/ /g'|grep -i -w ${2}|awk "{print "'$'$total_n"}"|grep -v "^$"|sort -u|head -n1` 2>/dev/null + [ -z "$tmptotal" ] && local tmptotal="0" + [ ! "$3" ] && echo `bytes_for_humans ${tmptotal}` || echo $tmptotal + # 剔除 + elif [ $1 == "down" ] ;then + [ "$2" ] && sed -i "/,${2},/d" ${dir}usage.db 2>/dev/null + fi +} + +# 流量数据单位换算 +function bytes_for_humans { + [ ! "$1" ] && return + [ "$1" -gt 1073741824 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1073741824'}'` GB" && return + [ "$1" -gt 1048576 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1048576'}'` MB" && return + [ "$1" -gt 1024 ] && echo "`awk 'BEGIN{printf "%.2f\n",'$1'/'1024'}'` KB" && return + echo "${1} bytes" +} + +# 设备异常流量检测 +function get_client_usage(){ + [ -z "$client_usage" ] && return + [ "$client_usage" -ne "1" ] && return + [ -z "$client_usage_max" ] && return + + [ -z "$get_client_usage_time" ] && get_client_usage_time=`date +%s` + ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "K\|k" ) && client_usage_max=`expr ${client_usage_max%?} \* 1024` + ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "M\|m" ) && client_usage_max=`expr ${client_usage_max%?} \* 1048576` + ( echo $client_usage_max|sed -r 's/.*(.)$/\1/'|grep -q "G\|g" ) && client_usage_max=`expr ${client_usage_max%?} \* 1073741824` + [ -z "$client_usage_disturb" ] && client_usage_disturb="0" + [ "$client_usage_disturb" -eq "0" ] && [ -f "${dir}ipAddress" ] && local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` + [ "$client_usage_disturb" -eq "1" ] && [ ! -z "$client_usage_whitelist" ] && local MACLIST=`echo "$client_usage_whitelist"` + [ -z "$MACLIST" ] && return + + if [ "$((`date +%s`-$get_client_usage_time))" -ge "60" ]; then + > ${dir}client_usage_aliases + for mac in $MACLIST; do + ( ! cat ${dir}ipAddress|grep -q -i -w $mac|grep -v "^$"|sort -u|head -n1 ) && continue + echo "$mac" `usage get ${mac} bytes` >> ${dir}client_usage_aliases + [ -f "${dir}old_client_usage_aliases" ] && get_client_usage_bytes=`cat ${dir}old_client_usage_aliases|grep -i -w $mac|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` || continue + [ -z "$get_client_usage_bytes" ] && get_client_usage_bytes="0" + if [ "$((`usage get ${mac} bytes`-$get_client_usage_bytes))" -ge "$client_usage_max" ]; then + local ip=`cat ${dir}ipAddress|grep -i -w $mac|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` + local ip_name=`getname ${ip} ${mac}` + local tmp_usage=$(bytes_for_humans $(expr `usage get ${mac} bytes` - ${get_client_usage_bytes})) + local time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + local ip_total=`usage get $mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" + local time1=`date +%s` + local time1=$(time_for_humans `expr ${time1} - ${time_up}`) + if [ -z "$title" ]; then + title="${ip_name} 流量异常" + content="${content}${str_splitline}${str_title_start} 设备流量异常${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + elif ( echo "$title"|grep -q "流量异常" ); then + title="${ip_name} ${title}" + content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + else + title="设备状态变化" + content="${content}${str_splitline}${str_title_start} 设备流量异常${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${mac}$ip_total${str_linefeed}${str_tab}一分钟内流量: ${str_space}${str_space}${str_space}${tmp_usage}${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + fi + fi + done + cat ${dir}client_usage_aliases > ${dir}old_client_usage_aliases + get_client_usage_time=`date +%s` + fi +} + +# 时间单位换算 +function time_for_humans { + [ ! "$1" ] && return + + if [ "$1" -lt 60 ]; then + echo "${1} 秒" + elif [ "$1" -lt 3600 ]; then + local usetime_min=`expr $1 / 60` + local usetime_sec=`expr $usetime_min \* 60` + local usetime_sec=`expr $1 - $usetime_sec` + echo "${usetime_min} 分 ${usetime_sec} 秒" + elif [ "$1" -lt 86400 ]; then + local usetime_hour=`expr $1 / 3600` + local usetime_min=`expr $usetime_hour \* 3600` + local usetime_min=`expr $1 - $usetime_min` + local usetime_min=`expr $usetime_min / 60` + echo "${usetime_hour} 小时 ${usetime_min} 分" + else + local usetime_day=`expr $1 / 86400` + local usetime_hour=`expr $usetime_day \* 86400` + local usetime_hour=`expr $1 - $usetime_hour` + local usetime_hour=`expr $usetime_hour / 3600` + echo "${usetime_day} 天 ${usetime_hour} 小时" + fi +} + +# 计算字符显示宽度 +function length_str { + [ ! "$1" ] && return + + local length_zh=`echo "$1"|awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` + local length_en=`echo "$1"|awk '{print gensub(/[^\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}'` + + echo $((length_zh / 3 * 2 + length_en)) +} + +function cut_str { + [ ! "$1" ] && return + [ ! "$2" ] && return + + if [ $(length_str "$1") -le "$2" ]; then + echo "$1" + return + fi + + local columns=$(echo "$1" | awk '{print NF}') + if [ "$columns" -gt 1 ]; then + local max_length=0 + local max_column=1 + for i in $(seq 1 $columns); do + local length=$(echo "$1"|awk '{print gensub(/[\u4e00-\u9FA5A-Za-z0-9_]/,"","g",$0)}'|awk -F "" '{print NF}') + if [ "$length" -gt "$max_length" ]; then + max_length=$length + max_column=$i + fi + done + local text=$(echo -n "$1" | cut -d ' ' -f $max_column) + if [ $(length_str "$text") -le "$2" ]; then + echo "$text" + return + fi + fi + + local temp_length=$2 + while [ $(length_str "$(echo -n "$1" | cut -c -$temp_length)") -lt "$2" ]; do + temp_length=$(expr $temp_length + 1) + done + while [ $(printf "%d" \'$(echo -n "$1" | cut -c $temp_length)) -ge "128" ] && [ $(printf "%d" \'$(echo -n "$1" | cut -c $temp_length)) -lt "224" ]; do + temp_length=$(expr $temp_length + 1) + done + temp_length=$(expr $temp_length - 1) + + echo "$(echo -n "$1" | cut -c -$temp_length)".. +} + + + +# 随机数 +function rand(){ + local min=$1 + local max=$(($2- $min + 1)) + local num=$(date +%s%N) + echo $(($num % $max + $min)) +} + +# 在线设备列表 +function first(){ + [ -f "${dir}ipAddress" ] && local IPLIST_ipAddress=`cat ${dir}ipAddress|awk '{print $1}'|grep -v "^$"|sort -u` + getgateway + scanlocalip;last_scan_ip_time=$(date +%s) + for ip in $IPLIST_ipAddress; do + [ ! -z "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + read -u 5 + { + down $ip + echo "" >&5 + }& + done + wait + + local IPLIST=`cat /proc/net/arp|grep "0x2\|0x6"|awk '{print $1}'|grep -v "^169.254."|grep -v "^$"|sort -u|grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'` + [ -f "${dir}gateway_info" ] && gatewayinfo=`cat ${dir}gateway_info` + local gateway_iplist=`echo "${gatewayinfo}"|awk '{print $1}'` + [ ! -z "$gateway_iplist" ] && local IPLIST=`echo -e "${IPLIST}\n${gateway_iplist}"|grep -v "^$"|sort -u` + for ip in $IPLIST; do + [ ! -z "$passive_mode" ] && [ "$passive_mode" -eq "1" ] && break + read -u 5 + { + up $ip + echo "" >&5 + }& + done + wait + + [ -z "$passive_mode" ] && return + [ "$passive_mode" -ne "1" ] && return + for ip in $IPLIST; do + if ( ! echo "$IPLIST_ipAddress"|grep -q -i -w $ip ); then + ip_mac=`getmac $ip` + ip_name=`getname ${ip} ${ip_mac}` + ip_interface=`getinterface ${ip_mac}` + echo "{'ip': '${ip}','mac': '${ip_mac}','name': '${ip_name}','uptime': '0秒','interface': '${ip_interface}','usage': ''}" > ${dir}client/${ip} + usage down ${ip} + echo "${ip} ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress + fi + done + for ip in $IPLIST_ipAddress; do + if ( echo "$IPLIST"|grep -q -i -w $ip ); then + ip_mac=`getmac $ip` + ip_name=`getname ${ip} ${ip_mac}` + ip_interface=`getinterface ${ip_mac}` + time_up=`cat ${dir}ipAddress|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + time1=`date +%s` + time1=$(time_for_humans `expr ${time1} - ${time_up}`) + echo "{'ip': '${ip}','mac': '${ip_mac}','name': '${ip_name}','uptime': '${time1}','interface': '${ip_interface}','usage': '`usage get $ip_mac`'}" > ${dir}client/${ip} + else + sed -i "/^${ip} /d" ${dir}ipAddress + rm -f ${dir}client/${ip} >/dev/null 2>&1 + fi + done +} + +# 创建计划任务 +function cron(){ + function del_cron(){ + ( echo `crontab -l 2>/dev/null`|grep -q "wechatpush" ) && crontab -l > conf && sed -i "/wechatpush/d" conf && crontab conf && rm -f conf >/dev/null 2>&1 + } + function re_cron(){ + /etc/init.d/cron stop + /etc/init.d/cron start + } + del_cron + if [ -z "$enable" ]; then + re_cron + return + fi + + # 重置流量 + if [ ! -z "$reset_regularly" ] && [ "$reset_regularly" -eq "1" ]; then + crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm ${dir}usage.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + crontab -l 2>/dev/null > conf && echo -e "0 0 * * * rm ${dir}usage6.db >/dev/null 2>&1" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + fi + + # 定时发送 + if [ -n "$crontab_regular_time" ]; then + crontab_regular_time=`echo $crontab_regular_time|sed 's/ /,/g'` 2>/dev/null + crontab -l 2>/dev/null > conf && echo -e "0 $crontab_regular_time * * * /usr/share/wechatpush/wechatpush send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + # 间隔发送 + elif [ -n "$crontab_interval_time" ]; then + crontab -l 2>/dev/null > conf && echo -e "0 */$crontab_interval_time * * * /usr/share/wechatpush/wechatpush send &" >> conf && crontab conf && rm -f conf >/dev/null 2>&1 + fi + + re_cron +} + +# 免打扰检测 +function disturb(){ + [ -z "$do_not_disturb_mode" ] || [ -z "$do_not_disturb_starttime" ] || [ -z "$do_not_disturb_endtime" ] && return 0 + + # 非免打扰时间 + if [ `date +%H` -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ `date +%H` -lt $do_not_disturb_starttime -a $do_not_disturb_starttime -lt $do_not_disturb_endtime ] || [ `date +%H` -lt $do_not_disturb_starttime -a `date +%H` -ge $do_not_disturb_endtime -a $do_not_disturb_starttime -gt $do_not_disturb_endtime ]; then + unset sheep_starttime + rm -f ${dir}sheep_usage ${dir}old_sheep_usage 2>/dev/null + disturb_text=`jq -r '._api' ${jsonpath}` + return 0 + # 免打扰 + else + [ -z "$sheep_starttime" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【免打扰】夜深了,该休息了" >> ${logfile} && sheep_starttime=`date +%s` + # 挂起 + if [ "$do_not_disturb_mode" -eq "1" ] ;then + while [ `date +%H` -lt "$do_not_disturb_endtime" ]; do + enable_detection + sleep $sleeptime + done + # 静默 + elif [ "$do_not_disturb_mode" -eq "2" ] ;then + disturb_text="【免打扰】" + return 1 + fi + fi +} + +# 文件锁 +function LockFile(){ + if [ $1 = "lock" ] ;then + [ ! -f "${dir}wechatpush.lock" ] && > ${dir}wechatpush.lock && return + while [ -f "${dir}wechatpush.lock" ]; do + enable_detection 1 + done + LockFile lock + fi + [ $1 = "unlock" ] && rm -f ${dir}wechatpush.lock >/dev/null 2>&1 + return 0 +} + +# 检测黑白名单 +function blackwhitelist(){ + [ ! "$1" ] && return 1 + + [ -z "$up_down_push_whitelist" ] && [ -z "$up_down_push_blacklist" ] && [ -z "$up_down_push_interface" ] && [ -z "$mac_online_list" ] && [ -z "$mac_offline_list" ] && return 0 + [ ! -z "$up_down_push_whitelist" ] && ( echo "$up_down_push_whitelist"|grep -q -i -w $1 ) && return 1 + [ ! -z "$up_down_push_blacklist" ] && ( ! echo "$up_down_push_blacklist"|grep -q -i -w $1 ) && return 1 + [ ! -z "$up_down_push_interface" ] && ( ! echo `getinterface ${1}`|grep -q -i -w $up_down_push_interface ) && return 1 + [ ! -z "$mac_online_list" ] && [ ! -z "$mac_online_status" ] && return 1 + [ ! -z "$mac_online_list" ] && ( echo "$mac_online_list"|grep -q -i -w $1 ) && return 1 + [ ! -z "$mac_offline_list" ] && [ -z "$mac_online_status" ] && return 1 + + return 0 +} + +# 重启网络服务 +function network_restart(){ +cat>${dir}network_restart</dev/null 2>&1 & +/etc/init.d/firewall restart >/dev/null 2>&1 & +/etc/init.d/dnsmasq restart >/dev/null 2>&1 & +EOF + chmod 0755 ${dir}network_restart && ${dir}network_restart + rm -f ${dir}network_restart >/dev/null 2>&1 +} + +# 查看无人值守任务设备是否在线 +function geterrdevicealiases(){ + [ -z "$unattended_device_aliases" ] && return + [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return + + local MACLIST=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` + for mac in $MACLIST; do + [ -z "$unattended_mac" ] && [ ! -z "$mac" ] && local unattended_mac=`echo "$unattended_device_aliases"|grep -i $mac|grep -v "^$"|sort -u|head -n1` + done + # 进入免打扰时间已经超过一小时 + if [ ! -z "$sheep_starttime" ] && [ "$((`date +%s`-$sheep_starttime))" -ge "3600" ]; then + > ${dir}sheep_usage + local MACLIST=`echo "$unattended_device_aliases"|grep -v "^$"|sort -u` + while IFS= read -r mac; do + [ ! -z "$mac" ] && local tmptotal=`usage get ${mac} bytes` + [ ! -z "$tmptotal" ] && awk 'BEGIN{printf "%.0f\n",'$tmptotal'/'204800'}' 2>/dev/null >> ${dir}sheep_usage + done <<< "$MACLIST" + old_sheep_usage=`cat ${dir}old_sheep_usage` 2>/dev/null + sheep_usage=`cat ${dir}sheep_usage` 2>/dev/null + [ "$old_sheep_usage" == "$sheep_usage" ] && [ -z "$sheep_nousage_starttime" ] && sheep_nousage_starttime=`date +%s` + [ "$old_sheep_usage" != "$sheep_usage" ] && unset sheep_nousage_starttime && cat ${dir}sheep_usage 2>/dev/null > ${dir}old_sheep_usage + [ ! -z "$sheep_nousage_starttime" ] && [ "$((`date +%s`-$sheep_nousage_starttime))" -ge "300" ] && unset unattended_mac + fi + [ -z "$unattended_mac" ] +} + +# 无人值守任务 +function unattended(){ + [ -z "$unattended_enable" ] || [ "$unattended_enable" -ne "1" ] && return + [ ! -z "$unattended_only_on_disturb_time" ] && [ "$unattended_only_on_disturb_time" -eq "1" ] && [ -z "$sheep_starttime" ] && return + geterrdevicealiases;[ $? -eq "1" ] && return + + if [ ! -z "$unattended_autoreboot_mode" ]; then + local interfaceuptime=`getinterfaceuptime` + if [ ! -z "$autoreboot_system_uptime" ] && [ `cat /proc/uptime|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$autoreboot_system_uptime" ] && [ "$unattended_autoreboot_mode" -eq "1" ]; then + echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重启路由器咯" >> ${logfile} + cat ${logfile} > /usr/share/wechatpush/errlog + sleep 2 && reboot && exit + elif [ ! -z "$autoreboot_network_uptime" ] && [ ! -z "$interfaceuptime" ] && [ `echo "$interfaceuptime"|awk -F. '{run_hour=$1/3600;printf("%d",run_hour)}'` -ge "$autoreboot_network_uptime" ] && [ "$unattended_autoreboot_mode" -eq "2" ]; then + echo "`date "+%Y-%m-%d %H:%M:%S"` 【无人值守任务】重新拨号咯" >> ${logfile} + ifup wan >/dev/null 2>&1 + sleep 60 + fi + fi +} + +# 检测网络状态 +function rand_geturl(){ + # 获取网络状态 + function getcheck(){ + local urllist="https://www.163.com https://www.qq.com https://www.baidu.com https://www.qidian.com https://www.douban.com" + local url_number=`expr $(echo "$urllist"|grep -o ' '|wc -l) + 1` + local url_str=`echo "$urllist"|awk -v i=$(rand 1 $url_number) '{print $i}'` + echo `curl -k -s -w "%{http_code}" -m 5 ${url_str} -A "${User_Agent}" -o /dev/null` + } + local check=`getcheck` + while [ -z "$check" ] || [[ $check -ne 200 && $check -ne 301 && $check -ne 302 ]]; do + local check=`getcheck` + if [ ! -z "$check" ] && [[ $check -eq 200 || $check -eq 301 || $check -eq 302 ]]; then + [ ! -z "$network_enable" ] && [ "$network_enable" -eq "404" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【网络状态】网络恢复正常.." >> ${logfile} + local network_enable="200" + else + [ -z "$network_enable" ] || [ "$network_enable" -eq "200" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】当前网络不通!停止检测! " >> ${logfile} + local network_enable="404" + # 无人值守、待弃用或改进 + [ -z "$network_unattended_time" ] && network_unattended_time=`date +%s` + if [ ! -z "$network_disconnect_event" ] && [ "$((`date +%s`-$network_unattended_time))" -ge "600" ]; then + > ${dir}send_enable.lock && first && deltemp + geterrdevicealiases + if [ "$?" -eq "0" ]; then + [ -f /usr/share/wechatpush/autoreboot_count ] && retry_count=`cat /usr/share/wechatpush/autoreboot_count` && rm -f /usr/share/wechatpush/autoreboot_count >/dev/null 2>&1 + [ ! -z "${retry_count}" ] && retry_count=0;retry_count=`expr $retry_count + 1` + if [ "$network_disconnect_event" -eq "1" ] ;then + if [ "$retry_count" -lt "3" ] ;then + echo "$retry_count" > /usr/share/wechatpush/autoreboot_count + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启路由,当前第 $retry_count 次 " >> ${logfile} + cat ${logfile} > /usr/share/wechatpush/errlog + getgateway "reboot" + sleep 2 && reboot && exit + fi + [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启路由2次,修复失败,请主人自行修复哦" >> ${logfile} + elif [ "$network_disconnect_event" -eq "2" ] ;then + [ "$retry_count" -lt "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】正在尝试重启网络,当前第 $retry_count 次 " >> ${logfile} && ifup wan >/dev/null 2>&1 + [ "$retry_count" -eq "3" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!!】已经重启网络2次,修复失败,请主人自行修复哦 " >> ${logfile} + fi + fi + elif [ -f /usr/share/wechatpush/autoreboot_count ]; then + network_unattended_time=`expr $network_unattended_time - 600` && sleep 60 + fi + enable_detection + sleep $sleeptime + fi + continue + done + rm -f /usr/share/wechatpush/autoreboot_count >/dev/null 2>&1 +} + +# 检测 ip 状况 +function ip_changes(){ + [ "$get_ipv4_mode" -eq "1" ] && local IPv4=`getip wanipv4` && local IPv4_URL="网络接口" + [ "$get_ipv4_mode" -eq "2" ] && local IPv4=`getip hostipv4` && local IPv4_URL=`echo ${IPv4}|jq -r '.URL'` && local IPv4=`echo ${IPv4}|jq -r '.IP'` + [ "$get_ipv6_mode" -eq "1" ] && local IPv6=`getip wanipv6` && local IPv6_URL="网络接口" + [ "$get_ipv6_mode" -eq "2" ] && local IPv6=`getip hostipv6` && local IPv6_URL=`echo ${IPv6}|jq -r '.URL'` && local IPv6=`echo ${IPv6}|jq -r '.IP'` + + # 存在临时文件 + if [ -f ${dir}ip ]; then + local last_IPv4=$(cat "${dir}ip"|grep IPv4|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) + local last_IPv6=$(cat "${dir}ip"|grep IPv6|awk '{print $2}'|grep -v "^$"|sort -u|head -n1) + if [ "$get_ipv4_mode" -ne "0" ] && [ ! -z "$IPv4" ] && ( ! echo ${IPv4}|grep -w -q ${last_IPv4} ); then + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IP:${IPv4} from:${IPv4_URL}" >> ${logfile} + echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $last_IPv6 >> ${dir}ip + title="IP 地址变化" + content="${content}${str_splitline}${str_title_start} IP 地址变化${str_title_end}${str_linefeed}${str_tab}当前 IP:${IPv4}" + fi + + if [ "$get_ipv6_mode" -ne "0" ] && [ ! -z "$IPv6" ] && ( ! echo "$IPv6"|grep -w -q ${last_IPv6} ); then + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IPv6:${IPv6} from:${IPv6_URL}" >> ${logfile} + echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip + [ -z "$title" ] && title="IPv6 地址变化" + [ ! -z "$title" ] && title="IP 地址变化" + content="${content}${str_splitline}${str_title_start} IPv6 地址变化${str_title_end}${str_linefeed}${str_tab}当前 IPv6:${IPv6}" + fi + + # 临时文件目录为空 + else + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}路由器已经重启!" >> ${logfile} + [ "$get_ipv4_mode" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IP: ${IPv4} from:${IPv4_URL}" >> ${logfile} + [ "$get_ipv6_mode" -ne "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}当前 IPv6: ${IPv6} from:${IPv6_URL}" >> ${logfile} + echo IPv4 $IPv4 > ${dir}ip && echo -e IPv6 $IPv6 >> ${dir}ip + title="路由器重新启动" + content="${content}${str_splitline}${str_title_start} 路由器重新启动${str_title_end}" + [ "$get_ipv4_mode" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IP:${IPv4}" + [ "$get_ipv6_mode" -ne "0" ] && content="${content}${str_linefeed}${str_tab}当前IPv6:${IPv6}" + fi + + # IP 变化,悄咪咪的重启 zerotier + if [ ! -z "$content" ] && [ -n "$zerotier_helper" ] && [ "$zerotier_helper" -eq "1" ];then + [ -z "$zerotier_enabled" ] && zerotier_enabled=$(uci get zerotier.sample_config.enabled) + if [ ! -z "$zerotier_enabled" ] && [ $zerotier_enabled -eq "1" ] ; then + /etc/init.d/zerotier restart >/dev/null 2>&1 + fi + fi +} + +# 检测设备上线 +function up(){ + [ -f ${dir}ipAddress ] && ( cat ${dir}ipAddress|grep -q -w $1 ) && return + + local ip_mac=`getmac $1` + local ip_name=`getname ${1} ${ip_mac}` + local ip_interface=`getinterface ${ip_mac}` + getping ${1} ${ip_mac} ${up_timeout} "1";local ping_online=$? + + # 连通 + if [ "$ping_online" -eq "0" ]; then + LockFile lock + [ ! -z "$up_down_push_blacklist" ] && local tmp_mac=`echo "${up_down_push_blacklist}"|grep -w -i ${ip_mac}` + [ ! -z "$up_down_push_whitelist" ] && local tmp_mac=`echo "${up_down_push_whitelist}"|grep -w -i ${ip_mac}` + echo "{'ip': '${1}','mac': '${ip_mac}','name': '${ip_name}','uptime': '0秒','interface': '${ip_interface}','usage': ''}" > ${dir}client/${1} + # ??? + if [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ); then + usage down ${1} + echo "${1} ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress + LockFile unlock && return + # ??? + elif [ ! -z "$tmp_mac" ] && [ -f "${dir}tmp_downlist" ] && ( cat ${dir}tmp_downip|grep -q -w -i ${tmp_mac} ); then + local tmp_downip=`cat ${dir}tmp_downlist|grep -w -i ${tmp_mac}|awk '{print $1}'|grep -v "^$"|sort -u|head -n1` + usage down $tmp_downip + sed -i "/^${tmp_downip} /d" ${dir}tmp_downlist + LockFile unlock && return + fi + # 从离线二次验证区恢复信息 + [ -f "${dir}tmp_downlist" ] && local tmp_downip=`cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1` + if [ ! -z "$tmp_downip" ]; then + cat ${dir}tmp_downlist|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}ipAddress + sed -i "/^${1} /d" ${dir}tmp_downlist + # up + else + usage down $1 + echo "$1 ${ip_mac} ${ip_name// /_} `date +%s` ${ip_interface}" >> ${dir}ipAddress + blackwhitelist ${ip_mac};local ip_blackwhite=$? + [ -f "${dir}send_enable.lock" ] || [ -z "$notification_online" ] || [ -z "$ip_blackwhite" ] && LockFile unlock && return + [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && LockFile unlock && return + + [ -f "${dir}title" ] && local title=`cat ${dir}title` + [ -f "${dir}content" ] && local content=`cat ${dir}content` + if [ -z "$title" ]; then + local title="$ip_name 连接了你的路由器" + local content="${str_splitline}${str_title_start} 新设备连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" + elif ( echo ${title}|grep -q "连接了你的路由器" ); then + local title="${ip_name} ${title}" + local content="${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" + else + local title="设备状态变化" + local content="${str_splitline}${str_title_start} 新设备连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${1}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}${str_linefeed}${str_tab}网络接口:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_interface}" + fi + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}新设备 ${ip_name} ${1} 连接了">> ${logfile} + #[ ! -z "$up_down_push_blacklist" ] && local title="你偷偷关注的设备上线了" + [ ! -z "$title" ] && echo "$title" >${dir}title + [ ! -z "$content" ] && echo -n "$content" >>${dir}content + fi + fi + LockFile unlock +} + +# 检测设备离线 +function down(){ + local ip_mac=`getmac $1` + local ip_name=`getname ${1} ${ip_mac}` + local ip_interface=`getinterface ${ip_mac}` + + tmp_timeout=$down_timeout && tmp_retry_count=$timeout_retry_count + [ -n "$only_timeout_push" ] && blackwhitelist ${ip_mac};local ip_blackwhite=$? && [ "$ip_blackwhite" -ne "0" ] && tmp_timeout=10 && tmp_retry_count=2 + getping ${1} ${ip_mac} ${tmp_timeout} ${tmp_retry_count};local ping_online=$? + # 离线,置入二次验证区 + if [ "$ping_online" -eq "1" ]; then + LockFile lock + [ ! -f "${dir}send_enable.lock" ] && cat ${dir}ipAddress|grep -w ${1}|grep -v "^$"|sort -u|head -n1 >> ${dir}tmp_downlist + sed -i "/^${1} /d" ${dir}ipAddress + sed -i "/^${1} /d" ${dir}gateway_info + rm -f ${dir}client/${1} >/dev/null 2>&1 + LockFile unlock + # 更新主机名或 MAC + else + local tmp_name=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $3}'|grep -v "^$"|sort -u|head -n1` + local tmp_mac=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $2}'|grep -v "^$"|sort -u|head -n1` + if [ ${ip_name// /_} != ${tmp_name} ] || [ ${ip_mac} != ${tmp_mac} ]; then + LockFile lock + olduptime=$(cat ${dir}ipAddress|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1) + sed -i "/^${1} /d" ${dir}ipAddress + [ ${ip_mac} != ${tmp_mac} ] && ip_name="unknown" # MAC 变化时应删除主机名重新获取 + echo "$1 ${ip_mac} ${ip_name// /_} ${olduptime} ${ip_interface}" >> ${dir}ipAddress + LockFile unlock + fi + local time_up=`cat ${dir}ipAddress|grep -w ${1}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + local time1=`date +%s` + local time1=$(time_for_humans `expr ${time1} - ${time_up}`) + echo "{'ip': '${1}','mac': '${ip_mac}','name': '${ip_name}','uptime': '${time1}','interface': '${ip_interface}','usage': '`usage get $ip_mac`'}" > ${dir}client/${1} + fi +} + +# 设备离线通知 +function down_send(){ + [ ! -f "${dir}tmp_downlist" ] && return + + local IPLIST=`cat ${dir}tmp_downlist|awk '{print $1}'` + for ip in $IPLIST; do + local ip_mac=`getmac ${ip}` + blackwhitelist ${ip_mac};local ip_blackwhite=$? + [ -z "$notification_offline" ] || [ -z "$ip_blackwhite" ] && continue + [ -z "$ip_blackwhite" ] || [ "$ip_blackwhite" -ne "0" ] && continue + [ ! -z "$up_down_push_blacklist" ] && local tmp_mac=`echo "${up_down_push_blacklist}"|grep -w -i ${ip_mac}` + [ ! -z "$up_down_push_whitelist" ] && local tmp_mac=`echo "${up_down_push_whitelist}"|grep -w -i ${ip_mac}` + [ ! -z "$tmp_mac" ] && ( cat ${dir}ipAddress|grep -q -w -i ${tmp_mac} ) && continue + + local ip_name=`getname ${ip} ${ip_mac}` + local time_up=`cat ${dir}tmp_downlist|grep -w ${ip}|awk '{print $4}'|grep -v "^$"|sort -u|head -n1` + local ip_total=`usage get $ip_mac` && [ ! -z "$ip_total" ] && local ip_total="${str_linefeed}${str_tab}总计流量: ${str_space}${str_space}${str_space}${str_space}${ip_total}" + local time1=`date +%s` + local time1=$(time_for_humans `expr ${time1} - ${time_up}`) + if [ -z "$title" ]; then + title="${ip_name} 断开连接" + content="${content}${str_splitline}${str_title_start} 设备断开连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + elif ( echo "$title"|grep -q "断开连接" ); then + title="${ip_name} ${title}" + content="${content}${str_splitline}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + else + title="设备状态变化" + content="${content}${str_splitline}${str_title_start} 设备断开连接${str_title_end}${str_linefeed}${str_tab}客户端名:${str_space}${str_space}${str_space}${str_space}${str_space}${ip_name}${str_linefeed}${str_tab}客户端IP: ${str_space}${str_space}${str_space}${str_space}${ip}${str_linefeed}${str_tab}客户端MAC:${str_space}${str_space}${str_space}${str_space}${ip_mac}$ip_total${str_linefeed}${str_tab}在线时间: ${str_space}${str_space}${str_space}${str_space}${time1}" + fi + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}设备 ${ip_name} ${ip} 断开连接 " >> ${logfile} + done + + rm -f ${dir}tmp_downlist >/dev/null 2>&1 +} + +# 当前设备列表 +function current_device(){ + ( echo "$lite_enable"|grep -q "content" ) || ( echo "$lite_enable"|grep -q "device" ) && return + [ -f ${dir}ipAddress ] && local logrow=$(grep -c "" ${dir}ipAddress) || local logrow="0";[ $logrow -eq "0" ] && return + + [ -f ${dir}usage.db ] && local ip_total_db="总计流量${str_space}${str_space}${str_space}${str_space}" + content="${content}${str_splitline}${str_title_start} 现有在线设备 ${logrow} 台,具体如下${str_title_end}${str_linefeed}${str_tab}IP 地址${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${str_space}${ip_total_db}客户端名" + local IPLIST=`cat ${dir}ipAddress|awk '{print $1}'` + for ip in $IPLIST; do + local ip_mac=`getmac ${ip}` + local ip_total=`usage get ${ip_mac}` + local ip_name=`getname ${ip} ${ip_mac}` + local ip_name=`cut_str "$ip_name" "15"` + if [ "${#ip}" -lt "15" ]; then + local n=`expr 15 - ${#ip}` + for i in `seq 1 $n`; do + local ip="${ip}${str_space}" + done + unset i n + fi + if [ ! -z "$ip_total" ]; then + local n=`expr 11 - ${#ip_total}` + for i in `seq 1 $n`; do + local ip_total="${ip_total}${str_space}" + done + fi + content="${content}${str_linefeed}${str_tab}${ip}${ip_total}${ip_name}" + unset i n ip_total ip_mac ip_name + done +} + +# 检测 cpu 状态 +function cpu_load(){ + if [ -n "$notification_temp" ] && [ -n "$temperature_threshold" ]; then + [ -z "$temp_last_overload_time" ] && temp_last_overload_time=`date +%s` + local cpu_temp=`soc_temp`; + + if [ ! -z "$cpu_temp" ] && [ `expr $cpu_temp \> $temperature_threshold` -eq "1" ]; then + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 温度过高: ${cpu_temp}" >> ${logfile} + else + temp_last_overload_time=`date +%s` + fi + + if [ ! -z "$cpu_temp" ] && [ "$((`date +%s`-$temp_last_overload_time))" -ge "300" ] && [ -z "$temperaturecd_time" ]; then + title="CPU 温度过高!" + temperaturecd_time=`date +%s` + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 温 度过高: ${cpu_temp}" >> ${logfile} + content="${content}${str_splitline}${str_title_start} CPU 温度过高${str_title_end}${str_linefeed}${str_tab}CPU 温度已连续五分钟超过预设${str_linefeed}${str_tab}接下来一小 时不再提示${str_linefeed}${str_tab}当前温度:${cpu_temp}℃" + elif [ ! -z "$temperaturecd_time" ] && [ "$((`date +%s`-$temperaturecd_time))" -ge "3300" ] ;then + unset temperaturecd_time + fi + fi + + if [ -n "$notification_load" ] && [ -n "$cpu_load_threshold" ]; then + [ -z "$cpu_last_overload_time" ] && cpu_last_overload_time=`date +%s` + local cpu_fuzai=`cat /proc/loadavg|awk '{print $1}'` 2>/dev/null + + if [ ! -z "$cpu_fuzai" ] && [ `expr $cpu_fuzai \> $cpu_load_threshold` -eq "1" ]; then + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 负载过高: ${cpu_fuzai}" >> ${logfile} + cputop log + elif [ ! -z "$cpu_fuzai" ]; then + cpu_last_overload_time=`date +%s` + fi + + if [ ! -z "$cpu_fuzai" ] && [ "$((`date +%s`-$cpu_last_overload_time))" -ge "300" ] && [ -z "$cpucd_time" ]; then + unset getlogtop + if [ ! -z "$title" ] && ( echo "$title"|grep -q "过高" ); then + title="设备报警!" + else + title="CPU 负载过高!" + fi + cpucd_time=`date +%s` + echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text} CPU 负 载过高: ${cpu_fuzai}" >> ${logfile} + content="${content}${str_splitline}${str_title_start} CPU 负载过高${str_title_end}${str_linefeed}${str_tab}CPU 负载已连续五分钟超过预设${str_linefeed}${str_tab}接下来一小 时不再提示${str_linefeed}${str_tab}当前负载:${cpu_fuzai}" + cputop + elif [ ! -z "$cpucd_time" ] && [ "$((`date +%s`-$cpucd_time))" -ge "3300" ] ;then + unset cpucd_time + fi + fi +} + +# CPU 占用前三 +function cputop(){ + [ -z "$1" ] && content="${content}${str_splitline}${str_title_start} 当前 CPU 占用前三的进程${str_title_end}" + local gettop=`top -bn 1|grep -v "top -bn 1"` + for i in `seq 5 7`; do + local top_name=`echo "${gettop}"|awk 'NR=='${i}|awk '{print ($8 ~ /\/bin\/sh|\/bin\/bash/) ? $9 : $8}'` + local top_load=`echo "${gettop}"|awk 'NR=='${i}|awk '{print $7}'` + local temp_top="${top_name} ${top_load}" + [ ! -z "$1" ] && local logtop="$logtop $temp_top" + [ -z "$1" ] && content="${content}${str_linefeed}${str_tab}${temp_top}" + done + unset i + [ ! -z "$1" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!警报!!】 CPU 占用前三: ${logtop}" >> ${logfile} +} + +# 检测硬盘状态 +function get_disk() { + mkdir -p "${dir}disk_info" + local_disk_names=() + pve_disk_names=() + + # 查询本地硬盘名 + local_disk_names=($(lsblk | awk '$NF=="disk" {print $1}' | sort -u)) + local_disk_tags=($(for _ in "${local_disk_names[@]}"; do echo "local"; done)) + + # 查询远程硬盘名 + if [ -n "$server_host" ]; then + ssh_command="ssh -o StrictHostKeyChecking=yes -o BatchMode=yes -i /root/.ssh/id_rsa root@${server_host} -p ${server_port}" + pve_disk_names=($(eval ${ssh_command} lsblk | awk '$NF=="disk" {print $1}' | sort -u)) + pve_disk_tags=($(for _ in "${pve_disk_names[@]}"; do echo "pve"; done)) + fi + + # 合并本地和远程硬盘名及标记 + all_disk_names=("${local_disk_names[@]}" "${pve_disk_names[@]}") + all_disk_tags=("${local_disk_tags[@]}" "${pve_disk_tags[@]}") + + for i in "${!all_disk_names[@]}"; do + tmp_name=${all_disk_names[i]} + tmp_tag=${all_disk_tags[i]} + error_pattern="No such device|Unable to detect device type|Unknown USB bridge|QEMU HARDDISK" + unset tmp_command disk_type disk_err + + # 判断硬盘类型 + if [ "$tmp_tag" == "pve" ]; then + tmp_command="$ssh_command" + disk_type="_pve" + fi + file_path="${dir}disk_info/${tmp_name}${disk_type}" + + # 如果不能获取值,使用分区名重试(因为不清楚是 OpenWrt 的原因还是 smartctl 版本的原因,使用出错重试的方式) + eval ${tmp_command} smartctl -i -n standby "/dev/${tmp_name}" | grep -qE "$error_pattern" && { + tmp_name=$(df -h | awk "/^\\/dev\\/${tmp_name}/ {print \$1}" | awk -F '/' '{print $NF}' | head -n1) + [ -z "$tmp_name" ] && continue + } + + # 手上的硬盘不能休眠,不确定命令是否会唤醒硬盘,每天只运行一次 + last_disk_time=$(date -r "${file_path}" +%s 2>/dev/null) || last_disk_time=0 + + if [ $(( $(date +%s) - $last_disk_time )) -gt 86000 ]; then + disk_info=$(eval ${tmp_command} smartctl -i -n standby "/dev/${tmp_name}") + echo "$disk_info" | grep -qE "$error_pattern" && { + continue + } || \ + echo "$disk_info" | grep -q "STANDBY" && { + echo "$disk_info" > "${file_path}" + } || \ + eval ${tmp_command} smartctl -a -j /dev/${tmp_name} > ${file_path} + fi + + # 硬盘状态 + if [ -f "${file_path}" ] && ( ! cat "${file_path}" | grep -q -v "STANDBY" ); then + disk_name=$(awk '/Device Model/{print $NF}' "$file_path") + [[ -n $disk_name && $disk_name != null && $disk_name != 0 ]] && { + disk_name=$(cut_str "$disk_name" "20") + disk_name="${disk_name}_$(eval ${tmp_command} lsblk -o NAME,SIZE | awk "/^${all_disk_names[i]}/ {print \$NF}")" + } + printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}${str_linefeed}${str_tab}硬盘休眠中" >> "$output_dir/get_disk" + elif [ -f "${file_path}" ]; then + # 硬盘名称 + disk_name=$(jq -r .model_name ${file_path}) + [[ -n $disk_name && $disk_name != null && $disk_name != 0 ]] && { + disk_name=$(cut_str "$disk_name" "20") + disk_name="${disk_name}_$(eval ${tmp_command} lsblk -o NAME,SIZE | awk "/^${all_disk_names[i]}/ {print \$NF}")" + } + printf "${str_linefeed}${str_title_start} 硬盘名称:${disk_name}${disk_type}${str_title_end}" >> "$output_dir/get_disk" + # 硬盘温度 + disk_temp=$(jq -r .temperature.current ${file_path}) + [[ -n $disk_temp && $disk_temp != null && $disk_temp != 0 ]] && printf "${str_linefeed}${str_tab}硬盘温度:${disk_temp}℃" >> "$output_dir/get_disk" + # 通电时间 + disk_time=$(jq -r .power_on_time.hours ${file_path}) + [[ -n $disk_time && $disk_time != null ]] && printf "${str_linefeed}${str_tab}通电时间:${disk_time}h" >> "$output_dir/get_disk" + # 空间使用 + disk_use=$(eval ${tmp_command} lsblk -o NAME,FSUSE%,TYPE | awk -v part_name="${all_disk_names[i]}" '$NF == "part" && $1 ~ part_name && NF > 2 {sub(".*" part_name, part_name, $1); printf "%s: %s ", $1, $2} END {print ""}') + [ -n "$disk_use" ] && [ -n "${disk_use// }" ] && echo -e -n "${str_linefeed}${str_tab}空间使用:${disk_use}" >> "$output_dir/get_disk" + # 寿命使用 + disk_health=$(jq -r .nvme_smart_health_information_log.percentage_used ${file_path}) + [[ -n $disk_health && $disk_health != null ]] && echo -e -n "${str_linefeed}${str_tab}寿命使用:${disk_health}%" >> "$output_dir/get_disk" + # 错误日志 + disk_log_err=$(jq -r .ata_smart_error_log.summary.count ${file_path}) + [[ -n $disk_log_err && $disk_log_err != null && $disk_log_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}错误日志:${disk_log_err}" >> "$output_dir/get_disk" + # 自检错误 + disk_test_err=$(jq -r .ata_smart_self_test_log.standard.error_count_total ${file_path}) + [[ -n $disk_test_err && $disk_test_err != null && $disk_test_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}自检错误:${disk_test_err}" >> "$output_dir/get_disk" + # 0E 错误 + disk_0e_err=$(jq -r .nvme_smart_health_information_log.media_errors ${file_path}) + [[ -n $disk_0e_err && $disk_0e_err != null && $disk_0e_err != 0 ]] && disk_err="true" && printf "${str_linefeed}${str_tab}0E 错误:${disk_0e_err}" >> "$output_dir/get_disk" + # 整体健康 + smart_status=$(jq -r .smart_status.passed ${file_path}) + [[ -n $smart_status && $smart_status != null && $smart_status != "true" ]] && { + echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘整体健康评估不通过!!!${str_title_end}" >> "$output_dir/get_disk" + disk_err="true" + } + [ -n "$disk_err" ] && echo -e -n "${str_linefeed}${str_tab}${str_title_start}硬盘存在错误,请及时备份数据!!!${str_title_end}" >> "$output_dir/get_disk" + fi + done +} + + +# 查询 IP 归属地 +function get_ip_attribution(){ + ip="$1" + [ -f ${dir}ipAddress ] && ( cat ${dir}ipAddress|grep -q -w -i "$ip" ) && echo "本地网络" && return + ip_attribution_urls=$(cat /usr/share/wechatpush/api/ip_attribution.list) + while IFS= read -r ip_attribution_command; do + login_ip_attribution=$(eval "$ip_attribution_command" 2>/dev/null) + [ "$login_ip_attribution" == "null" ] && unset login_ip_attribution + [ -n "$login_ip_attribution" ] && break + done <<< "$ip_attribution_urls" + echo "$login_ip_attribution" +} + +# 登录提醒通知 +function login_send(){ + [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv4" + [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] && init_ip_black "ipv6" + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv4" + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] && init_ip_white "ipv6" + tmp_ip_list=`echo "$login_ip_white_list"|grep -v "^$"|sort -u` + while IFS= read -r tmp_ip; do + [ -n "$tmp_ip" ] && add_ip_white "$tmp_ip" "0" + done <<< "$tmp_ip_list" + [ -z "$web_logged" ] && [ -z "$ssh_logged" ] && [ -z "$web_login_failed" ] && [ -z "$ssh_login_failed" ] && return + set_ip_black + sys_log=$(logread notice) + + # Web 登录提醒 + [ -f ${dir}web_login ] && for login_ip in `cat ${dir}web_login | sort -u`; do + [ -z "$login_ip" ] && continue + local login_time=$(echo "$sys_log" | grep -w ${login_ip} | awk '{print $4}' | tail -n 1) + local login_mode=$(echo "$sys_log" | grep -w ${login_ip} | awk '{print $13}' | tail -n 1) + unset log_only content_attribution content_mode + echo "$login_ip_white_list" | grep -w -q "$login_ip" && log_only="1" && [ -n "$login_log_enable" ] && continue + if [ -z "$log_only" ] && [ ! -z "$login_disturb" ] && [ "$login_disturb" -eq "2" ]; then + [ -f "$logfile" ] && login_log=$(grep -w "$login_ip" "$logfile" | grep -v "\【info\】" | tail -n 1) + [ ! -z "$login_log" ] && log_timestamp=$(date -d "$(echo "$login_log" | awk '{print $1, $2}')" +%s) || log_timestamp=0 + [ $(($(date +%s) - log_timestamp)) -lt $login_notification_delay ] && log_only="1" && [ -n "$login_log_enable" ] && continue + fi + [ -n "$log_only" ] && echo "`date "+%Y-%m-%d"` ${login_time} 【info】设备 ${login_ip} 通过 Web ${login_mode} 登录了路由器 " >> ${logfile} && continue + + login_ip_attribution=$(get_ip_attribution ${login_ip}) + [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}IP 归属地: ${str_space}${str_space}${str_space}${str_space}${login_ip_attribution}" + [ -n "$login_mode" ] && content_mode="${str_linefeed}${str_tab}登录方式: ${str_space}${str_space}${str_space}${str_space}${login_mode}" + if { [ -z "$login_disturb" ] || [ "$login_disturb" -ne "1" ]; }; then + if [ -z "$title" ]; then + title="${login_ip} 通过 Web 登录了路由器" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + elif ( echo "$title"|grep -q "登录了路由器" ); then + title="${login_ip} ${title}" + content="${content}${str_splitline}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + else + title="${login_ip} 通过 Web 登录了路由器" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + fi + fi + echo "`date "+%Y-%m-%d"` ${login_time} ${disturb_text}设备 ${login_ip} (${login_ip_attribution}) 通过 Web ${login_mode} 登录了路由器 " >> ${logfile} + done + rm -f ${dir}web_login >/dev/null 2>&1 + unset login_ip login_time login_mode + # SSH 登录提醒 + [ -f ${dir}ssh_login ] && for login_ip in `cat ${dir}ssh_login | sort -u`; do + [ -z "$login_ip" ] && continue + local login_time=$(echo "$sys_log" | grep -w ${login_ip} | awk '{print $4}' | tail -n 1) + local login_mode=$(echo "$sys_log" | grep -w ${login_ip} | awk '{print $8}' | tail -n 1) + unset log_only content_attribution content_mode + echo "$login_ip_white_list"|grep -w -q "$login_ip" && log_only="1" && [ -n "$login_log_enable" ] && continue + if [ -z "$log_only" ] && [ ! -z "$login_disturb" ] && [ "$login_disturb" -eq "2" ]; then + [ -f "$logfile" ] && login_log=$(grep -w "$login_ip" "$logfile" | grep -v "\【info\】" | tail -n 1) + [ ! -z "$login_log" ] && log_timestamp=$(date -d "$(echo "$login_log" | awk '{print $1, $2}')" +%s) || log_timestamp=0 + [ $(($(date +%s) - log_timestamp)) -lt $login_notification_delay ] && log_only="1" && [ -n "$login_log_enable" ] && continue + fi + [ -n "$log_only" ] && echo "`date "+%Y-%m-%d"` ${login_time} 【info】设备 ${login_ip} 通过 SSH ${login_mode} 登录了路由器 " >> ${logfile} && continue + + login_ip_attribution=$(get_ip_attribution ${login_ip}) + [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}IP 归属地: ${str_space}${str_space}${str_space}${str_space}${login_ip_attribution}" + [ ! -z "$login_mode" ] && content_mode="${str_linefeed}${str_tab}登录方式: ${str_space}${str_space}${str_space}${str_space}${login_mode}" + if { [ -z "$login_disturb" ] || [ "$login_disturb" -ne "1" ]; }; then + if [ -z "$title" ]; then + title="${login_ip} 通过 SSH 登录了路由器" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + elif ( echo "$title"|grep -q "登录了路由器" ); then + title="${login_ip} ${title}" + content="${content}${str_splitline}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + else + title="${login_ip} 通过 SSH 登录了路由器" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}时间:${str_space}${str_space}${str_space}${str_space}${str_space}${login_time}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}${content_mode}" + fi + fi + echo "`date "+%Y-%m-%d"` ${login_time} ${disturb_text}设备 ${login_ip} (${login_ip_attribution}) 通过 SSH ${login_mode} 登录了路由器 " >> ${logfile} + done + rm -f ${dir}ssh_login >/dev/null 2>&1 + unset login_ip login_time login_mode + # Web 非法登录 + [ -f ${dir}web_failed ] && for login_ip in `cat ${dir}web_failed | sort -u`; do + [ -z "$login_ip" ] && continue + login_ip_attribution=$(get_ip_attribution ${login_ip}) + [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}IP 归属地: ${str_space}${str_space}${str_space}${str_space}${login_ip_attribution}" || unset content_attribution + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 ${login_ip} (${login_ip_attribution}) 通过 Web 频繁尝试登录" >> ${logfile} + [ -n "$login_disturb" ] && [ "$login_disturb" -eq "1" ] && continue + if [ -z "$title" ]; then + title="${login_ip} 通过 Web 频繁尝试登录" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + elif ( echo "$title"|grep -q "频繁尝试登录" ); then + title="${login_ip} ${title}" + content="${content}${str_splitline}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + else + title="设备状态变化" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + fi + done + rm -f ${dir}web_failed >/dev/null 2>&1 + unset login_ip + # SSH 非法登录 + [ -f ${dir}ssh_failed ] && for login_ip in `cat ${dir}ssh_failed | sort -u`; do + [ -z "$login_ip" ] && continue + login_ip_attribution=$(get_ip_attribution ${login_ip}) + [ -n "$login_ip_attribution" ] && content_attribution="${str_linefeed}${str_tab}IP 归属地: ${str_space}${str_space}${str_space}${str_space}${login_ip_attribution}" || unset content_attribution + echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】设备 ${login_ip} (${login_ip_attribution}) 通过 SSH 频繁尝试登录" >> ${logfile} + [ -n "$login_disturb" ] && [ "$login_disturb" -eq "1" ] && continue + if [ -z "$title" ]; then + title="${login_ip} 通过 SSH 频繁尝试登录" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + elif ( echo "$title"|grep -q "频繁尝试登录" ); then + title="${login_ip} ${title}" + content="${content}${str_splitline}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + else + title="设备状态变化" + content="${content}${str_splitline}${str_title_start} 登录信息${str_title_end}${str_linefeed}${str_tab}设备 IP: ${str_space}${str_space}${str_space}${str_space}${login_ip}${content_attribution}" + fi + done + rm -f ${dir}ssh_failed >/dev/null 2>&1 + unset login_ip +} + +# 添加白名单,懒得写删除项和信息显示了,纯粹就是懒 +function add_ip_white() { + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] || return + [ -z "$2" ] && timeout=$login_ip_white_timeout || timeout=$2 + # 检查 IP 版本 + ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && local ipset_name="wechatpush_whitelist" + ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && local ipset_name="wechatpush_whitelistv6" + [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】白名单添加失败,IP 格式错误" >> ${logfile} && return + + [ -n "$nftables_version" ] && nft delete element inet fw4 $ipset_name { $1 } >/dev/null 2>&1 + [ -n "$nftables_version" ] && nft add element inet fw4 $ipset_name { $1 expires ${timeout}s } && return #没找到刷新时间的命令,删除再添加 + ipset -exist add $ipset_name $1 timeout $timeout +} + +# 初始化白名单 +function init_ip_white() { + [ -n "$port_knocking_enable" ] && [ "$port_knocking_enable" -eq "1" ] || return + # 设置 IP 版本变量 + if [ $1 == "ipv4" ]; then + ipset_name="wechatpush_whitelist" + ip_version="ip" + elif [ $1 == "ipv6" ]; then + ipset_name="wechatpush_whitelistv6" + ip_version="ip6" + nat_table_cmd="family inet6" + fi + + if [ -n "$nftables_version" ]; then + ! nft list set inet fw4 $ipset_name >/dev/null 2>&1 && nft add set inet fw4 $ipset_name { type ${1}_addr\; flags timeout\; timeout ${login_ip_white_timeout}s\; } + nft -- add chain inet fw4 wechatpush_dstnat { type nat hook prerouting priority -100 \; } + nft add chain inet fw4 wechatpush_srcnat { type nat hook postrouting priority 100 \; } + else + ! ipset list $ipset_name >/dev/null 2>&1 && ipset create $ipset_name hash:ip timeout $login_ip_white_timeout $nat_table_cmd >/dev/null 2>&1 + fi + + # 端口放行 + if [ ! -z "$login_port_white" ]; then + local login_port_white=`echo "$login_port_white"|sed 's/ //g'|sed 's/,/, /g'` 2>/dev/null + if [ -n "$nftables_version" ]; then + local count_accept_rules=`nft list ruleset | grep -c "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\""` + if [ $count_accept_rules -eq 0 ]; then + nft insert rule inet fw4 input tcp dport { $login_port_white } $ip_version saddr @$ipset_name counter accept comment \"\!wechatpush Accept rule\" >/dev/null 2>&1 + elif [ $count_accept_rules -ne 1 ]; then + local i=0 + local handles=`nft --handle list ruleset | grep "\!wechatpush Accept rule" | grep -v "tcp dport.* ${login_port_white}.* $ip_version saddr @${ipset_name} counter packets .* accept comment \"\!wechatpush Accept rule\"" | awk '{print $NF}'` + for handle in $handles; do + [ $i -eq 0 ] && i=1 && continue + nft delete rule $handle + done + fi + else + ${ip_version}tables -C INPUT -m set --match-set $ipset_name src -p tcp -m multiport --dport $login_port_white -j ACCEPT >/dev/null 2>&1 || ${ip_version}tables -I INPUT -m set --match-set $ipset_name src -p tcp -m multiport --dport $login_port_white -j ACCEPT >/dev/null 2>&1 + fi + fi + unset handle + # 端口转发 + while IFS= read -r port_forward; do + port_forward=`echo "$port_forward"|sed 's/,/ /g'` 2>/dev/null + [ `echo $port_forward| awk -F" " '{print NF}'` -ne "4" ] && continue + local src_ip=`echo ${port_forward}|awk '{print $1}'` + local src_port=`echo ${port_forward}|awk '{print $2}'` + local dst_ip=`echo ${port_forward}|awk '{print $3}'` + local dst_port=`echo ${port_forward}|awk '{print $4}'` + if [ -n "$nftables_version" ]; then + ! nft list ruleset|grep "$ip_version saddr @${ipset_name} tcp dport $src_port counter .* dnat $ip_version to $dst_ip:$dst_port comment \"\!wechatpush DNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_dstnat meta nfproto $1 $ip_version saddr @${ipset_name} tcp dport $src_port counter dnat to "$dst_ip:$dst_port" comment \"\!wechatpush DNAT rule\" >/dev/null 2>&1 + ! nft list ruleset|grep "$ip_version daddr $dst_ip tcp dport $dst_port counter .* snat $ip_version to $src_ip comment \"\!wechatpush SNAT rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 wechatpush_srcnat $ip_version daddr $dst_ip tcp dport $dst_port counter snat to $src_ip comment \"\!wechatpush SNAT rule\" >/dev/null 2>&1 + else + ${ip_version}tables -t nat -C PREROUTING -m set --match-set $ipset_name src -p tcp --dport $src_port -j DNAT --to-destination "$dst_ip:$dst_port" >/dev/null 2>&1 || ${ip_version}tables -t nat -I PREROUTING -m set --match-set $ipset_name src -p tcp --dport $src_port -j DNAT --to-destination "$dst_ip:$dst_port" >/dev/null 2>&1 + ${ip_version}tables -t nat -C POSTROUTING -m set --match-set $ipset_name src -p tcp -d $dst_ip --dport $dst_port -j SNAT --to-source $src_ip >/dev/null 2>&1 || ${ip_version}tables -t nat -I POSTROUTING -m set --match-set $ipset_name src -p tcp -d $dst_ip --dport $dst_port -j SNAT --to-source $src_ip >/dev/null 2>&1 + fi + done <<< "$login_port_forward_list" + unset port_forward +} + +# 初始化黑名单规则 +function init_ip_black(){ + [ -n "$login_web_black" ] && [ "$login_web_black" -eq "1" ] || return + # 设置 IP 版本变量 + if [ $1 == "ipv4" ]; then + ipset_name="wechatpush_blacklist" + ip_version="ip" + elif [ $1 == "ipv6" ]; then + ipset_name="wechatpush_blacklistv6" + ip_version="ip6" + nat_table_cmd="family inet6" + fi + + if [ -n "$nftables_version" ]; then + ! nft list set inet fw4 ${ipset_name} >/dev/null 2>&1 && nft add set inet fw4 ${ipset_name} { type ${1}_addr\; flags timeout\; timeout ${login_ip_black_timeout}s\; } + ! nft list ruleset|grep "$ip_version saddr @${ipset_name} counter .* comment \"\!wechatpush Drop rule\"" >/dev/null 2>&1 && nft insert rule inet fw4 input $ip_version saddr @${ipset_name} counter drop comment \"\!wechatpush Drop rule\" >/dev/null 2>&1 + else + ipset list $ipset_name >/dev/null 2>&1 || ipset create ${ipset_name} hash:ip timeout ${login_ip_black_timeout} ${nat_table_cmd} >/dev/null 2>&1 + ${ip_version}tables -C INPUT -m set --match-set ${ipset_name} src -j DROP >/dev/null 2>&1 || ${ip_version}tables -I INPUT -m set --match-set ${ipset_name} src -j DROP >/dev/null 2>&1 + fi +} + +# 添加黑名单 +function add_ip_black(){ + [ ! "$1" ] && return + echo "$login_ip_white_list"|grep -w -q "$1" && return + # 检查 IP 版本 + ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && ipset_name="wechatpush_blacklist" + ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && ipset_name="wechatpush_blacklistv6" + [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】黑名单添加失败,IP 格式错误" >> ${logfile} && return + + ! cat ${ip_blacklist_path}|grep -q -w -i ${1} && echo "$1 timeout ${login_ip_black_timeout}" >> ${ip_blacklist_path} + + [ -n "$nftables_version" ] && nft add element inet fw4 ${ipset_name} { $1 expires ${login_ip_black_timeout}s } >/dev/null 2>&1 + [ -n "$nftables_version" ] && return + + ipset -exist add $ipset_name ${1} timeout ${login_ip_black_timeout} +} + +# 移出黑名单 +function del_ip_black(){ + [ ! "$1" ] && return + sed -i "/^${1}/d" ${ip_blacklist_path} + + # 检查 IP 版本 + ( echo "$1"|grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$' ) && ipset_name="wechatpush_blacklist" + ( echo "$1"|grep -Eq '^([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}$' ) && ipset_name="wechatpush_blacklistv6" + [ -z "$ipset_name" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】黑名单删除失败,IP 格式错误" >> ${logfile} && return + + [ -n "$nftables_version" ] && nft delete element inet fw4 ${ipset_name} { $1 } >/dev/null 2>&1 + [ -n "$nftables_version" ] && return + ipset list ${ipset_name} >/dev/null 2>&1 && ipset -! del ${ipset_name} ${1} +} + +# 设置防火墙列表 +function set_ip_black(){ + # 检查换行,避免出错 + [ `tail -n1 "${ip_blacklist_path}" | wc -l` -eq "0" ] && echo -e >> ${ip_blacklist_path} + + # 从 ip_blacklist 文件逐行添加黑名单,add_ip_black() 处验证是否重复 + for ip_black in `cat ${ip_blacklist_path}|awk '{print $1}'`; do + add_ip_black ${ip_black} + done + # 当 ip_blacklist 文件清除 IP 时,从集合中清除 IP + [ -n "$nftables_version" ] && fw_info_blacklist=$(nft list set inet fw4 wechatpush_blacklist | tr -d '\n' | grep -oE 'elements = \{[^}]*\}' | grep -oE '[^{}]+ expires [^,}]+[,\}]' | tr ',}' '\n' | tr -s ' ' | sed -e 's/^[[:space:]]*//') + [ -n "$nftables_version" ] && fw_info_blacklistv6=$(nft list set inet fw4 wechatpush_blacklistv6 | tr -d '\n' | grep -oE 'elements = \{[^}]*\}' | grep -oE '[^{}]+ expires [^,}]+[,\}]' | tr ',}' '\n' | tr -s ' ' | sed -e 's/^[[:space:]]*//') + [ -z "$nftables_version" ] && fw_info_blacklist=$(ipset list wechatpush_blacklist | grep "timeout" 2>/dev/null) + [ -z "$nftables_version" ] && fw_info_blacklistv6=$(ipset list wechatpush_blacklistv6 | grep "timeout" 2>/dev/null) + + fw_info_blacklist="${fw_info_blacklist}\n${fw_info_blacklistv6}" + while IFS= read -r ip_black_info; do + ip_black=$(echo "$ip_black_info"|grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}") + [ -z "$ip_black" ] && ip_black=$(echo "$ip_black_info"|grep -Eo "([\da-fA-F0-9]{1,4}(:{1,2})){1,15}[\da-fA-F0-9]{1,4}") + [ -z "$ip_black" ] && continue + cat ${ip_blacklist_path}|grep -q -w -i ${ip_black} && sed -i "/^${ip_black}/d" ${ip_blacklist_path} && echo ${ip_black_info} >> ${ip_blacklist_path} || del_ip_black ${ip_black} + done <<< "$fw_info_blacklist" +} + +# 发送定时数据 +function send(){ + echo "`date "+%Y-%m-%d %H:%M:%S"` 【定时数据】创建定时任务" >> ${logfile} + disturb;local send_disturb=$? + get_config "send_title" "send_notification" + + cpuload=`getcpu` + service_status=$(ubus call service list '{"name": "wechatpush"}' | grep -o '"running": true') + [ -z "$1" ] && ( echo "$send_notification"|grep -q "client_list" ) && > ${dir}send_enable.lock && [ -z "$service_status" ] && first & + if [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_status" ); then + cat /proc/loadavg|awk '{print $1" "$2" "$3}' > "$output_dir/systemload" & + free -m|sed -n '2p'|awk '{printf "%.2f%%\n",($3/$2)*100}' > "$output_dir/ramload" & + curl -o /dev/null --connect-timeout 5 -s -w %{http_code} www.google.com > "$output_dir/Qwai" & + cat /proc/uptime|awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("运行时间:%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}' > "$output_dir/systemstatustime" & + systeminfo_enable="1" + fi + [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_temp" ) && soc_temp > "$output_dir/cputemp" & + + if [ -z "$1" ] && ( echo "$send_notification"|grep -q "wan_info" ); then + getip wanipv4 > "$output_dir/send_wanIP" & + getip hostipv4|jq -r '.IP' > "$output_dir/send_hostIP" & + waninfo_enable="1" + if [ "$get_ipv6_mode" -ne "0" ]; then + getip wanipv6 > "$output_dir/send_wanIPv6" & + getip hostipv6|jq -r '.IP' > "$output_dir/send_hostIPv6" & + ipv6_enable="1" + fi + getinterfaceuptime > "$output_dir/interfaceuptime" & + fi + + #[ -z "$1" ] && ( echo "$send_notification"|grep -q "disk_info" ) && get_disk & + [ -z "$1" ] && get_disk & + + [ -z "$send_title" ] && send_title="路由状态:" + [ ! -z "$1" ] && send_title="发送测试:" && send_content="${str_splitline}${str_title_start}内容1${str_title_end}${str_linefeed}${str_tab}设备1${str_linefeed}${str_tab}设备2${str_splitline}${str_title_start}内容2${str_title_end}${str_linefeed}${str_tab}设备3${str_linefeed}${str_tab}设备4" + + wait_and_cat + if [ -n "$systeminfo_enable" ]; then + [[ $Qwai -eq 200 || $Qwai -eq 301 || $Qwai -eq 302 ]] && Qwai_status="已连通!" || Qwai_status="已断开!" + send_content="${send_content}${str_splitline}${str_title_start} 系统运行状态${str_title_end}" + send_content="${send_content}${str_linefeed}${str_tab}平均负载:${systemload}" + send_content="${send_content}${str_linefeed}${str_tab}CPU占用:${cpuload}" + send_content="${send_content}${str_linefeed}${str_tab}内存占用:${ramload}" + send_content="${send_content}${str_linefeed}${str_tab}全球互联:${Qwai_status}" + send_content="${send_content}${str_linefeed}${str_tab}${systemstatustime}" + fi + + if [ -z "$1" ] && ( echo "$send_notification"|grep -q "router_temp" ); then + [ ! -z "$cputemp" ] && send_content="${send_content}${str_splitline}${str_title_start} 设备温度${str_title_end}${str_linefeed}${str_tab}CPU:${cputemp}℃" + [ -z "$cputemp" ] && send_content="${send_content}${str_splitline}${str_title_start} 设备温度${str_title_end}${str_linefeed}${str_tab}无法获取设备温度" + fi + + #if [ -z "$1" ] && ( echo "$send_notification"|grep -q "disk_info" ); then + if [ -z "$1" ]; then + send_content="${send_content}${get_disk}" + fi + + if [ -n "$waninfo_enable" ]; then + send_content="${send_content}${str_splitline}${str_title_start} WAN 口信息${str_title_end}" + if [ "$send_wanIP" == "$send_hostIP" ]; then + send_content="${send_content}${str_linefeed}${str_tab}IPv4: ${send_wanIP}" + elif [ "$get_ipv4_mode" -eq "1" ]; then + send_content="${send_content}${str_linefeed}${str_tab}接口 IPv4: ${send_wanIP}" + [ -n "$send_hostIP" ] && send_content="${send_content}${str_linefeed}${str_tab}外网 IPv4: ${send_hostIP}" + elif [ "$get_ipv4_mode" -eq "2" ]; then + [ -n "$send_wanIP" ] && send_content="${send_content}${str_linefeed}${str_tab}接口 IPv4: ${send_wanIP}" + send_content="${send_content}${str_linefeed}${str_tab}外网 IPv4: ${send_hostIP}" + fi + if [ -n "$ipv6_enable" ]; then + if [ "$send_wanIPv6" == "$send_hostIPv6" ]; then + send_content="${send_content}${str_linefeed}${str_tab}IPv6: ${send_wanIPv6}" + elif [ "$get_ipv6_mode" -eq "1" ]; then + send_content="${send_content}${str_linefeed}${str_tab}接口 IPv6: ${send_wanIPv6}" + [ -n "$send_hostIPv6" ] && send_content="${send_content}${str_linefeed}${str_tab}外网 IPv6: ${send_hostIPv6}" + elif [ "$get_ipv6_mode" -eq "2" ]; then + [ -n "$send_wanIPv6" ] && send_content="${send_content}${str_linefeed}${str_tab}接口 IPv6: ${send_wanIPv6}" + send_content="${send_content}${str_linefeed}${str_tab}外网 IPv6: ${send_hostIPv6}" + fi + fi + interfaceuptime=`getinterfaceuptime` + [ ! -z "$interfaceuptime" ] && wanstatustime=$(printf "在线时间:%d天%d时%d分%d秒" $((interfaceuptime / 86400)) $(((interfaceuptime % 86400) / 3600)) $(((interfaceuptime % 3600) / 60)) $((interfaceuptime % 60))) + send_content="${send_content}${str_linefeed}${str_tab}${wanstatustime}" + fi + + if [ -z "$1" ] && ( echo "$send_notification"|grep -q "client_list" ); then + wait + awk '{ lines[i++] = $0 } END { for (j = i-1; j >= 0; j--) print lines[j] }' "${dir}ipAddress" > "${dir}tmp_sort_file" + logrow=$(awk 'END {print NR}' ${dir}ipAddress) + send_content="${send_content}${str_splitline}${str_title_start} 现有在线设备 ${logrow} 台${str_title_end}" + [ "$logrow" -eq "0" ] && send_content="${send_content}${str_splitline}${str_title_start} 当前无在线设备${str_title_end}" + time_now=$(date +%s) + while read -r tmp_ip tmp_mac tmp_name time_up _; do + tmp_name=${tmp_name//_/ } + tmp_name=$(cut_str "$tmp_name" "20") + time_online=$(time_for_humans $((time_now - time_up))) + ip_total=$(usage get "$tmp_mac") + [ -n "$ip_total" ] && ip_total="总计流量:${ip_total} " + send_content="${send_content}${str_linefeed}【${tmp_name}】 ${ip_total}${str_linefeed}${str_tab}${tmp_ip} 在线 ${time_online}" + done < "${dir}tmp_sort_file" + fi + + [ ! -z "$device_name" ] && send_title="【$device_name】${send_title}" + [ -z "$send_content" ] && send_content="${str_splitline}${str_title_start} 我遇到了一个难题${str_title_end}${str_linefeed}${str_tab}定时发送选项错误,你没有选择需要发送的项目,该怎 么办呢${str_splitline}" + [ "$send_disturb" -eq "0" ] && diy_send "${send_title}" "${send_content}" "${jsonpath}" "$1" >/dev/null 2>&1 + RETVAL=$? + [ $RETVAL -eq 1 ] && [ "$send_disturb" -eq "0" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】定时推送失败,请检查网络或设置信息" >> ${logfile} || echo "`date "+%Y-%m-%d %H:%M:%S"` ${disturb_text}定时推送任务完成" >> ${logfile} + deltemp + return $RETVAL +} + +# 初始化 +if [ "$1" ] ;then + [ $1 == "soc" ] && get_config "soc_code" "server_host" "server_port" + [ $1 == "soc" ] && soc_temp && exit $? + read_config + [ $1 == "send" ] && send && exit $? + [ $1 == "test" ] && send test && exit $? + [ $1 == "t1" ] && thread_num=1 || exit +else + read_config +fi + +# 限制并发进程 +[ -z "$thread_num" ] || [ "$thread_num" -eq "0" ] && thread_num=5 +[ -e ${dir}fd1 ] || mkfifo ${dir}fd1 +exec 5<>${dir}fd1 +rm -f ${dir}fd1 >/dev/null 2>&1 +for i in `seq 1 $thread_num`; do + echo >&5 +done +unset i + +process_login() { + local login_ip=$1 + local -n login_counts=$2 + + if [ -z "${login_counts["$login_ip"]}" ]; then + login_counts["$login_ip"]=0 + fi + login_counts["$login_ip"]=$((login_counts["$login_ip"]+1)) + local count=${login_counts["$login_ip"]} + login_log=$(logread notice | grep -w -i "$login_ip" | tail -n 1) + + if [[ $count -eq $login_max_num && ( "$2" == "web_failed_counts" || "$2" == "ssh_failed_counts" ) ]]; then + add_ip_black ${login_ip} + unset login_counts["$login_ip"] + [ "$2" == "web_failed_counts" ] && echo "${login_ip}" >> ${dir}web_failed + [ "$2" == "ssh_failed_counts" ] && echo "${login_ip}" >> ${dir}ssh_failed + fi + + if [[ "$2" == "web_login_counts" || "$2" == "ssh_login_counts" ]]; then + add_ip_white ${login_ip} + unset web_failed_counts["$login_ip"] + unset ssh_failed_counts["$login_ip"] + unset login_counts["$login_ip"] + [ "$2" == "web_login_counts" ] && echo "${login_ip}" >> ${dir}web_login + [ "$2" == "ssh_login_counts" ] && echo "${login_ip}" >> ${dir}ssh_login + [ "${#login_counts[@]}" -gt "100" ] && login_counts=("${login_counts[@]: -100}") + fi +} + +# 监听登录事件 +if [ -n "$web_logged" ] || [ -n "$ssh_logged" ] || [ -n "$web_login_failed" ] || [ -n "$ssh_login_failed" ]; then + declare -A web_login_counts + declare -A ssh_login_counts + declare -A web_failed_counts + declare -A ssh_failed_counts + [ -f ${dir}child_pid ] && child_pid=$(cat ${dir}child_pid) + [ -n "$child_pid" ] && kill $child_pid >/dev/null 2>&1 + ( + logread -f -p notice | while IFS= read -r line; do + [ -n "$web_logged" ] && { + web_login_ip=$(echo "$line" | grep -i "accepted login" | awk '{print $NF}') + [ -n "$web_login_ip" ] && process_login "$web_login_ip" web_login_counts + } + + [ -n "$ssh_logged" ] && { + ssh_login_ip=$(echo "$line" | grep -i "Password auth succeeded\|Pubkey auth succeeded" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') + [ -n "$ssh_login_ip" ] && process_login "$ssh_login_ip" ssh_login_counts + } + + [ -n "$web_login_failed" ] && { + web_failed_ip=$(echo "$line" | grep -i "failed login"|awk '{print $NF}') + [ -n "$web_failed_ip" ] && process_login "$web_failed_ip" web_failed_counts + } + + [ -n "$ssh_login_failed" ] && { + ssh_failed_ip=$(echo "$line" | grep -i "Bad password attempt\|Login attempt for nonexistent user from" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') + [ -z "$ssh_failed_ip" ] && ssh_failed_num=$(echo "$line" | sed -n 's/.*authpriv\.warn dropbear\[\([0-9]\+\)\]: Login attempt for nonexistent user/\1/p') && [ -n "$ssh_failed_num" ] && ssh_failed_ip=$(logread notice | grep "authpriv\.info dropbear\[${failed_user_id}\].*Child connection from" | awk '{print $NF}' | sed -nr 's#^(.*):.[0-9]{1,5}#\1#gp' | sed -e 's/%.*//') + [ -n "$ssh_failed_ip" ] && process_login "$ssh_failed_ip" ssh_failed_counts + } + done + ) & + # 分离子shell,避免影响 wait + child_pid=$! + sleep 1 + disown "$child_pid" + echo $child_pid > ${dir}child_pid +fi + +# 载入在线设备 +init;[ $? -eq 1 ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【!!!】读取设置出错,请检查设置项 " >> ${logfile} && exit +echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】载入在线设备..." >> ${logfile} +> ${dir}send_enable.lock && first && deltemp +echo "`date "+%Y-%m-%d %H:%M:%S"` 【初始化】初始化完成" >> ${logfile} + +# 循环 +while [ "$enable" -eq "1" ]; do + deltemp + usage update + disturb;disturb=$? + device_aliases=`cat /usr/share/wechatpush/api/device_aliases.list` 2>/dev/null + + [ -f ${dir}ipAddress ] && ipAddress_logrow=$(grep -c "" ${dir}ipAddress) || ipAddress_logrow="0"; + if [ $ipAddress_logrow -ne "0" ]; then + online_list=`cat ${dir}ipAddress|awk '{print $2}'|grep -v "^$"|sort -u` + for online_mac in $online_list; do + [ ! -z "$online_mac" ] && mac_online_status="`echo "$mark_mac_list"|grep -i $online_mac|grep -v "^$"|sort -u|head -n1`${mac_online_status}" + done + fi + + # 网络状态与 IP 变动 + if [ "$get_ipv4_mode" -ne "0" ] || [ "$get_ipv6_mode" -ne "0" ]; then + rand_geturl + ip_changes + fi + + # 设备列表 + if [ ! -f "${dir}send_enable.lock" ]; then + [ ! -z "$title" ] && echo "$title" > ${dir}title + [ ! -z "$content" ] && echo "$content" > ${dir}content + first + [ -f "${dir}title" ] && title=`cat ${dir}title` && rm -f ${dir}title >/dev/null 2>&1 + [ -f "${dir}content" ] && content=`cat ${dir}content` && rm -f ${dir}content >/dev/null 2>&1 + fi + + # 离线二次验证区推送 + [ ! -f "${dir}send_enable.lock" ] && down_send + + # 当前设备列表 + [ ! -z "$content" ] && [ ! -f "${dir}send_enable.lock" ] && current_device + + # 无人值守任务 + [ ! -f "${dir}send_enable.lock" ] && unattended + + # CPU 检测 + [ ! -f "${dir}send_enable.lock" ] && cpu_load + + # 硬盘检测 + #[ ! -f "${dir}send_enable.lock" ] && get_disk + + # 异常流量检测 + [ ! -f "${dir}send_enable.lock" ] && get_client_usage + + # 登录提醒通知 + [ ! -f "${dir}send_enable.lock" ] && login_send + + # 推送 + if [ ! -f "${dir}send_enable.lock" ] && [ ! -z "$title" ] && [ ! -z "$content" ]; then + [ ! -z "$device_name" ] && title="【$device_name】$title" + ( echo "$lite_enable"|grep -q "content" ) && content="$title" + [ "$disturb" -eq "0" ] && diy_send "${title}" "${content}" "${jsonpath}" >/dev/null 2>&1 + fi + + # 等待定时任务推送完成 + while [ -f "${dir}send_enable.lock" ]; do + sleep $sleeptime + done + + sleep $sleeptime +done \ No newline at end of file