Просмотр исходного кода

Merge pull request #1875 from zxlhhyccc/tuic

luci-app-ssr-plus: Add Xray Hysteria2 protocol configuration import and subscribe.
zxl hhyccc 3 недель назад
Родитель
Сommit
299dd2ee2d

+ 46 - 5
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua

@@ -141,6 +141,12 @@ local function set_apply_on_parse(map)
     end
 end
 
+local has_xray = is_finded("xray")
+local has_hysteria2 = is_finded("hysteria")
+
+-- 读取当前存储的 xray_hy2_type
+local xray_hy2_type = uci:get_first("shadowsocksr", "server_subscribe", "xray_hy2_type")
+
 local has_ss_rust = is_finded("sslocal") or is_finded("ssserver")
 local has_ss_libev = is_finded("ss-redir") or is_finded("ss-local")
 
@@ -269,6 +275,43 @@ o.rawhtml = true
 o.template = "shadowsocksr/ssrurl"
 o.value = sid
 
+-- 新增一个选择框,用于选择 Xray 或 Hysteria2 核心
+o = s:option(ListValue, "xray_hy2_type", string.format("<b><span style='color:red;'>%s</span></b>", translatef("%s Node Use Type", "Hysteria2")))
+o.description = translate("The configured type also applies to the core specified when manually importing nodes.")
+-- 设置默认 Xray 或 Hysteria2 核心
+-- 动态添加选项
+if has_xray then
+    o:value("xray", translate("Xray"))
+end
+if has_hysteria2 then
+    o:value("hysteria2", translate("Hysteria2"))
+end
+-- 设置默认值
+if xray_hy2_type == "xray" then
+    o.default = "xray"
+elseif xray_hy2_type == "hysteria2" then
+    o.default = "hysteria2"
+end
+o.write = function(self, section, value)
+    -- 更新 Hysteria 节点的 xray_hy2_type
+    uci:foreach("shadowsocksr", "servers", function(s)
+        local node_type = uci:get("shadowsocksr", s[".name"], "type")  -- 获取节点类型
+        if node_type == "hysteria2" then  -- 仅修改 Hysteria 节点
+            local old_value = uci:get("shadowsocksr", s[".name"], "xray_hy2_type")
+            if old_value ~= value then
+                uci:set("shadowsocksr", s[".name"], "xray_hy2_type", value)
+            end
+        end
+    end)
+    -- 更新 server_subscribe 的 xray_hy2_type
+    local old_value = uci:get("shadowsocksr", "server_subscribe", "xray_hy2_type")
+    if old_value ~= value then
+        uci:set("shadowsocksr", "@server_subscribe[0]", "xray_hy2_type", value)
+    end
+    -- 更新当前 section 的 xray_hy2_type
+    ListValue.write(self, section, value)
+end
+
 o = s:option(ListValue, "type", translate("Server Node Type"))
 if is_finded("xray") or is_finded("v2ray") then
 	o:value("v2ray", translate("V2Ray/XRay"))
@@ -315,7 +358,7 @@ o:depends("type", "tun")
 o.description = translate("Redirect traffic to this network interface")
 
 -- 新增一个选择框,用于选择 Shadowsocks 版本
-o = s:option(ListValue, "has_ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translate("ShadowSocks Node Use Version")))
+o = s:option(ListValue, "has_ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translatef("%s Node Use Version", "ShadowSocks")))
 o.description = translate("Selection ShadowSocks Node Use Version.")
 -- 设置默认 Shadowsocks 版本
 -- 动态添加选项
@@ -343,15 +386,13 @@ o.write = function(self, section, value)
             end
         end
     end)
-
     -- 更新 server_subscribe 的 ss_type
     local old_value = uci:get("shadowsocksr", "server_subscribe", "ss_type")
     if old_value ~= value then
         uci:set("shadowsocksr", "@server_subscribe[0]", "ss_type", value)
     end
-
     -- 更新当前 section 的 has_ss_type
-    Value.write(self, section, value)
+    ListValue.write(self, section, value)
 end
 
 o = s:option(ListValue, "v2ray_protocol", translate("V2Ray/XRay protocol"))
@@ -363,7 +404,7 @@ if is_finded("xray") then
 	o:value("wireguard", translate("WireGuard"))
 end
 if is_finded("xray") then
-	o:value("hysteria2", translate("hysteria2"))
+	o:value("hysteria2", translate("Hysteria2"))
 end
 o:value("socks", translate("Socks"))
 o:value("http", translate("HTTP"))

+ 48 - 3
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua

@@ -39,6 +39,27 @@ local function optimize_cbi_ui()
 	]])
 end
 
+local has_xray = is_finded("xray")
+local has_hysteria2 = is_finded("hysteria")
+
+local hy2_type_list = {}
+
+if has_xray then
+    table.insert(hy2_type_list, { id = "xray", name = translate("Xray") })
+end
+if has_hysteria2 then
+    table.insert(hy2_type_list, { id = "hysteria2", name = translate("Hysteria2") })
+end
+
+-- 如果用户没有手动设置,则自动选择
+if not xray_hy2_type or xray_hy2_type == "" then
+    if has_hysteria2 then
+        xray_hy2_type = "hysteria2"
+    elseif has_xray then
+        xray_hy2_type = "xray"
+    end
+end
+
 local has_ss_rust = is_finded("sslocal") or is_finded("ssserver")
 local has_ss_libev = is_finded("ss-redir") or is_finded("ss-local")
 
@@ -52,7 +73,7 @@ if has_ss_libev then
 end
 
 -- 如果用户没有手动设置,则自动选择
-if ss_type == "" then
+if not ss_type or ss_type == "" then
     if has_ss_rust then
         ss_type = "ss-rust"
     elseif has_ss_libev then
@@ -103,9 +124,33 @@ o.default = 30
 o.rmempty = true
 o:depends("auto_update", "1")
 
+-- 确保 hy2_type_list 不为空
+if #hy2_type_list > 0 then
+    o = s:option(ListValue, "xray_hy2_type", string.format("<b><span style='color:red;'>%s</span></b>", translatef("%s Node Use Type", "Hysteria2")))
+	o.description = translate("The configured type also applies to the core specified when manually importing nodes.")
+    for _, v in ipairs(hy2_type_list) do
+        o:value(v.id, v.name) -- 存储 "Xray" / "Hysteria2",但 UI 显示完整名称
+    end
+    o.default = xray_hy2_type  -- 设置默认值
+    o.write = function(self, section, value)
+        -- 更新 Hysteria 节点的 xray_hy2_type
+        uci:foreach("shadowsocksr", "servers", function(s)
+            local node_type = uci:get("shadowsocksr", s[".name"], "type")  -- 获取节点类型
+            if node_type == "hysteria2" then  -- 仅修改 Hysteria 节点
+                local old_value = uci:get("shadowsocksr", s[".name"], "xray_hy2_type")
+                if old_value ~= value then
+                    uci:set("shadowsocksr", s[".name"], "xray_hy2_type", value)
+                end
+            end
+        end)
+        -- 更新当前 section 的 xray_hy2_type
+        ListValue.write(self, section, value)
+    end
+end
+
 -- 确保 ss_type_list 不为空
 if #ss_type_list > 0 then
-    o = s:option(ListValue, "ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translate("ShadowSocks Node Use Version")))
+    o = s:option(ListValue, "ss_type", string.format("<b><span style='color:red;'>%s</span></b>", translatef("%s Node Use Version", "ShadowSocks")))
     o.description = translate("Selection ShadowSocks Node Use Version.")
     for _, v in ipairs(ss_type_list) do
         o:value(v.id, v.name) -- 存储 "ss-libev" / "ss-rust",但 UI 显示完整名称
@@ -123,7 +168,7 @@ if #ss_type_list > 0 then
             end
         end)
         -- 更新当前 section 的 ss_type
-        Value.write(self, section, value)
+        ListValue.write(self, section, value)
     end
 end
 

+ 23 - 12
luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm

@@ -2,10 +2,12 @@
 <%
 local map = self.map
 local ss_type = map:get("@server_subscribe[0]", "ss_type")
+local xray_hy2_type = map:get("@server_subscribe[0]", "xray_hy2_type")
 -%>
 <script type="text/javascript">
 //<![CDATA[
 let ss_type = "<%=ss_type%>"
+let xray_hy2_type = "<%=xray_hy2_type%>"
 
 function padright(str, cnt, pad) {
 	return str + Array(cnt + 1).join(pad);
@@ -110,8 +112,26 @@ function import_ssr_url(btn, urlname, sid) {
 				return false;
 			}
 
-			document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = (ssu[0] === "hy2") ? "hysteria2" : ssu[0];
-			document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
+			if (xray_hy2_type === "hysteria2") {
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = (ssu[0] === "hy2") ? "hysteria2" : ssu[0];
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
+
+				if (params.get("protocol")) {
+					document.getElementsByName('cbid.shadowsocksr.' + sid + '.flag_transport')[0].checked = true; // 设置 flag_transport 为 true
+					document.getElementsByName('cbid.shadowsocksr.' + sid + '.flag_transport')[0].dispatchEvent(event); // 触发事件
+
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport_protocol')[0].value = params.get("protocol") || "udp";
+				}
+
+				if (params.get("pinSHA256")) {
+					document.getElementsByName('cbid.shadowsocksr.' + sid + '.pinsha256')[0].value = params.get("pinSHA256") || "";
+				}
+			} else {
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = "v2ray"
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.v2ray_protocol')[0].value = (ssu[0] === "hy2") ? "hysteria2" : ssu[0];
+				document.getElementsByName('cbid.shadowsocksr.' + sid + '.v2ray_protocol')[0].dispatchEvent(event);
+			}
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = url.hostname;
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = url.port || "443";
 			if (params.get("lazy") === "1") { 
@@ -124,12 +144,6 @@ function import_ssr_url(btn, urlname, sid) {
 
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.port_range')[0].value = params.get("mport") || "";
 			}
-			if (params.get("protocol")) {
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.flag_transport')[0].checked = true; // 设置 flag_transport 为 true
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.flag_transport')[0].dispatchEvent(event); // 触发事件
-
-			document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport_protocol')[0].value = params.get("protocol") || "udp";
-			}
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.hy2_auth')[0].value = decodeURIComponent(url.username);
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.hy2_auth')[0].dispatchEvent(event);
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.uplink_capacity')[0].value =
@@ -143,7 +157,7 @@ function import_ssr_url(btn, urlname, sid) {
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.obfs_type')[0].value = params.get("obfs");
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.salamander')[0].value = params.get("obfs-password") || params.get("obfs_password");
 			}
-			if (params.get("sni") || params.get("alpn")) {
+			if (params.get("security") === "tls" || params.get("sni") || params.get("alpn")) {
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].checked = true; // 设置 flag_obfs 为 true
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].dispatchEvent(event); // 触发事件
 				if (params.get("sni")) {
@@ -157,9 +171,6 @@ function import_ssr_url(btn, urlname, sid) {
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.insecure')[0].checked = true;
 				document.getElementsByName('cbid.shadowsocksr.' + sid + '.insecure')[0].dispatchEvent(event);
 			}
-			if (params.get("pinSHA256")) {
-				document.getElementsByName('cbid.shadowsocksr.' + sid + '.pinsha256')[0].value = params.get("pinSHA256") || "";
-			}
 			document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = url.hash ? decodeURIComponent(url.hash.slice(1)) : "";
 
 			s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";

Разница между файлами не показана из-за своего большого размера
+ 189 - 176
luci-app-ssr-plus/po/templates/ssr-plus.pot


Разница между файлами не показана из-за своего большого размера
+ 189 - 176
luci-app-ssr-plus/po/zh_Hans/ssr-plus.po


+ 8 - 0
luci-app-ssr-plus/root/etc/uci-defaults/luci-ssr-plus

@@ -40,10 +40,18 @@ if [ -s "/etc/config/shadowsocksr" ]; then
         uci -q set shadowsocksr.@server_subscribe[0].auto_update_min_time='0'
     fi
 
+    if ! uci -q get shadowsocksr.@server_subscribe[0].ss_type > /dev/null; then
+        uci -q set shadowsocksr.@server_subscribe[0].ss_type='ss-rust'
+    fi
+
     if ! uci -q get shadowsocksr.@server_subscribe[0].user_agent > /dev/null; then
         uci -q set shadowsocksr.@server_subscribe[0].user_agent='v2rayN/9.99'
     fi
 
+    if ! uci -q get shadowsocksr.@server_subscribe[0].xray_hy2_type > /dev/null; then
+        uci -q set shadowsocksr.@server_subscribe[0].xray_hy2_type='hysteria2'
+    fi
+
     if ! uci -q get shadowsocksr.@global_xray_fragment[0] > /dev/null; then
         uci -q add shadowsocksr global_xray_fragment
         uci -q set shadowsocksr.@global_xray_fragment[0].fragment='0'

+ 2 - 0
luci-app-ssr-plus/root/usr/share/shadowsocksr/shadowsocksr.config

@@ -26,7 +26,9 @@ config server_subscribe
 	option auto_update_week_time '*'
 	option auto_update_day_time '2'
 	option auto_update_min_time '0'
+	option ss_type 'ss-rust'
 	option user_agent 'v2rayN/9.99'
+	option xray_hy2_type 'hysteria2'
 	option filter_words '过期/套餐/剩余/QQ群/官网/防失联/回国'
 
 config access_control

+ 41 - 13
luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua

@@ -34,14 +34,35 @@ local ss_type = ucic:get_first(name, 'server_subscribe', 'ss_type', 'ss-rust')
 -- 根据 ss_type 选择对应的程序
 local ss_program = "sslocal"
 if ss_type == "ss-rust" then
-    ss_program = "sslocal"  -- Rust 版本使用 sslocal
+	ss_program = "sslocal"  -- Rust 版本使用 sslocal
 elseif ss_type == "ss-libev" then
-    ss_program = "ss-redir"  -- Libev 版本使用 ss-redir
+	ss_program = "ss-redir"  -- Libev 版本使用 ss-redir
+end
+-- 从 UCI 配置读取 xray_hy2_type 设置
+local xray_hy2_type = ucic:get_first(name, 'server_subscribe', 'xray_hy2_type', 'hysteria2')
+local xray_hy2_program = "hysteria"
+if xray_hy2_type == "xray" then
+	xray_hy2_program = "xray"  -- Hysteria2 使用 Xray
+elseif xray_hy2_type == "hysteria2" then
+	xray_hy2_program = "hysteria"  -- Hysteria2 使用 Hysteria
 end
 local v2_ss = luci.sys.exec('type -t -p ' .. ss_program .. ' 2>/dev/null') ~= "" and "ss" or "v2ray"
 local has_ss_type = luci.sys.exec('type -t -p ' .. ss_program .. ' 2>/dev/null') ~= "" and ss_type
 local v2_tj = luci.sys.exec('type -t -p trojan') ~= "" and "trojan" or "v2ray"
-local hy2_type = luci.sys.exec('type -t -p hysteria') ~= "" and "hysteria2"
+-- 检查程序是否存在
+local program_exists = luci.sys.exec('type -t -p ' .. xray_hy2_program .. ' 2>/dev/null') ~= ""
+-- 初始化变量
+local hy2_type = nil
+local has_xray_hy2_type = nil
+if program_exists then
+	-- 设置节点类型
+	if xray_hy2_type == "hysteria2" then
+		hy2_type = "hysteria2"
+	else
+		hy2_type = "v2ray"  -- 当使用 Xray 时,节点类型是 "v2ray"
+		has_xray_hy2_type = "hysteria2"  -- 可用的协议类型是 Hysteria2
+	end
+end
 local tuic_type = luci.sys.exec('type -t -p tuic-client') ~= "" and "tuic"
 local log = function(...)
 	print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({...}, " "))
@@ -194,19 +215,29 @@ local function processData(szType, content)
 		--	log(k.."="..v)
 		-- end
 
-		-- 如果 hy2 程序未安装则跳过订阅	
-		if not hy2_type then
+		-- 如果 hy2 程序未安装则跳过订阅
+		if not (hy2_type or has_xray_hy2_type) then
 			return nil
 		end
+	
+		if xray_hy2_type == "hysteria2" then
+			if params.protocol then
+				result.flag_transport = "1"
+				result.transport_protocol = params.protocol or "udp"
+			end
+			if params.pinSHA256 then
+				result.pinsha256 = params.pinSHA256
+			end
+		else
+			result.v2ray_protocol = has_xray_hy2_type
+		end
 
 		result.alias = url.fragment and UrlDecode(url.fragment) or nil
+		result.xray_hy2_type = xray_hy2_type
 		result.type = hy2_type
 		result.server = url.host
 		result.server_port = url.port or 443
-		if params.protocol then
-			result.flag_transport = "1"
-			result.transport_protocol = params.protocol or "udp"
-		end
+
 		result.hy2_auth = url.user
 		result.uplink_capacity = tonumber((params.upmbps or ""):match("^(%d+)")) or nil
 		result.downlink_capacity = tonumber((params.downmbps or ""):match("^(%d+)")) or nil
@@ -219,7 +250,7 @@ local function processData(szType, content)
 			result.obfs_type = params.obfs
 			result.salamander = params["obfs-password"] or params["obfs_password"]
 		end
-		if (params.sni and params.sni ~= "") or (params.alpn and params.alpn ~= "") then
+		if (params.security and params.security == "tls") or (params.sni and params.sni ~= "") or (params.alpn and params.alpn ~= "") then
 			result.tls = "1"
 			if params.sni then
 				result.tls_host = params.sni
@@ -235,9 +266,6 @@ local function processData(szType, content)
 		if params.insecure == "1" then
 			result.insecure = params.insecure
 		end
-		if params.pinSHA256 then
-			result.pinsha256 = params.pinSHA256
-		end
 	elseif szType == 'ssr' then
 		-- 去掉前后空白和#注释
 		local link = trim(content:gsub("#.*$", ""))

Некоторые файлы не были показаны из-за большого количества измененных файлов