#!/usr/bin/lua require "nixio" require "luci.util" local SYS = require "luci.sys" local HTTP = require "luci.http" local UCI = require("luci.model.uci").cursor() local FS = require "luci.openclash" local JSON = require "luci.jsonc" local UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" local class_type = type local type = arg[1] local all_test local router_self_proxy = tonumber(FS.uci_get_config("config", "router_self_proxy")) or 1 local now_name, group_name, group_type, group_show, status, ip, port, passwd, group_match_name local groups = {} local proxies = {} local tested_proxy = {} local unlock_cache_file = "/etc/openclash/history/streaming_unlock_cache" local unlock_cache = FS.readfile(unlock_cache_file) local unlock_cache_info = {} if unlock_cache then unlock_cache_info = JSON.parse(unlock_cache) or {} end local self_status = SYS.exec(string.format('ps -w |grep -v grep |grep -c "openclash_streaming_unlock.lua %s"', type)) local select_logic = FS.uci_get_config("config", "stream_auto_select_logic") or "urltest" if not type then print(os.date("%Y-%m-%d %H:%M:%S").." [Error] Streaming Unlock Has No Parameter of Type, Exiting...") os.exit(0) elseif router_self_proxy == 0 then print(os.date("%Y-%m-%d %H:%M:%S").." [Error] Streaming Unlock Could not Work Because of Router-Self Proxy Disabled, Exiting...") os.exit(0) elseif tonumber(self_status) > 1 then print(os.date("%Y-%m-%d %H:%M:%S").." [Error] Multiple Scripts Running, Exiting...") os.exit(0) end if arg[2] == "all" then all_test = true else all_test = false end function unlock_auto_select() local key_group, region, old_region, now, proxy, group_match, proxy_default, auto_get_group, info, group_now local original = {} local other_region_unlock = {} local no_old_region_unlock = {} local full_support_list = {} local fallback_select = {} local gorup_i18 = "Group:" local no_group_find = "failed to search based on keywords and automatically obtain the group, please confirm the validity of the regex!" local full_support_no_area = "full support" local full_support = "full support, area:" local only_original = "only support homemade!" local no_unlock = "not support unlock!" local select_success = "unlock node auto selected successfully, the current selected is" local select_faild = "unlock node auto selected failed, no node available, rolled back to the" local test_faild = "unlock test faild!" local test_start = "Start auto select unlock proxy..." local original_no_select = "only support homemade! the type of group is not select, auto select could not work!" local no_unlock_no_select = "not support unlock! the type of group is not select, auto select could not work!" local faild_no_select = "unlock test faild! the type of group is not select, auto select could not work!" local original_test_start = "only support homemade! start auto select unlock proxy..." local no_unlock_test_start = "not support unlock! start auto select unlock proxy..." local faild_test_start = "unlock test faild! start auto select unlock proxy..." local area_i18 = ", area:" local select_faild_other_region = "unlock node auto selected failed, no node match the regex, rolled back to other full support node" local other_region_unlock_test = ", but not match the regex!" local other_region_unlock_no_select = ", but not match the regex! the type of group is not select, auto select could not work!" local other_region_unlock_test_start = ", but not match the regex! start auto select unlock proxy..." local select_all_full_support = "unlock node test finished, rolled back to the full support node" local select_all_other_region = "unlock node test finished, no node match the regex, rolled back to other full support node" local select_all_faild = "unlock node test finished, no node available, rolled back to the" local no_nodes_filter = "no nodes name match the regex!" local select_success_no_old_region = "unlock node auto selected successfully, no node match the old region, rolled back to other full support node" local no_old_region_unlock_test = "full support but not match the old region!" local no_old_region_unlock_old_region = ", but not match the old region:" local no_old_region_unlock_old_region_no_select = ", the type of group is not select, auto select could not work!" local no_old_region_unlock_no_select = "but not match the old region! the type of group is not select, auto select could not work!" local select_all_no_old_region = "unlock node test finished, no node match the old region, rolled back to other full support node" --Get ip port and password get_auth_info() info = SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET http://%s:%s/proxies', passwd, ip, port)) if info then info = JSON.parse(info) if not info or not info.proxies then os.exit(0) end end --try to get group instead of matching the key auto_get_group = auto_get_policy_group(passwd, ip, port) if not auto_get_group then auto_get_group = auto_get_policy_group(passwd, ip, port) end if not auto_get_group then if type == "Netflix" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_netflix") or "netflix|奈飞" elseif type == "Disney Plus" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_disney") or "disney|迪士尼" elseif type == "HBO Max" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_hbo_max") or "hbo|hbomax|hbo max" elseif type == "YouTube Premium" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_ytb") or "youtobe|油管" elseif type == "TVB Anywhere+" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_tvb_anywhere") or "tvb" elseif type == "Amazon Prime Video" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_prime_video") or "prime video|amazon" elseif type == "DAZN" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_dazn") or "dazn" elseif type == "Paramount Plus" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_paramount_plus") or "paramount" elseif type == "Discovery Plus" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_discovery_plus") or "discovery" elseif type == "Bilibili" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_bilibili") or "bilibili" elseif type == "Google" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_google_not_cn") or "google|谷歌" elseif type == "OpenAI" then key_group = FS.uci_get_config("config", "stream_auto_select_group_key_openai") or "OpenAI|ChatGPT" end if not key_group then key_group = type end else key_group = "^" .. auto_get_group .. "$" end --save group name for _, value in pairs(info.proxies) do if value.all then table.insert(groups, value.name) end end group_match = false for _, value in pairs(info.proxies) do --match only once while true do --find group if not datamatch(value.name, key_group) then break else --get groups info group_match_name = value.name get_proxy(info, value.name, value.name) table.insert(tested_proxy, now_name) group_match = true --test now proxy region = proxy_unlock_test() if table_include(groups, now_name) then now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】" else now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】" end if status ~= 2 and status ~= 4 then region = proxy_unlock_test() end if status == 2 or status == 4 then if region ~= "" then table.insert(full_support_list, {value.now, value.now, get_group_now(info, value.now), region}) print(now..full_support.."【"..region.."】") write_cache(type, get_group_now(info, value.now), region) else table.insert(full_support_list, {value.now, value.now, get_group_now(info, value.now)}) print(now..full_support_no_area) write_cache(type, get_group_now(info, value.now)) end if not all_test and #nodes_filter(now_name, info) ~= 0 then if status == 4 then status = 2 if region ~= "" then write_cache(type, "old_region", region) end end if status == 2 and type == "Google" then write_cache(type, "old_region", now_name) end break else status = 0 end elseif status == 3 then if region ~= "" then table.insert(other_region_unlock, {value.now, value.now, get_group_now(info, value.now), region}) write_cache(type, get_group_now(info, value.now), region) else table.insert(other_region_unlock, {value.now, value.now, get_group_now(info, value.now)}) write_cache(type, get_group_now(info, value.now)) end if not all_test then if region ~= "" then print(now..full_support.."【"..region.."】"..other_region_unlock_test_start) else print(now..full_support_no_area..other_region_unlock_test_start) end else if region ~= "" then print(now..full_support.."【"..region.."】"..other_region_unlock_test) else print(now..full_support_no_area..other_region_unlock_test) end end elseif status == 1 then table.insert(original, {value.now, value.now, get_group_now(info, value.now)}) if not all_test then if type == "Netflix" then print(now..original_test_start) else print(now..no_unlock_test_start) end else if type == "Netflix" then print(now..only_original) else print(now..no_unlock) end end delete_cache(type, get_group_now(info, value.now)) else if not all_test then print(now..faild_test_start) else print(now..test_faild) end end --find new unlock if value.type == "Selector" then --save group current selected proxy_default = value.now if not all_test then --filter nodes value.all = nodes_filter(value.all, info) if #value.all > 1 then if select_logic == "random" then --sort by random value.all = table_rand(value.all, proxy_default) else --sort by urltest value.all = table_sort_by_urltest(value.all, proxy_default) end --sort by cache value.all = table_sort_by_cache(value.all) end end if #(value.all) == 0 then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..no_nodes_filter) break end --loop proxy test for i = 1, #(value.all) do while true do if value.all[i] == "REJECT" or value.all[i] == "REJECT-DROP" or value.all[i] == "PASS" then break else get_proxy(info, value.all[i], value.name) if group_type == "Selector" then if group_name == value.all[i] then SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, group_name, ip, port, urlencode(value.name))) end if not all_test then --filter nodes proxies = nodes_filter(proxies, info) if #proxies > 1 then if select_logic == "random" then --sort by random proxies = table_rand(proxies) else --sort by urltest proxies = table_sort_by_urltest(proxies) end --sort by cache proxies = table_sort_by_cache(proxies) end end if #(proxies) == 0 then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】"..no_nodes_filter) break end for p = 1, #(proxies) do proxy = proxies[p] if table_include(groups, proxy) then group_now = get_group_now(info, proxy) now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..group_now.."】" else now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..proxy.."】" end --skip tested proxy while true do if table_include(tested_proxy, get_group_now(info, proxy)) then break else table.insert(tested_proxy, proxy) end while true do if proxy == "REJECT" or proxy == "REJECT-DROP" or proxy == "PASS" or get_group_now(info, proxy) == "REJECT" or get_group_now(info, proxy) == "REJECT-DROP" or get_group_now(info, proxy) == "PASS" then break else SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, proxy, ip, port, urlencode(group_name))) region = proxy_unlock_test() if status == 2 then if region ~= "" then table.insert(full_support_list, {value.all[i], group_name, proxy, region}) if not all_test then print(now..full_support.."【"..region.."】") print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】"..area_i18.."【"..region.."】") else print(now..full_support.."【"..region.."】") end write_cache(type, proxy, region) else table.insert(full_support_list, {value.all[i], group_name, proxy}) if not all_test then print(now..full_support_no_area) print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】") else print(now..full_support_no_area) end write_cache(type, proxy) end elseif status == 3 then if region ~= "" then table.insert(other_region_unlock, {value.all[i], group_name, proxy, region}) print(now..full_support.."【"..region.."】"..other_region_unlock_test) write_cache(type, proxy, region) else table.insert(other_region_unlock, {value.all[i], group_name, proxy}) print(now..full_support_no_area..other_region_unlock_test) write_cache(type, proxy) end elseif status == 4 then if region ~= "" then table.insert(no_old_region_unlock, {value.all[i], group_name, proxy, region}) print(now..full_support.."【"..region.."】"..no_old_region_unlock_old_region.."【"..old_region.."】") write_cache(type, proxy, region) else table.insert(no_old_region_unlock, {value.all[i], group_name, proxy}) print(now..no_old_region_unlock_test) write_cache(type, proxy) end elseif status == 1 then table.insert(original, {value.all[i], group_name, proxy}) if type == "Netflix" then print(now..only_original) else print(now..no_unlock) end delete_cache(type, proxy) else print(now..test_faild) end end break end if status == 2 and not all_test then break elseif p == #(proxies) and #(proxies) ~= 1 then SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, now_name, ip, port, urlencode(group_name))) end break end if status == 2 and not all_test then break end end else --only group expand SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, value.all[i], ip, port, urlencode(group_name))) while true do if table_include(tested_proxy, now_name) or #nodes_filter(now_name, info) == 0 then break else table.insert(tested_proxy, now_name) end region = proxy_unlock_test() if table_include(groups, now_name) then now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】" else now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】" end if status == 2 then if region ~= "" then table.insert(full_support_list, {value.all[i], group_name, value.all[i], region}) if not all_test then print(now..full_support.."【"..region.."】") print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】"..area_i18.."【"..region.."】") else print(now..full_support.."【"..region.."】") end write_cache(type, value.all[i], region) else table.insert(full_support_list, {value.all[i], group_name, value.all[i]}) if not all_test then print(now..full_support_no_area) print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】") else print(now..full_support_no_area) end write_cache(type, value.all[i]) end elseif status == 3 then if region ~= "" then table.insert(other_region_unlock, {value.all[i], group_name, value.all[i], region}) print(now..full_support.."【"..region.."】"..other_region_unlock_no_select) write_cache(type, value.all[i], region) else table.insert(other_region_unlock, {value.all[i], group_name, value.all[i]}) print(now..full_support_no_area..other_region_unlock_no_select) write_cache(type, value.all[i]) end elseif status == 4 then if region ~= "" then table.insert(no_old_region_unlock, {value.all[i], group_name, value.all[i], region}) print(now..full_support.."【"..region.."】"..no_old_region_unlock_old_region.."【"..old_region.."】"..no_old_region_unlock_old_region_no_select) write_cache(type, value.all[i], region) else table.insert(no_old_region_unlock, {value.all[i], group_name, value.all[i]}) print(now..full_support_no_area..no_old_region_unlock_no_select) write_cache(type, value.all[i]) end elseif status == 1 then table.insert(original, {value.all[i], group_name, value.all[i]}) if type == "Netflix" then print(now..original_no_select) else print(now..no_unlock_no_select) end delete_cache(type, value.all[i]) else print(now..faild_no_select) end break end end end break end if status == 2 and not all_test then close_connections() break elseif i == #(value.all) and (#original > 0 or #other_region_unlock > 0 or #no_old_region_unlock > 0 or #full_support_list > 0) then if #full_support_list > 0 then fallback_select = full_support_list elseif #no_old_region_unlock > 0 then fallback_select = no_old_region_unlock elseif #other_region_unlock > 0 then fallback_select = other_region_unlock else fallback_select = original end for k, v in pairs(fallback_select) do if #nodes_filter(v[3], info) ~= 0 then if v[4] then table.insert(fallback_select, 1, {v[1], v[2], v[3], v[4]}) write_cache(type, "old_region", v[4]) else table.insert(fallback_select, 1, {v[1], v[2], v[3]}) end break end end for k, v in pairs(fallback_select) do SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, v[1], ip, port, urlencode(value.name))) SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, v[3], ip, port, urlencode(v[2]))) if table_include(groups, v[3]) then group_now = "【".. v[3] .. " ➟ " .. get_group_now(info, v[3]) .. "】" else group_now = "【".. v[3] .. "】" end if v[4] then group_now = group_now .. area_i18 .. "【"..v[4].."】" end if #full_support_list > 0 then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_full_support..group_now) elseif #no_old_region_unlock > 0 then if not all_test then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success_no_old_region..group_now) else print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_no_old_region..group_now) end elseif #other_region_unlock > 0 then if not all_test then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild_other_region..group_now) else print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_other_region..group_now) end else if not all_test then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild..group_now) else print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_faild..group_now) end end close_connections() break end elseif i == #(value.all) then SYS.exec(string.format("curl -sL -m 5 --retry 2 -w %%{http_code} -o /dev/null -H 'Authorization: Bearer %s' -H 'Content-Type:application/json' -X PUT -d '{\"name\":\"%s\"}' http://%s:%s/proxies/%s", passwd, proxy_default, ip, port, urlencode(value.name))) if table_include(groups, proxy_default) then group_now = value.name.." ➟ "..proxy_default.." ➟ "..get_group_now(info, proxy_default) else group_now = value.name.." ➟ "..proxy_default end print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild.."【"..group_now.."】") end end elseif #nodes_filter(get_group_now(info, value.name), info) ~= 0 then region = proxy_unlock_test() if status == 2 then if region ~= "" then if not all_test then print(now..full_support.."【"..region.."】") end print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】"..area_i18.."【"..region.."】") else if not all_test then print(now..full_support_no_area) end print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】") end if not all_test then break end elseif status == 3 then if region ~= "" then print(now..full_support.."【"..region.."】"..other_region_unlock_no_select) else print(now..full_support_no_area..other_region_unlock_no_select) end elseif status == 4 then if region ~= "" then print(now..full_support.."【"..region.."】"..no_old_region_unlock_old_region.."【"..old_region.."】"..no_old_region_unlock_old_region_no_select) else print(now..full_support_no_area..no_old_region_unlock_no_select) end elseif status == 1 then if type == "Netflix" then print(now..original_no_select) else print(now..no_unlock_no_select) end else print(now..faild_no_select) end end end break end if auto_get_group and group_match then break end if status == 2 and not all_test then break end end if not group_match and not auto_get_group then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..key_group.."】"..no_group_find) end --write cache FS.writefile(unlock_cache_file, JSON.stringify(unlock_cache_info)) end function urlencode(data) if not data then return end return HTTP.urlencode(data) or data end function datamatch(data, regex) local result = SYS.exec(string.format('ruby -E UTF-8 -e "x=\'%s\'; if x =~ /%s/i then print \'true\' else print \'false\' end"', data, regex)) if result == "true" then return true else return false end end function get_old_region(stream_type) local old_region = "" if not stream_type then stream_type = type end if unlock_cache_info[stream_type] and unlock_cache_info[stream_type]["old_region"] then return unlock_cache_info[stream_type]["old_region"] end return old_region end function get_old_regex(stream_type) local old_regex = "" if not stream_type then stream_type = type end if unlock_cache_info[stream_type] and unlock_cache_info[stream_type]["old_regex"] then return unlock_cache_info[stream_type]["old_regex"] end return old_regex end function write_cache(stream_type, node, value) if not value then value = "" end if not unlock_cache_info[stream_type] then unlock_cache_info[stream_type] = {} end if unlock_cache_info[stream_type][node] ~= value then unlock_cache_info[stream_type][node] = value end end function delete_cache(stream_type, node) if unlock_cache_info[stream_type] and unlock_cache_info[stream_type][node] then unlock_cache_info[stream_type][node] = nil end end function table_rand(t, d) if t == nil then return end local tab = {} math.randomseed(tostring(os.time()):reverse():sub(1, 9)) while #t ~= 0 do local n = math.random(0, #t) if t[n] ~= nil then if type == "YouTube Premium" and get_old_region("Google") == t[n] then table.insert(tab, 1, t[n]) elseif d ~= nil and table_include(groups, d) and d == t[n] then table.insert(tab, 1, t[n]) else table.insert(tab, t[n]) end table.remove(t, n) end end return tab end function table_sort_by_urltest(t, d) local info, get_delay, group_delay, delay_url local tab = {} local result = {} if t == nil then return end info = SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET http://%s:%s/providers/proxies', passwd, ip, port)) if info then info = JSON.parse(info) if not info or not info.providers then return t end end for n = 1, #(t) do get_delay = false for _, value in pairs(info.providers) do if value.proxies and value.name ~= "default" then for _, v in pairs(value.proxies) do if v.name == t[n] then if v.history and #(v.history) ~= 0 and v.history[#(v.history)].delay ~= 0 then table.insert(tab, {v.name, v.history[#(v.history)].delay}) get_delay = true end end if get_delay then break end end end if get_delay then break end end if not get_delay then if table_include(groups, t[n]) or t[n] == "DIRECT" then if FS.uci_get_config("config", "urltest_address_mod") and FS.uci_get_config("config", "urltest_address_mod") ~= "0" then delay_url = FS.uci_get_config("config", "urltest_address_mod") else delay_url = "http://www.gstatic.com/generate_204" end group_delay = SYS.exec(string.format('curl -sL -m 60 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET "http://%s:%s/proxies/%s/delay?timeout=5000&url=%s"', passwd, ip, port, urlencode(t[n]), urlencode(delay_url))) if group_delay then group_delay = JSON.parse(group_delay) end if group_delay and group_delay.delay and group_delay.delay ~= 0 then table.insert(tab, {t[n], group_delay.delay}) else table.insert(tab, {t[n], 123456}) end else table.insert(tab, {t[n], 123456}) end end end table.sort(tab, function(a, b) return a[2] < b[2] end) for _, value in pairs(tab) do if type == "YouTube Premium" and get_old_region("Google") == value[1] then table.insert(result, 1, value[1]) elseif d ~= nil and table_include(groups, d) and d == value[1] then table.insert(result, 1, value[1]) else table.insert(result, value[1]) end end return result end function table_sort_by_cache(t) local tab = {} local tab_b = {} local old_region = get_old_region() for n = 1, #(t) do if unlock_cache_info and unlock_cache_info[type] and unlock_cache_info[type][t[n]] and unlock_cache_info[type][t[n]] == old_region then table.insert(tab, t[n]) else table.insert(tab_b, t[n]) end end if #tab > 0 then print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." Group:".."【"..group_match_name.."】".."Cached Compliant Nodes Number:".."【"..#(tab).."】"..", Cached Non-compliant Nodes Number:".."【"..#(tab_b).."】"..", Prioritize Testing With Cached Compliant Nodes...") end for k,v in pairs(tab_b) do table.insert(tab, v) end return tab end function table_include(table, value) if table == nil then return false end for k, v in pairs(table) do if class_type(v) == "table" and class_type(value) == "table" then if table_eq(v, value) then return true else for i = 1, #(v) do if v[i] ~= value[i] then return false end end return true end else if v == value then return true end end end return false end function table_eq(t1, t2) if t1 == t2 then return true end if class_type(t1) ~= "table" or class_type(t2) ~= "table" then return false end for k1, v1 in pairs(t1) do local v2 = t2[k1] if v2 == nil or not table_eq(v1, v2) then return false end end for k2, v2 in pairs(t2) do local v1 = t1[k2] if v1 == nil or not table_eq(v1, v2) then return false end end return true end function get_auth_info() port = FS.uci_get_config("config", "cn_port") passwd = FS.uci_get_config("config", "dashboard_password") or "" ip = FS.lanip() if not ip or not port then os.exit(0) end end function close_connections() local con local group_cons_id = {} local enable = tonumber(FS.uci_get_config("config", "stream_auto_select_close_con")) or 1 if enable == 0 then return end con = SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET http://%s:%s/connections', passwd, ip, port)) if con then con = JSON.parse(con) end if con then for i = 1, #(con.connections) do if con.connections[i].chains[#(con.connections[i].chains)] == group_match_name then table.insert(group_cons_id, (con.connections[i].id)) end end --close connections if #(group_cons_id) > 0 then for i = 1, #(group_cons_id) do SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -X DELETE http://%s:%s/connections/%s >/dev/null 2>&1 &', passwd, ip, port, group_cons_id[i])) end end end end function nodes_filter(t, info) if t == nil then return end local tab = {} local regex, group_now if type == "Netflix" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_netflix") or "" elseif type == "Disney Plus" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_disney") or "" elseif type == "HBO Max" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_hbo_max") or "" elseif type == "YouTube Premium" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_ytb") or "" elseif type == "TVB Anywhere+" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_tvb_anywhere") or "" elseif type == "Amazon Prime Video" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_prime_video") or "" elseif type == "DAZN" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_dazn") or "" elseif type == "Paramount Plus" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_paramount_plus") or "" elseif type == "Discovery Plus" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_discovery_plus") or "" elseif type == "Bilibili" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_bilibili") or "" elseif type == "Google" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_google_not_cn") or "" elseif type == "OpenAI" then regex = FS.uci_get_config("config", "stream_auto_select_node_key_openai") or "" end if class_type(t) == "table" then if not regex or regex == "" then tab = t return tab end for n = 1, #t do if table_include(groups, t[n]) and info then group_now = get_group_now(info, t[n]) if datamatch(group_now, regex) then table.insert(tab, t[n]) end elseif datamatch(t[n], regex) and not table_include(groups, t[n]) then table.insert(tab, t[n]) end end else if not regex or regex == "" then table.insert(tab, t) return tab end if table_include(groups, t) and info then group_now = get_group_now(info, t) if datamatch(group_now, regex) then table.insert(tab, t) end elseif datamatch(t, regex) and not table_include(groups, t) then table.insert(tab, t) end end return tab end function proxy_unlock_test() local region = "" if type == "Netflix" then region = netflix_unlock_test() elseif type == "Disney Plus" then region = disney_unlock_test() elseif type == "HBO Max" then region = hbo_max_unlock_test() elseif type == "YouTube Premium" then region = ytb_unlock_test() elseif type == "TVB Anywhere+" then region = tvb_anywhere_unlock_test() elseif type == "Amazon Prime Video" then region = prime_video_unlock_test() elseif type == "DAZN" then region = dazn_unlock_test() elseif type == "Paramount Plus" then region = paramount_plus_unlock_test() elseif type == "Discovery Plus" then region = discovery_plus_unlock_test() elseif type == "Bilibili" then region = bilibili_unlock_test() elseif type == "Google" then region = google_not_cn_test() elseif type == "OpenAI" then region = openai_unlock_test() end return region end function auto_get_policy_group(passwd, ip, port) local auto_get_group, con if type == "Netflix" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.netflix.com &') elseif type == "Disney Plus" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.disneyplus.com &') elseif type == "HBO Max" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.max.com &') elseif type == "YouTube Premium" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://m.youtube.com/premium &') elseif type == "TVB Anywhere+" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.tvbanywhere.com/img/tvb/vip_purchase.png &') elseif type == "Amazon Prime Video" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.primevideo.com &') elseif type == "DAZN" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.dazn.com &') elseif type == "Paramount Plus" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.paramountplus.com/ &') elseif type == "Discovery Plus" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.discoveryplus.com/ &') elseif type == "Bilibili" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://www.bilibili.com/ &') elseif type == "Google" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://timeline.google.com &') elseif type == "OpenAI" then SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://chatgpt.com/ &') end os.execute("sleep 1") con = SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET http://%s:%s/connections', passwd, ip, port)) if con then con = JSON.parse(con) end if con and con.connections then for i = 1, #(con.connections) do if type == "Netflix" then if string.match(con.connections[i].metadata.host, "www%.netflix%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Disney Plus" then if string.match(con.connections[i].metadata.host, "www%.disneyplus%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "HBO Max" then if string.match(con.connections[i].metadata.host, "www%.max%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "YouTube Premium" then if string.match(con.connections[i].metadata.host, "m%.youtube%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "TVB Anywhere+" then if string.match(con.connections[i].metadata.host, "www%.tvbanywhere%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Amazon Prime Video" then if string.match(con.connections[i].metadata.host, "www%.primevideo%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "DAZN" then if string.match(con.connections[i].metadata.host, "www%.dazn%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Paramount Plus" then if string.match(con.connections[i].metadata.host, "www%.paramountplus%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Discovery Plus" then if string.match(con.connections[i].metadata.host, "www%.discoveryplus%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Bilibili" then if string.match(con.connections[i].metadata.host, "www%.bilibili%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "Google" then if string.match(con.connections[i].metadata.host, "timeline%.google%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end elseif type == "OpenAI" then if string.match(con.connections[i].metadata.host, "chatgpt%.com") then auto_get_group = con.connections[i].chains[#(con.connections[i].chains)] break end end end end return auto_get_group end function get_group_now(info, group) local now local group_ = group if table_include(groups, group_) then while table_include(groups, group_) do for _, value in pairs(info.proxies) do if value.name == group_ then now = value.now group_ = value.now break end end end end return now or group end function get_proxy(info, group, name) --group maybe a proxy proxies = {} group_show = "" local expand_group = tonumber(FS.uci_get_config("config", "stream_auto_select_expand_group")) or 0 if expand_group == 1 then if table_include(groups, group) then while table_include(groups, group) do for _, value in pairs(info.proxies) do if value.name == group then if group_show ~= "" then group_show = group_show .. " ➟ " .. group else if name == group then group_show = group else group_show = name .. " ➟ " .. group end end group_name = group group = value.now now_name = value.now or group_name proxies = value.all group_type = value.type break end end end if group_type ~= "Selector" then for _, value in pairs(info.proxies) do if value.name == name then group_name = name proxies = {} table.insert(proxies, group) break end end end else for _, value in pairs(info.proxies) do if value.name == name then group_show = name group_name = name now_name = value.now or name table.insert(proxies, group) group_type = value.type break end end end else if table_include(groups, group) then for _, value in pairs(info.proxies) do if value.name == name then group_name = name table.insert(proxies, group) group_type = value.type break end end while table_include(groups, group) do for _, value in pairs(info.proxies) do if value.name == group then if group_show ~= "" then group_show = group_show .. " ➟ " .. group else if name == group then group_show = group else group_show = name .. " ➟ " .. group end end now_name = value.now or group group = value.now break end end end else for _, value in pairs(info.proxies) do if value.name == name then table.insert(proxies, group) now_name = value.now or name group_show = name group_name = name group_type = value.type break end end end end end -- Thanks https://github.com/lmc999/RegionRestrictionCheck -- function netflix_unlock_test() status = 0 local filmId = 70143836 local url = "https://www.netflix.com/title/"..filmId local headers = "User-Agent: "..UA local info = SYS.exec(string.format('curl -sLI --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -o /dev/null -w %%{json} -H "Content-Type: application/json" -H "host: www.netflix.com" -H "accept-language: en-US,en;q=0.9" -H "sec-ch-ua: Google Chrome;v=125, Chromium;v=125, Not.A/Brand;v=24" -H "sec-ch-ua-mobile: ?0" -H "sec-ch-ua-platform: Windows" -H "sec-fetch-site: none" -H "sec-fetch-mode: navigate" -H "sec-fetch-user: ?1" -H "sec-fetch-dest: document" -H "%s" -XGET %s', headers, url)) local result = {} local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_netflix") or "" if info then info = JSON.parse(info) end if info then if info.http_code == 200 then status = 2 string.gsub(info.url_effective, '[^/]+', function(w) table.insert(result, w) end) if string.match(result[3], "^%a+") then region = string.upper(string.match(result[3], "^%a+")) if region == "TITLE" then region = "US" end if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end elseif info.http_code == 404 or info.http_code == 403 then status = 1 end end return region end function disney_unlock_test() status = 0 local url = "https://disney.api.edge.bamgrid.com/devices" local url2 = "https://disney.api.edge.bamgrid.com/token" local url3 = "https://disney.api.edge.bamgrid.com/graph/v1/device/graphql" local headers = '-H "Accept-Language: en" -H "Content-Type: application/json" -H "authorization: ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84"' local auth = '-H "authorization: Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84"' local body = '{"query":"mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }","variables":{"input":{"deviceFamily":"browser","applicationRuntime":"chrome","deviceProfile":"windows","deviceLanguage":"en","attributes":{"osDeviceIds":[],"manufacturer":"microsoft","model":null,"operatingSystem":"windows","operatingSystemVersion":"10.0","browserName":"chrome","browserVersion":"96.0.4606"}}}}' local region = "" local assertion, data, preassertion, disneycookie, tokencontent local regex = FS.uci_get_config("config", "stream_auto_select_region_key_disney") or "" local old_region = get_old_region() local old_regex = get_old_regex() preassertion = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 %s -H 'User-Agent: %s' -H 'content-type: application/json; charset=UTF-8' -d '{\"deviceFamily\":\"browser\",\"applicationRuntime\":\"chrome\",\"deviceProfile\":\"windows\",\"attributes\":{}}' -XPOST %s", auth, UA, url)) if preassertion and JSON.parse(preassertion) then assertion = JSON.parse(preassertion).assertion end if not assertion then return region end disneycookie = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&latitude=0&longitude=0&platform=browser&subject_token="..assertion.."&subject_token_type=urn%3Abamtech%3Aparams%3Aoauth%3Atoken-type%3Adevice" tokencontent = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 %s -H 'User-Agent: %s' -d '%s' -XPOST %s", auth, UA, disneycookie, url2)) if tokencontent and JSON.parse(tokencontent) then if JSON.parse(tokencontent).error_description then status = 1 return region end end data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 %s -H 'User-Agent: %s' -d '%s' -XPOST %s", headers, UA, body, url3)) if data and JSON.parse(data) then status = 1 if JSON.parse(data).extensions and JSON.parse(data).extensions.sdk and JSON.parse(data).extensions.sdk.session and JSON.parse(data).extensions.sdk.session.location.countryCode then region = JSON.parse(data).extensions.sdk.session.location.countryCode inSupportedLocation = JSON.parse(data).extensions.sdk.session.inSupportedLocation or "" if region == "JP" then status = 2 if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and not datamatch(region, old_region) and not all_test then status = 4 end if status == 2 and not all_test and region ~= "" and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end return region end if region ~= "" and inSupportedLocation then status = 2 if not datamatch(region, regex) then status = 3 elseif old_region ~= "" and not datamatch(region, old_region) and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end return region end end end return region end function hbo_max_unlock_test() status = 0 local url = "https://www.max.com/" local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s", UA, url)) local region = "" local outofregion local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_hbo_max") or "" if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 if string.match(data, "\"isUserOutOfRegion\":%a+") then outofregion = string.lower(string.sub(string.match(data, "\"isUserOutOfRegion\":%a+"), 21, -1)) end if string.match(data, "\"userCountry\":\"%a+\"") then region = string.upper(string.sub(string.match(data, "\"userCountry\":\"%a+\""), 16, -2)) end if region ~= "" and outofregion == "false" then status = 2 if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end return region end function ytb_unlock_test() status = 0 local url = "https://m.youtube.com/premium" local region = "" local old_region = get_old_region() local data, he_data local regex = FS.uci_get_config("config", "stream_auto_select_region_key_ytb") or "" local old_regex = get_old_regex() data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' -b 'YSC=BiCUU3-5Gdk; CONSENT=YES+cb.20220301-11-p0.en+FX+700; GPS=1; VISITOR_INFO1_LIVE=4VwPMkB7W5A; PREF=tz=Asia.Shanghai; _gcl_au=1.1.1809531354.1646633279' %s", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 if string.find(data,"www%.google%.cn") or string.find(data, "is not available in your country") then return region end if string.match(data, "\"GL\":\"%a+\"") then region = string.sub(string.match(data, "\"GL\":\"%a+\""), 7, -2) status = 2 else he_data = SYS.exec(string.format("curl -sIL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s", UA, url)) if string.match(he_data, "gl=%a+") then region = string.sub(string.match(he_data, "gl=%a+"), 4, -1) status = 2 else region = "US" end end if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= "" and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end return region end function tvb_anywhere_unlock_test() status = 0 local url = "https://uapisfm.tvbanywhere.com.sg/geoip/check/platform/android" local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_tvb_anywhere") or "" local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 data = JSON.parse(data) if data and data.allow_in_this_country then status = 2 if data.country then region = string.upper(data.country) end if region ~= "" then if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end return region end function prime_video_unlock_test() status = 0 local url = "https://www.primevideo.com" local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_prime_video") or "" local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 if string.match(data, "\"currentTerritory\":\"%a+\"") then region = string.sub(string.match(data, "\"currentTerritory\":\"%a+\""), 21, -2) status = 2 if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end return region end function dazn_unlock_test() status = 0 local url = "https://startup.core.indazn.com/misl/v5/Startup" local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_dazn") or "" local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' -X POST -d '{\"LandingPageKey\":\"generic\",\"Languages\":\"zh-CN,zh,en\",\"Platform\":\"web\",\"PlatformAttributes\":{},\"Manufacturer\":\"\",\"PromoCode\":\"\",\"Version\":\"2\"}' %s", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 data = JSON.parse(data) if data and data.Region and data.Region.isAllowed then status = 2 if data.Region.GeolocatedCountry then region = string.upper(data.Region.GeolocatedCountry) end if region ~= "" then if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end return region end function paramount_plus_unlock_test() status = 0 local url = "https://www.paramountplus.com/" local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_paramount_plus") or "" local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_%%{url_effective}_TAGS_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then status = 1 if string.match(data, "_TAG_[^\n]+_TAGS_") then if not string.find(string.match(data, "_TAG_[^\n]+_TAGS_"), "intl") then status = 2 if string.match(data, "\"siteEdition\":\"%a+|%a+\"") or string.match(data, "property: '%a+'") then region = string.upper(string.sub(string.match(data, "\"siteEdition\":\"%a+|%a+\""), 19, -2)) or string.upper(string.sub(string.match(data, "property: '%a+'"), 12, -2)) if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end end return region end function discovery_plus_unlock_test() status = 0 local url = "https://us1-prod-direct.discoveryplus.com/token?deviceId=d1a4a5d25212400d1e6985984604d740&realm=go&shortlived=true" local url1 = "https://us1-prod-direct.discoveryplus.com/users/me" local region = "" local old_region = get_old_region() local old_regex = get_old_regex() local regex = FS.uci_get_config("config", "stream_auto_select_region_key_discovery_plus") or "" local token = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, url)) if token and JSON.parse(token) and JSON.parse(token).data and JSON.parse(token).data.attributes then status = 1 token = JSON.parse(token).data.attributes.token local cookie = string.format("-b \"_gcl_au=1.1.858579665.1632206782; _rdt_uuid=1632206782474.6a9ad4f2-8ef7-4a49-9d60-e071bce45e88; _scid=d154b864-8b7e-4f46-90e0-8b56cff67d05; _pin_unauth=dWlkPU1qWTRNR1ZoTlRBdE1tSXdNaTAwTW1Nd0xUbGxORFV0WWpZMU0yVXdPV1l6WldFeQ; _sctr=1|1632153600000; aam_fw=aam%%3D9354365%%3Baam%%3D9040990; aam_uuid=24382050115125439381416006538140778858; st=%s; gi_ls=0; _uetvid=a25161a01aa711ec92d47775379d5e4d; AMCV_BC501253513148ED0A490D45%%40AdobeOrg=-1124106680%%7CMCIDTS%%7C18894%%7CMCMID%%7C24223296309793747161435877577673078228%%7CMCAAMLH-1633011393%%7C9%%7CMCAAMB-1633011393%%7CRKhpRz8krg2tLO6pguXWp5olkAcUniQYPHaMWWgdJ3xzPWQmdj0y%%7CMCOPTOUT-1632413793s%%7CNONE%%7CvVersion%%7C5.2.0; ass=19ef15da-95d6-4b1d-8fa2-e9e099c9cc38.1632408400.1632406594\"", token) local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' %s %s", UA, cookie, url1)) if data and JSON.parse(data) and JSON.parse(data).data and JSON.parse(data).data.attributes and (JSON.parse(data).data.attributes.currentLocationSovereignTerritory or JSON.parse(data).data.attributes.currentLocationTerritory) then region = JSON.parse(data).data.attributes.currentLocationTerritory or JSON.parse(data).data.attributes.currentLocationSovereignTerritory if region ~= "" then region = string.upper(region) status = 2 if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end return region end function bilibili_unlock_test() status = 0 local randsession = SYS.exec("cat /dev/urandom 2>/dev/null | head -n 32 | md5sum | head -c 32") local region = "" local httpcode, data, url local regex = FS.uci_get_config("config", "stream_auto_select_region_key_bilibili") or "CN" local old_region = get_old_region() if regex == "HK/MO/TW" then url = string.format("https://api.bilibili.com/pgc/player/web/playurl?avid=18281381&cid=29892777&qn=0&type=&otype=json&ep_id=183799&fourk=1&fnver=0&fnval=16&session=%s&module=bangumi", randsession) region = "HK/MO/TW" elseif regex == "TW" then url = string.format("https://api.bilibili.com/pgc/player/web/playurl?avid=50762638&cid=100279344&qn=0&type=&otype=json&ep_id=268176&fourk=1&fnver=0&fnval=16&session=%s&module=bangumi", randsession) region = "TW" else url = string.format("https://api.bilibili.com/pgc/player/web/playurl?avid=82846771&qn=0&type=&otype=json&ep_id=307247&fourk=1&fnver=0&fnval=16&session=%s&module=bangumi", randsession) region = "CN" end data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -w '_TAG_%%{http_code}_TAG_' -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, url)) if data and tonumber(string.sub(string.match(data, "_TAG_%d+_TAG_"), 6, 8)) == 200 then data = JSON.parse(data) status = 1 if data.code then if data.code == 0 then status = 2 if old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end return region end function google_not_cn_test() status = 0 local url = "https://timeline.google.com" local region = "" local httpcode = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -o /dev/null -w %%{http_code} -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, url)) if httpcode then if tonumber(httpcode) == 200 then status = 2 region = "NOT CN" if not all_test then write_cache(type, "old_region", tested_proxy[-1]) end else region = "CN" status = 1 end end return region end function openai_unlock_test() status = 0 local url = "https://api.openai.com/compliance/cookie_requirements" local url2 = "https://ios.chat.openai.com/" local region_url = "https://chat.openai.com/cdn-cgi/trace" local UA_SEC_CH_UA = '"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"' local regex = FS.uci_get_config("config", "stream_auto_select_region_key_openai") or "" local region = "" local region_data local old_region = get_old_region() local old_regex = get_old_regex() local data = SYS.exec(string.format("curl -sIL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'authority: api.openai.com' -H 'accept: */*' -H 'accept-language: en-US,en;q=0.9' -H 'authorization: Bearer null' -H 'content-type: application/json' -H 'origin: https://platform.openai.com' -H 'referer: https://platform.openai.com/' -H 'sec-ch-ua: %s' -H 'sec-ch-ua-mobile: ?0' -H 'sec-ch-ua-platform: \"Windows\"' -H 'sec-fetch-dest: empty' -H 'sec-fetch-mode: cors' -H 'sec-fetch-site: same-site' -H 'User-Agent: %s' '%s'", UA_SEC_CH_UA, UA, url)) local datas = SYS.exec(string.format("curl -sIL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'authority: ios.chat.openai.com' -H 'accept: */*;q=0.8,application/signed-exchange;v=b3;q=0.7' -H 'accept-language: en-US,en;q=0.9' -H 'sec-ch-ua: %s' -H 'sec-ch-ua-mobile: ?0' -H 'sec-ch-ua-platform: \"Windows\"' -H 'sec-fetch-dest: document' -H 'sec-fetch-mode: navigate' -H 'sec-fetch-site: none' -H 'sec-fetch-user: ?1' -H 'upgrade-insecure-requests: 1' -H 'User-Agent: %s' '%s'", UA_SEC_CH_UA, UA, url2)) if data and datas then if string.find(data, "unsupported_country") or string.find(datas, "VPN") then status = 1 else status = 2 region_data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, region_url)) if region_data and string.match(region_data, "loc=%a+") then region = string.upper(string.sub(string.match(region_data, "loc=%a+"), 5, -1)) if not datamatch(region, regex) then status = 3 elseif old_regex ~= regex and not all_test then status = 2 elseif old_region ~= "" and region ~= old_region and not all_test then status = 4 end if status == 2 and not all_test and region ~= old_region then write_cache(type, "old_region", region) end if status == 2 and not all_test and regex ~= old_regex then write_cache(type, "old_regex", regex) end end end end return region end function network_test() local test_url1 = "https://www.gstatic.com/generate_204" local test_url2 = "https://cp.cloudflare.com/generate_204" local httpcode1 = SYS.exec(string.format("curl -sL --connect-timeout 10 -m 15 --speed-time 5 --speed-limit 1 --retry 2 -o /dev/null -w %%{http_code} -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, test_url1)) local httpcode2 = SYS.exec(string.format("curl -sL --connect-timeout 10 -m 15 --speed-time 5 --speed-limit 1 --retry 2 -o /dev/null -w %%{http_code} -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, test_url2)) if httpcode1 or httpcode2 then if tonumber(httpcode1) == 204 or tonumber(httpcode2) == 204 then unlock_auto_select() return end end print(os.date("%Y-%m-%d %H:%M:%S").." [Error] Network Anomaly, Suspend Unlock Detection...") end network_test()