mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-06-17 08:55:29 +08:00
luci-app-ssr-plus: optimize subscribe script and support ssd subscribe (#2726)
* luci-app-ssr-plus: optimize subscribe script and support ssd subscribe * luci-app-ssr-plus: optimize ping test and added socket test * luci-app-ssr-plus: fix script path * luci-app-ssr-plus: optimize script * luci-app-ssr-plus: optimize script and remove /tmp/ssrpro.log * luci-app-ssr-plus: adjust log function * luci-app-ssr-plus: fix index * luci-app-ssr-plus: adjust content
This commit is contained in:
parent
4da966dae3
commit
9fcb30d870
@ -9,26 +9,28 @@ function index()
|
||||
end
|
||||
|
||||
|
||||
entry({"admin", "services", "shadowsocksr"},alias("admin", "services", "shadowsocksr", "client"),_("ShadowSocksR Plus+"), 10).dependent = true
|
||||
entry({"admin", "services", "shadowsocksr"}, alias("admin", "services", "shadowsocksr", "client"),_("ShadowSocksR Plus+"), 10).dependent = true
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "client"},cbi("shadowsocksr/client"),_("SSR Client"), 10).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "client"}, cbi("shadowsocksr/client"),_("SSR Client"), 10).leaf = true
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "servers"}, arcombine(cbi("shadowsocksr/servers", {autoapply=true}), cbi("shadowsocksr/client-config")),_("Severs Nodes"), 20).leaf = true
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "control"},cbi("shadowsocksr/control"),_("Access Control"), 30).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "control"},cbi("shadowsocksr/control"), _("Access Control"), 30).leaf = true
|
||||
|
||||
-- entry({"admin", "services", "shadowsocksr", "list"},form("shadowsocksr/list"),_("GFW List"), 40).leaf = true
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "advanced"},cbi("shadowsocksr/advanced"),_("Advanced Settings"), 50).leaf = true
|
||||
|
||||
if nixio.fs.access("/usr/bin/ssr-server") then
|
||||
entry({"admin", "services", "shadowsocksr", "server"},arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")),_("SSR Server"), 60).leaf = true
|
||||
entry({"admin", "services", "shadowsocksr", "server"},arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")),_("SSR Server"), 60).leaf = true
|
||||
end
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "status"},form("shadowsocksr/status"),_("Status"), 70).leaf = true
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "check"}, call("check_status"))
|
||||
entry({"admin", "services", "shadowsocksr", "refresh"}, call("refresh_data"))
|
||||
entry({"admin", "services", "shadowsocksr", "subscribe"}, call("subscribe"))
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "checkport"}, call("check_port"))
|
||||
|
||||
entry({"admin", "services", "shadowsocksr", "log"},form("shadowsocksr/log"),_("Log"), 80).leaf = true
|
||||
@ -39,31 +41,49 @@ function index()
|
||||
|
||||
end
|
||||
|
||||
function subscribe()
|
||||
luci.sys.call("/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >> /tmp/ssrplus.log 2>&1")
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ ret = 1 })
|
||||
end
|
||||
|
||||
function act_status()
|
||||
local e={}
|
||||
e.running=luci.sys.call("busybox ps -w | grep ssr-retcp | grep -v grep >/dev/null")==0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
local e={}
|
||||
e.running=luci.sys.call("busybox ps -w | grep ssr-retcp | grep -v grep >/dev/null")==0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function act_ping()
|
||||
local e={}
|
||||
e.index=luci.http.formvalue("index")
|
||||
e.ping=luci.sys.exec("ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'"%luci.http.formvalue("domain"))
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
local e = {}
|
||||
local domain = luci.http.formvalue("domain")
|
||||
local port = luci.http.formvalue("port")
|
||||
e.index = luci.http.formvalue("index")
|
||||
e.ping = luci.sys.exec("ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'" % domain)
|
||||
|
||||
local iret = luci.sys.call(" ipset add ss_spec_wan_ac " .. domain .. " 2>/dev/null")
|
||||
local socket = nixio.socket("inet", "stream")
|
||||
socket:setopt("socket", "rcvtimeo", 3)
|
||||
socket:setopt("socket", "sndtimeo", 3)
|
||||
e.socket = socket:connect(domain, port)
|
||||
socket:close()
|
||||
if (iret == 0) then
|
||||
luci.sys.call(" ipset del ss_spec_wan_ac " .. domain)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function check_status()
|
||||
local set ="/usr/bin/ssr-check www." .. luci.http.formvalue("set") .. ".com 80 3 1"
|
||||
sret=luci.sys.call(set)
|
||||
if sret== 0 then
|
||||
retstring ="0"
|
||||
else
|
||||
retstring ="1"
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ ret=retstring })
|
||||
local set = "/usr/bin/ssr-check www." .. luci.http.formvalue("set") .. ".com 80 3 1"
|
||||
sret = luci.sys.call(set)
|
||||
if sret == 0 then
|
||||
retstring ="0"
|
||||
else
|
||||
retstring ="1"
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ ret=retstring })
|
||||
end
|
||||
|
||||
function refresh_data()
|
||||
@ -72,82 +92,82 @@ local icount =0
|
||||
|
||||
if set == "gfw_data" then
|
||||
if nixio.fs.access("/usr/bin/wget-ssl") then
|
||||
refresh_cmd="wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64"
|
||||
refresh_cmd="wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64"
|
||||
else
|
||||
refresh_cmd="wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64"
|
||||
refresh_cmd="wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64"
|
||||
end
|
||||
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
|
||||
if sret== 0 then
|
||||
luci.sys.call("/usr/bin/ssr-gfw")
|
||||
icount = luci.sys.exec("cat /tmp/gfwnew.txt | wc -l")
|
||||
if tonumber(icount)>1000 then
|
||||
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf")
|
||||
retstring=tostring(math.ceil(tonumber(icount)/2))
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
else
|
||||
retstring ="-1"
|
||||
end
|
||||
luci.sys.exec("rm -f /tmp/gfwnew.txt ")
|
||||
luci.sys.call("/usr/bin/ssr-gfw")
|
||||
icount = luci.sys.exec("cat /tmp/gfwnew.txt | wc -l")
|
||||
if tonumber(icount)>1000 then
|
||||
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/gfw_list.conf | wc -l")
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/gfwnew.txt /etc/dnsmasq.ssr/gfw_list.conf")
|
||||
retstring=tostring(math.ceil(tonumber(icount)/2))
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
else
|
||||
retstring ="-1"
|
||||
end
|
||||
luci.sys.exec("rm -f /tmp/gfwnew.txt ")
|
||||
else
|
||||
retstring ="-1"
|
||||
retstring ="-1"
|
||||
end
|
||||
elseif set == "ip_data" then
|
||||
refresh_cmd="wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' 2>/dev/null| awk -F\\| '/CN\\|ipv4/ { printf(\"%s/%d\\n\", $4, 32-log($5)/log(2)) }' > /tmp/china_ssr.txt"
|
||||
sret=luci.sys.call(refresh_cmd)
|
||||
icount = luci.sys.exec("cat /tmp/china_ssr.txt | wc -l")
|
||||
if sret== 0 and tonumber(icount)>1000 then
|
||||
oldcount=luci.sys.exec("cat /etc/china_ssr.txt | wc -l")
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/china_ssr.txt /etc/china_ssr.txt")
|
||||
retstring=tostring(tonumber(icount))
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
oldcount=luci.sys.exec("cat /etc/china_ssr.txt | wc -l")
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/china_ssr.txt /etc/china_ssr.txt")
|
||||
retstring=tostring(tonumber(icount))
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
|
||||
else
|
||||
retstring ="-1"
|
||||
retstring ="-1"
|
||||
end
|
||||
luci.sys.exec("rm -f /tmp/china_ssr.txt ")
|
||||
else
|
||||
local need_process = 0
|
||||
if nixio.fs.access("/usr/bin/wget-ssl") then
|
||||
refresh_cmd="wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf"
|
||||
need_process = 1
|
||||
local need_process = 0
|
||||
if nixio.fs.access("/usr/bin/wget-ssl") then
|
||||
refresh_cmd="wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf"
|
||||
need_process = 1
|
||||
else
|
||||
refresh_cmd="wget -O /tmp/ad.conf http://iytc.net/tools/ad.conf"
|
||||
refresh_cmd="wget -O /tmp/ad.conf http://iytc.net/tools/ad.conf"
|
||||
end
|
||||
sret=luci.sys.call(refresh_cmd .. " 2>/dev/null")
|
||||
if sret== 0 then
|
||||
if need_process == 1 then
|
||||
luci.sys.call("/usr/bin/ssr-ad")
|
||||
end
|
||||
icount = luci.sys.exec("cat /tmp/ad.conf | wc -l")
|
||||
if tonumber(icount)>1000 then
|
||||
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
|
||||
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l")
|
||||
else
|
||||
oldcount=0
|
||||
end
|
||||
if need_process == 1 then
|
||||
luci.sys.call("/usr/bin/ssr-ad")
|
||||
end
|
||||
icount = luci.sys.exec("cat /tmp/ad.conf | wc -l")
|
||||
if tonumber(icount)>1000 then
|
||||
if nixio.fs.access("/etc/dnsmasq.ssr/ad.conf") then
|
||||
oldcount=luci.sys.exec("cat /etc/dnsmasq.ssr/ad.conf | wc -l")
|
||||
else
|
||||
oldcount=0
|
||||
end
|
||||
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/ad.conf /etc/dnsmasq.ssr/ad.conf")
|
||||
retstring=tostring(math.ceil(tonumber(icount)))
|
||||
if oldcount==0 then
|
||||
luci.sys.call("/etc/init.d/dnsmasq restart")
|
||||
end
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
else
|
||||
retstring ="-1"
|
||||
end
|
||||
luci.sys.exec("rm -f /tmp/ad.conf ")
|
||||
if tonumber(icount) ~= tonumber(oldcount) then
|
||||
luci.sys.exec("cp -f /tmp/ad.conf /etc/dnsmasq.ssr/ad.conf")
|
||||
retstring=tostring(math.ceil(tonumber(icount)))
|
||||
if oldcount==0 then
|
||||
luci.sys.call("/etc/init.d/dnsmasq restart")
|
||||
end
|
||||
else
|
||||
retstring ="0"
|
||||
end
|
||||
else
|
||||
retstring ="-1"
|
||||
end
|
||||
luci.sys.exec("rm -f /tmp/ad.conf ")
|
||||
else
|
||||
retstring ="-1"
|
||||
retstring ="-1"
|
||||
end
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
|
@ -1,6 +1,3 @@
|
||||
local fs = require "nixio.fs"
|
||||
local conffile = "/tmp/ssrpro.log"
|
||||
|
||||
f = SimpleForm("logview")
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
@ -8,8 +5,12 @@ t = f:field(TextValue, "conf")
|
||||
t.rmempty = true
|
||||
t.rows = 20
|
||||
function t.cfgvalue()
|
||||
luci.sys.exec("[ -f /tmp/ssrplus.log ] && sed '1!G;h;$!d' /tmp/ssrplus.log > /tmp/ssrpro.log")
|
||||
return fs.readfile(conffile) or ""
|
||||
local logs = luci.util.execi("cat /tmp/ssrplus.log")
|
||||
local s = ""
|
||||
for line in logs do
|
||||
s = line .. "\n" .. s
|
||||
end
|
||||
return s
|
||||
end
|
||||
t.readonly="readonly"
|
||||
|
||||
|
@ -45,12 +45,18 @@ o = s:option(Flag, "proxy", translate("Through proxy update"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Through proxy update list, Not Recommended ")
|
||||
|
||||
o = s:option(Button,"update",translate("Update All Subscribe Severs"))
|
||||
o.inputstyle = "apply"
|
||||
o.write = function()
|
||||
luci.sys.exec("bash /usr/share/shadowsocksr/subscribe.sh >>/tmp/ssrplus.log 2>&1")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
|
||||
end
|
||||
o = s:option(Button,"subscribe", translate("Update All Subscribe Severs"))
|
||||
o.rawhtml = true
|
||||
o.template = "shadowsocksr/subscribe"
|
||||
|
||||
|
||||
-- o.inputstyle = "apply"
|
||||
-- o.write = function()
|
||||
-- luci.sys.call("lua /root/subscribe.lua >>/tmp/ssrplus.log 2>&1")
|
||||
-- -- luci.sys.call("echo 123 >>/tmp/ssrplus.log 2>&1")
|
||||
-- --luci.sys.exec("bash /usr/share/shadowsocksr/subscribe.sh >>/tmp/ssrplus.log 2>&1")
|
||||
-- luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
|
||||
-- end
|
||||
|
||||
|
||||
o = s:option(Button,"delete",translate("Delete all severs"))
|
||||
@ -113,7 +119,11 @@ function o.cfgvalue(...)
|
||||
return Value.cfgvalue(...) or "0"
|
||||
end
|
||||
|
||||
o = s:option(DummyValue,"server",translate("Ping Latency"))
|
||||
o = s:option(DummyValue, "server_port", translate("Socket Connected"))
|
||||
o.template="shadowsocksr/socket"
|
||||
o.width="10%"
|
||||
|
||||
o = s:option(DummyValue, "server", translate("Ping Latency"))
|
||||
o.template="shadowsocksr/ping"
|
||||
o.width="10%"
|
||||
|
||||
|
@ -9,16 +9,56 @@ local dsp = require "luci.dispatcher"
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var pings = document.getElementsByClassName('pingtime');
|
||||
for(var i = 0; i < pings.length; i++) {
|
||||
XHR.get('<%=dsp.build_url("admin/services/shadowsocksr/ping")%>', {
|
||||
index: i,
|
||||
domain: pings[i].getAttribute("hint")
|
||||
const doms = document.getElementsByClassName('pingtime');
|
||||
const ports = document.getElementsByClassName("socket-connected")
|
||||
// 这也是卡的原罪 CGI 一下子处理不这么多
|
||||
const xhr = (index) => {
|
||||
return new Promise((res) => {
|
||||
const dom = doms[index];
|
||||
const port = ports[index];
|
||||
if (!dom) res()
|
||||
XHR.get('<%=dsp.build_url("admin/services/shadowsocksr/ping")%>', {
|
||||
index,
|
||||
domain: dom.getAttribute("hint"),
|
||||
port: port.getAttribute("hint")
|
||||
},
|
||||
function(x, result) {
|
||||
pings[result.index].innerHTML = (result.ping ? result.ping : "--") + " ms";
|
||||
}
|
||||
);
|
||||
(x, result) => {
|
||||
let col = '#ff0000';
|
||||
if (result.ping) {
|
||||
if (result.ping < 300) col = '#ff3300';
|
||||
if (result.ping < 200) col = '#ff7700';
|
||||
if (result.ping < 100) col = '#249400';
|
||||
}
|
||||
dom.innerHTML = `<font color="${col}">${(result.ping ? result.ping : "--") + " ms"}</font>`
|
||||
if (result.socket) {
|
||||
port.innerHTML = '<font color="#249400">ok</font>'
|
||||
} else {
|
||||
port.innerHTML = '<font color="#ff0000">fail</font>'
|
||||
}
|
||||
res()
|
||||
});
|
||||
})
|
||||
}
|
||||
(async () => {
|
||||
for (let group = 0; group < Math.ceil(doms.length / 5); group++) {
|
||||
await Promise.all([
|
||||
xhr(group * 5 + 0),
|
||||
xhr(group * 5 + 1),
|
||||
xhr(group * 5 + 2),
|
||||
xhr(group * 5 + 3),
|
||||
xhr(group * 5 + 4),
|
||||
])
|
||||
}
|
||||
})()
|
||||
// for(var i = 0; i < pings.length; i++) {
|
||||
// XHR.get('<%=dsp.build_url("admin/services/shadowsocksr/ping")%>', {
|
||||
// index: i,
|
||||
// domain: pings[i].getAttribute("hint")
|
||||
// },
|
||||
// function(x, result) {
|
||||
// pings[result.index].innerHTML = (result.ping ? result.ping : "--") + " ms";
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
//]]>
|
||||
</script>
|
@ -0,0 +1,3 @@
|
||||
<%+cbi/valueheader%>
|
||||
<span class="socket-connected" hint="<%=self:cfgvalue(section)%>">connecting</:wqspan>
|
||||
<%+cbi/valuefooter%>
|
@ -0,0 +1,23 @@
|
||||
<%+cbi/valueheader%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
function subscribe(btn,dataname) {
|
||||
btn.disabled = true;
|
||||
btn.value = '<%:Refresh...%> ';
|
||||
murl=dataname;
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr","subscribe")%>', { set:murl }, function(x,rv) {
|
||||
// 先简单刷新,后期如果重构会考虑下如何组织lua shell JavaScript之间的代码逻辑和各自的调用逻辑
|
||||
window.location.reload()
|
||||
// btn.disabled = false;
|
||||
// btn.value = '<%:Refresh Data %>';
|
||||
});
|
||||
return false;
|
||||
}
|
||||
//]]></script>
|
||||
|
||||
|
||||
<input type="button" class="cbi-button cbi-input-apply" value="<%:Update All Subscribe Severs%> " onclick="return subscribe(this,'<%=self.option%>')" />
|
||||
<!-- <span id="<%=self.option%>-status"><em><%=self.value%></em></span> -->
|
||||
|
||||
<%+cbi/valuefooter%>
|
@ -512,6 +512,9 @@ msgstr "所有端口(默认)"
|
||||
msgid "Only Common Ports"
|
||||
msgstr "仅常用端口(不走P2P流量到代理)"
|
||||
|
||||
msgid "Socket Connected"
|
||||
msgstr "连接测试"
|
||||
|
||||
msgid "Ping Latency"
|
||||
msgstr "Ping延迟"
|
||||
|
||||
|
@ -45,7 +45,7 @@ add_cron()
|
||||
{
|
||||
sed -i '/shadowsocksr/d' $CRON_FILE
|
||||
sed -i '/ssrplus.log/d' $CRON_FILE && echo '0 1 * * * echo "" > /tmp/ssrplus.log' >> $CRON_FILE
|
||||
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/share/shadowsocksr/subscribe.sh" >> $CRON_FILE
|
||||
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/bin/lua /usr/share/shadowsocksr/subscribe.lua" >> $CRON_FILE
|
||||
[ $(uci_get_by_type server_subscribe auto_update 0) -eq 1 ] && echo "0 $(uci_get_by_type server_subscribe auto_update_time) * * * /usr/share/shadowsocksr/update.sh" >> $CRON_FILE
|
||||
crontab $CRON_FILE
|
||||
}
|
||||
|
295
package/lean/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua
Executable file
295
package/lean/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua
Executable file
@ -0,0 +1,295 @@
|
||||
#!/usr/bin/lua
|
||||
------------------------------------------------
|
||||
-- This file is part of the luci-app-ssr-plus subscribe.lua
|
||||
-- @author William Chan <root@williamchan.me>
|
||||
------------------------------------------------
|
||||
require 'nixio'
|
||||
require 'luci.util'
|
||||
require 'luci.jsonc'
|
||||
require 'luci.sys'
|
||||
|
||||
-- these global functions are accessed all the time by the event handler
|
||||
-- so caching them is worth the effort
|
||||
local tinsert = table.insert
|
||||
local ssub, slen, schar, srep, sbyte, sformat, sgsub =
|
||||
string.sub, string.len, string.char, string.rep, string.byte, string.format, string.gsub
|
||||
local cache = {}
|
||||
local nodeResult = setmetatable({}, { __index = cache }) -- update result
|
||||
local name = 'shadowsocksr'
|
||||
local uciType = 'servers'
|
||||
local ucic = luci.model.uci.cursor()
|
||||
local proxy = ucic:get_first(name, 'server_subscribe', 'proxy', '0')
|
||||
local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
|
||||
|
||||
local log = function(...)
|
||||
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({ ... }, " "))
|
||||
end
|
||||
-- 分割字符串
|
||||
local function split(full, sep)
|
||||
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
|
||||
local off, result = 1, {}
|
||||
while true do
|
||||
local nEnd = full:find(sep, off)
|
||||
if not nEnd then
|
||||
local res = ssub(full, off, slen(full))
|
||||
if #res > 0 then -- 过滤掉 \0
|
||||
tinsert(result, res)
|
||||
end
|
||||
break
|
||||
else
|
||||
tinsert(result, ssub(full, off, nEnd - 1))
|
||||
off = nEnd + slen(sep)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
-- urlencode
|
||||
local function get_urlencode(c)
|
||||
return sformat("%%%02X", sbyte(c))
|
||||
end
|
||||
|
||||
function urlEncode(szText)
|
||||
local str = szText:gsub("([^0-9a-zA-Z ])", get_urlencode)
|
||||
str = str:gsub(" ", "+")
|
||||
return str
|
||||
end
|
||||
-- trim
|
||||
local function trim(text)
|
||||
if not text or text == "" then
|
||||
return ""
|
||||
end
|
||||
return (sgsub(text, "^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
-- md5
|
||||
local function md5(content)
|
||||
local stdout = luci.sys.exec('echo \"' .. urlEncode(content) .. '\" | md5sum | cut -d \" \" -f1')
|
||||
-- assert(nixio.errno() == 0)
|
||||
return trim(stdout)
|
||||
end
|
||||
-- base64
|
||||
local function base64Decode(text, safe)
|
||||
if not text then return '' end
|
||||
text = text:gsub("%z", "")
|
||||
if safe then
|
||||
text = text:gsub("_", "/")
|
||||
text = text:gsub("-", "+")
|
||||
local mod4 = #text % 4
|
||||
text = text .. string.sub('====', mod4 + 1)
|
||||
end
|
||||
return nixio.bin.b64decode(text):gsub("%z", "")
|
||||
end
|
||||
-- 处理数据
|
||||
local function processData(szType, content)
|
||||
local result = {
|
||||
auth_enable = '0',
|
||||
switch_enable = '1',
|
||||
type = szType,
|
||||
local_port = 1234,
|
||||
timeout = 60, -- 不太确定 好像是死的
|
||||
fast_open = 0,
|
||||
kcp_enable = 0,
|
||||
kcp_port = 0,
|
||||
kcp_param = '--nocomp'
|
||||
}
|
||||
local hash
|
||||
if type(content) == 'string' then
|
||||
hash = md5(content)
|
||||
else
|
||||
hash = md5(luci.jsonc.stringify(content))
|
||||
end
|
||||
result.hashkey = hash
|
||||
if szType == 'ssr' then
|
||||
local dat = split(content, "/\\?")
|
||||
local hostInfo = split(dat[1], ':')
|
||||
result.server = hostInfo[1]
|
||||
result.server_port = hostInfo[2]
|
||||
result.protocol = hostInfo[3]
|
||||
result.encrypt_method = hostInfo[4]
|
||||
result.obfs = hostInfo[5]
|
||||
result.password = base64Decode(hostInfo[6], true)
|
||||
local params = {}
|
||||
for k, v in pairs(split(dat[2], '&')) do
|
||||
local t = split(v, '=')
|
||||
params[t[1]] = t[2]
|
||||
end
|
||||
result.obfs_param = base64Decode(params.bfsparam, true)
|
||||
result.protocol_param = base64Decode(params.protoparam, true)
|
||||
local group = base64Decode(params.group, true)
|
||||
if group then
|
||||
result.alias = "[" .. group .. "] "
|
||||
end
|
||||
result.alias = result.alias .. base64Decode(params.remarks, true)
|
||||
elseif szType == 'vmess' then
|
||||
local info = luci.jsonc.parse(content)
|
||||
result.type = 'v2ray'
|
||||
result.server = info.add
|
||||
result.server_port = info.port
|
||||
result.tcp_guise = "none"
|
||||
result.transport = info.net
|
||||
result.alter_id = info.aid
|
||||
result.vmess_id = info.id
|
||||
result.alias = info.ps
|
||||
result.ws_host = info.host
|
||||
result.ws_path = info.path
|
||||
result.h2_host = info.host
|
||||
result.h2_path = info.path
|
||||
if not info.security then
|
||||
result.security = 'auto'
|
||||
end
|
||||
if info.tls == 'tls' or info.tls == '1' then
|
||||
result.tls = '1'
|
||||
else
|
||||
result.tls = '0'
|
||||
end
|
||||
elseif szType == 'ssd' then
|
||||
result.type = 'ss'
|
||||
result.server = content.server
|
||||
result.server_port = content.port
|
||||
result.password = content.password
|
||||
result.encrypt_method_ss = content.encryption
|
||||
result.alias = '[' .. content.airport .. '] ' .. content.remarks
|
||||
end
|
||||
return result, hash
|
||||
end
|
||||
-- wget
|
||||
local function wget(url)
|
||||
local stdout = luci.sys.exec('wget-ssl --user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" --no-check-certificate -t 3 -T 10 -O- ' .. url)
|
||||
return trim(stdout)
|
||||
end
|
||||
|
||||
local execute = function()
|
||||
-- exec
|
||||
do
|
||||
-- subscribe_url = {'https://www.google.comc'}
|
||||
if proxy == '0' then -- 不使用代理更新的话先暂停
|
||||
log('服务正在暂停')
|
||||
luci.sys.init.stop(name)
|
||||
end
|
||||
for k, url in ipairs(subscribe_url) do
|
||||
local raw = wget(url)
|
||||
if #raw > 0 then
|
||||
local node, szType
|
||||
local groupHash = md5(url)
|
||||
cache[groupHash] = {}
|
||||
tinsert(nodeResult, {})
|
||||
local index = #nodeResult
|
||||
-- SSD 似乎是这种格式 ssd:// 开头的 不知道 SS 什么格式 没支持 不清楚其他机场是否这样
|
||||
if raw:find('ssd://') then
|
||||
szType = 'ssd'
|
||||
local nEnd = select(2, raw:find('ssd://'))
|
||||
node = base64Decode(raw:sub(nEnd + 1, #raw), true)
|
||||
node = luci.jsonc.parse(node)
|
||||
local extra = {
|
||||
airport = node.airport,
|
||||
port = node.port,
|
||||
encryption = node.encryption,
|
||||
password = node.password
|
||||
}
|
||||
local servers = {}
|
||||
-- SS里面包着 干脆直接这样
|
||||
for _, server in ipairs(node.servers) do
|
||||
tinsert(servers, setmetatable(server, { __index = extra }))
|
||||
end
|
||||
node = servers
|
||||
else
|
||||
-- ssd 外的格式
|
||||
node = split(base64Decode(raw, true), "\n")
|
||||
end
|
||||
for _, v in ipairs(node) do
|
||||
if v then
|
||||
local result, hash
|
||||
if szType == 'ssd' then
|
||||
result, hash = processData(szType, v)
|
||||
elseif not szType then
|
||||
local dat = split(v, "://")
|
||||
if dat and dat[1] and dat[2] then
|
||||
result, hash = processData(dat[1], base64Decode( dat[2], true))
|
||||
end
|
||||
else
|
||||
log('跳过未知类型: ' .. szType)
|
||||
end
|
||||
-- log(hash, result)
|
||||
if hash and result then
|
||||
if result.alias:find("过期时间") or
|
||||
result.alias:find("剩余流量") or
|
||||
result.alias:find("QQ群") or
|
||||
result.alias:find("官网") or
|
||||
result.server == ''
|
||||
then
|
||||
log('丢弃无效节点: ' .. result.type ..' 节点, ' .. result.alias)
|
||||
else
|
||||
log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
|
||||
result.grouphashkey = groupHash
|
||||
tinsert(nodeResult[index], result)
|
||||
cache[groupHash][hash] = nodeResult[index][#nodeResult[index]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
log('成功解析节点数量: ' ..#node)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- diff
|
||||
do
|
||||
assert(next(nodeResult))
|
||||
local add, del = 0, 0
|
||||
ucic:foreach(name, uciType, function(old)
|
||||
if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除
|
||||
if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then
|
||||
ucic:delete(name, old['.name'])
|
||||
del = del + 1
|
||||
else
|
||||
local dat = nodeResult[old.grouphashkey][old.hashkey]
|
||||
ucic:tset(name, old['.name'], dat)
|
||||
-- 标记一下
|
||||
setmetatable(nodeResult[old.grouphashkey][old.hashkey], { __index = { _ignore = true } })
|
||||
end
|
||||
else
|
||||
log('忽略手动添加的节点: ' .. old.alias)
|
||||
end
|
||||
end)
|
||||
for k, v in ipairs(nodeResult) do
|
||||
for kk, vv in ipairs(v) do
|
||||
if not vv._ignore then
|
||||
local section = ucic:add(name, uciType)
|
||||
ucic:tset(name, section, vv)
|
||||
add = add + 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
ucic:commit(name)
|
||||
-- 如果服务器已经不见了把帮换一个
|
||||
local globalServer = ucic:get_first(name, 'global', 'global_server', '')
|
||||
local firstServer = ucic:get_first(name, uciType)
|
||||
if not ucic:get(name, globalServer) then
|
||||
if firstServer then
|
||||
ucic:set(name, ucic:get_first(name, 'global'), 'global_server', firstServer)
|
||||
ucic:commit(name)
|
||||
log('当前主服务器已更新,正在自动更换。')
|
||||
end
|
||||
end
|
||||
if firstServer then
|
||||
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||
else
|
||||
luci.sys.call("/etc/init.d/" .. name .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||
end
|
||||
log('新增节点数量: ' ..add, '删除节点数量: ' .. del)
|
||||
log('更新成功服务正在启动')
|
||||
end
|
||||
end
|
||||
|
||||
if subscribe_url and #subscribe_url > 0 then
|
||||
xpcall(execute, function()
|
||||
log('发生错误, 正在恢复服务')
|
||||
local firstServer = ucic:get_first(name, uciType)
|
||||
if firstServer then
|
||||
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||
else
|
||||
luci.sys.call("/etc/init.d/" .. name .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||
end
|
||||
log('更新失败服务正在恢复')
|
||||
log(debug.traceback())
|
||||
end)
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user