diff --git a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua index ed71adae..2dd8890f 100644 --- a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua +++ b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua @@ -509,3 +509,46 @@ function parseClashNode(raw, remark) return #links > 0 and table.concat(links, "\n") or "" end + +function parse_clash_sub_info(headers) + local userinfo = headers:match("[Ss]ubscription%-userinfo:%s*([^\r\n]+)") + if not userinfo then return nil end + + local upload = tonumber(userinfo:match("upload=(%d+)")) or 0 + local download = tonumber(userinfo:match("download=(%d+)")) or 0 + local total = tonumber(userinfo:match("total=(%d+)")) or 0 + local expire = tonumber(userinfo:match("expire=(%d+)")) + local remain = total - (upload + download) + if remain < 0 then remain = 0 end + + local function format_size(bytes) + local units = { "B", "KB", "MB", "GB", "TB", "PB" } + local i = 1 + while bytes >= 1024 and i < #units do + bytes = bytes / 1024 + i = i + 1 + end + if bytes >= 100 then + return string.format("%.0f%s", bytes, units[i]) + elseif bytes >= 10 then + return string.format("%.1f%s", bytes, units[i]) + else + return string.format("%.2f%s", bytes, units[i]) + end + end + + local rem_traffic = format_size(remain) + + local expired_date + if expire and expire > 0 then + local t = os.date("*t", expire) + expired_date = string.format("%d-%d-%d", t.year, t.month, t.day) + else + expired_date = "长期有效" + end + + return { + rem_traffic = rem_traffic, + expired_date = expired_date + } +end diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 2f1fefee..b3bed70f 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -455,6 +455,10 @@ local function get_subscribe_info(cfgid, value) if type(cfgid) ~= "string" or cfgid == "" or type(value) ~= "string" then return end + local info = subscribe_info[cfgid] + if info and info.expired_date and info.expired_date ~= "" and info.rem_traffic and info.rem_traffic ~= "" then + return + end value = value:gsub("%s+", "") local date_patterns = {"套餐到期:(.+)", "过期时间:(.+)", "有效期至:(.+)", "到期时间:(.+)", "截止日期:(.+)"} local expired_date @@ -1627,7 +1631,12 @@ end local function curl(url, file, ua, mode) if not url or url == "" then return 22, 404 end local curl_args = { - "-fskL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "-H 'Accept-Encoding: identity'" + "-fskL", + "--retry 3", + "--connect-timeout 3", + "-H 'Accept-Encoding: identity'", + "--dump-header -", + "-w '\\n%{http_code}'" } ua = (ua and ua ~= "") and ua or "passwall" @@ -1645,7 +1654,21 @@ local function curl(url, file, ua, mode) else return_code, result = api.curl_logic(url, file, curl_args) end - return return_code, tonumber(result) + + if result and result ~= "" then + local body, code = result:match("^(.-)%s*([0-9]+)$") + if code then + http_code = tonumber(code) or 0 + header_str = body + else + http_code = tonumber(result:match("(%d+)%s*$")) or 0 + end + end + if header_str ~= "" then + header_str = header_str:gsub("\r", "") + end + + return return_code, http_code, header_str end function get_headers() @@ -2122,7 +2145,7 @@ local execute = function() local cfgid = value[".name"] local remark = value.remark or "" local url = value.url or "" - local tmp_file, ua + local tmp_file, ua, headers local url_is_local if fs.access(url) then @@ -2137,7 +2160,7 @@ local execute = function() log('正在订阅:【' .. remark .. '】' .. url .. ' [' .. result .. ']') tmp_file = "/tmp/" .. cfgid local return_code - return_code, value.http_code = curl(url, tmp_file, ua, access_mode) + return_code, value.http_code, headers = curl(url, tmp_file, ua, access_mode) if return_code ~= 0 then fail_list[#fail_list + 1] = value luci.sys.call("rm -f " .. tmp_file) @@ -2155,6 +2178,7 @@ local execute = function() log('订阅:【' .. remark .. '】没有变化,无需更新。') else raw_data = parseClashNode(raw_data) + subscribe_info[cfgid] = parse_clash_sub_info(headers) parse_link(raw_data, "2", remark, value) uci:set(appname, cfgid, "md5", new_md5) end