diff --git a/.gitignore b/.gitignore index f3b3916..dff8181 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /cmake-build-debug-vs2019/ -/out_yml/ +/out_yml/*.yaml /.idea + diff --git a/config/cache.db b/config/cache.db index 40fee79..b8b7218 100644 Binary files a/config/cache.db and b/config/cache.db differ diff --git a/config/config.yaml b/config/config.yaml index b35737f..e3d7dd5 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,13 +5,17 @@ mode: rule log-level: info external-controller: 0.0.0.0:7898 secret: "" +external-ui: ./ui +external-ui-name: ui +external-ui-url: https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip external-controller-cors: allow-private-network: true allow-origins: - "*" -external-ui: ./ui -external-ui-name: ui -external-ui-url: https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip +geox-url: + geoip: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat + geosite: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat + mmdb: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb unified-delay: true tcp-concurrent: true ipv6: false @@ -51,6 +55,14 @@ tun: - 10.0.0.0/8 - 192.168.0.0/16 - 127.0.0.0/8 +proxy-providers: + 赔钱机场: + type: http + url: https://dash.xn--cp3a08l.com/api/v1/pq/8424b1c22e1363a5906a156b2e849937?flag=meta + interval: 86400 + path: ./proxy_providers/airport-a.yaml + health-check: {enable: true, url: https://www.gstatic.com/generate_204, interval: 300, lazy: true} + exclude-filter: (?i)(剩余|流量|到期|套餐|官网|公告) proxies: - {name: 🇯🇵日本东京01-0.1倍 | 电信联通推荐, type: vless, server: unamecf.xn--ghqu5fm27b67w.com, port: 443, uuid: 0320e2e7-5862-4306-bf8b-0466b7993079, udp: true, tls: true, skip-cert-verify: false, client-fingerprint: ios, servername: ujp1.xn--ghqu5fm27b67w.com, network: ws, ws-opts: {path: /pq/jp1, headers: {Host: ujp1.xn--ghqu5fm27b67w.com}}} - {name: 🇯🇵日本东京02-0.1倍 | 电信联通推荐, type: vless, server: unamecf.xn--ghqu5fm27b67w.com, port: 443, uuid: 0320e2e7-5862-4306-bf8b-0466b7993079, udp: true, tls: true, skip-cert-verify: false, client-fingerprint: ios, servername: ujp2.xn--ghqu5fm27b67w.com, network: ws, ws-opts: {path: /pq/jp2, headers: {Host: ujp2.xn--ghqu5fm27b67w.com}}} diff --git a/out_yml/check.ps1 b/out_yml/check.ps1 new file mode 100644 index 0000000..f88106d --- /dev/null +++ b/out_yml/check.ps1 @@ -0,0 +1,21 @@ +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" +$rootDir = Split-Path -Parent $PSScriptRoot +$mihomoPath = Join-Path $rootDir "mihomo-windows-amd64-v3-go125.exe" +$configPath = Join-Path $PSScriptRoot "config.yaml" +if (-not (Test-Path -LiteralPath $mihomoPath -PathType Leaf)) { + Write-Error "未找到 Mihomo 内核:$mihomoPath" + exit 1 +} +if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) { + Write-Error "未找到配置文件:$configPath" + exit 1 +} +Write-Host "正在检查配置格式:$configPath" +& $mihomoPath -t -d $rootDir -f $configPath +$exitCode = $LASTEXITCODE +if ($exitCode -ne 0) { + Write-Host "配置格式检查失败。" -ForegroundColor Red + exit $exitCode +} +Write-Host "配置格式检查通过。" -ForegroundColor Green \ No newline at end of file diff --git a/src/base/groups.cpp b/src/base/groups.cpp index 136cc62..9ea0801 100644 --- a/src/base/groups.cpp +++ b/src/base/groups.cpp @@ -40,6 +40,22 @@ YAML::Node make_url_test_group(const std::string& group_name, const std::vector< return group; } +YAML::Node make_smart_group(const std::string& group_name, const std::vector& list, bool hidden) { + YAML::Node group(YAML::NodeType::Map); + group["name"] = group_name; + group["type"] = "smart"; + group["url"] = "http://cp.cloudflare.com/generate_204"; + group["interval"] = 200; + group["lazy"] = true; + group["uselightgbm"] = true; + group["proxies"] = make_proxy_list(list); + if (hidden) { + group["hidden"] = true; + } + group.SetStyle(YAML::EmitterStyle::Flow); + return group; +} + YAML::Node make_fallback_group(const std::string& group_name, const std::vector& list, int interval, int timeout, bool lazy) { YAML::Node group = make_group(group_name, "fallback", list); set_test_options(group, interval, timeout, lazy); diff --git a/src/base/groups.h b/src/base/groups.h index 9944883..6e8e14f 100644 --- a/src/base/groups.h +++ b/src/base/groups.h @@ -6,6 +6,8 @@ YAML::Node make_select_group(const std::string& group_name, const std::vector& list); // 创建 url-test 策略组,按测速结果从 proxies 列表中选择延迟最低的出口。 YAML::Node make_url_test_group(const std::string& group_name, const std::vector& list, bool hidden = true); +// 创建 smart 策略组,基于节点历史表现和 LightGBM 模型从 proxies 列表中动态选择综合质量更优的出口。 +YAML::Node make_smart_group(const std::string& group_name, const std::vector& list, bool hidden = true); // 创建 fallback 策略组,按 proxies 顺序使用第一个可用出口,前面的不可用才切后面的。 YAML::Node make_fallback_group(const std::string& group_name, const std::vector& list, int interval = 60, int timeout = 1000, bool lazy = false); // 创建 load-balance 策略组,在 proxies 列表中做负载均衡,strategy 常用 consistent-hashing 或 round-robin。 diff --git a/src/cpp_filter/main.cpp b/src/cpp_filter/main.cpp index 0c5c3dc..e2ec3b7 100644 --- a/src/cpp_filter/main.cpp +++ b/src/cpp_filter/main.cpp @@ -28,6 +28,14 @@ struct Atomic_Unit_Proxy_Group_Map { data["🔥" + group_name].push_back(key); } std::map> data; + + [[nodiscard]] std::vector atomic_unit_proxy_group_list() const { + std::vector keys; + for (const auto& item : data) { + keys.emplace_back(item.first); + } + return keys; + } }; #define Key_Word(kw) bool kw = contain(name, #kw); \ if (kw) map.push(#kw, name); @@ -38,78 +46,101 @@ int main() { #endif auto yml_dir = get_executable_dir() + "/../yml"; YAML::Node origin = YAML::Load(read_file_content(yml_dir + "/origin.yaml")); - auto proxies = origin["proxies"]; - YAML::Node node = YAML::Load(read_file_content(yml_dir + "/base_ruler.yaml")); YAML::Node another_rules = YAML::Load(read_file_content(yml_dir + "/another_rules.yaml")); YAML::Node final_rules = another_rules["rules"]; for (const auto& rule : origin["rules"]) { final_rules.push_back(rule); } - - std::vector all; - Atomic_Unit_Proxy_Group_Map map; - for (const auto& proxy : proxies) { - auto name = proxy["name"].as(); - auto type = proxy["type"].as(); - // std::cout << name << " " << type << std::endl; - all.push_back(name); - Key_Word(日本) - Key_Word(日本东京) - Key_Word(美国) - Key_Word(美国圣何塞) - Key_Word(美国阿什本) - Key_Word(新加坡) - bool 下载 = contain_any(name, {"0.1", "0.01"}); - if (下载) { - map.push("下载", name); - } - if (!日本 && !美国 && !新加坡 && !下载) { - map.push("其他", name); - } - } + std::vector proxy_list; YAML::Node groups_node(YAML::NodeType::Sequence); groups_node.SetStyle(YAML::EmitterStyle::Block); - { - std::vector list{"🔥日本", "🔥日本东京", "🔥美国", "🔥美国圣何塞", "🔥美国阿什本", "🔥新加坡", "🔥下载"}; - for (const auto& i : all) { - list.push_back(i); - } - groups_node.push_back(make_url_test_group("️♻️自动选择", list)); - groups_node.push_back(make_select_group("🚀节点选择", list)); - std::vector list2{"🚀节点选择"}; - for (const auto& li : list) { - list2.push_back(li); - } - groups_node.push_back(make_select_group("🎬媒体解锁", list2)); - groups_node.push_back(make_select_group("🐟漏网之鱼", list2)); - groups_node.push_back(make_select_group("🤖AI网站", list2)); - groups_node.push_back(make_select_group("▶️油管视频", list2)); - groups_node.push_back(make_select_group("Ⓜ️微软服务", list2)); - } - for (const auto& t : map.data) { - const auto& group_name = t.first; - std::cout << "group_name:" << group_name << std::endl; - const auto& list = t.second; - if (list.empty()) { + Atomic_Unit_Proxy_Group_Map props; + + + for (const auto& proxy : proxies) { + auto name = proxy["name"].as(); + if (contain(name, "剩余流量") || contain(name, "套餐到期")) { continue; } - groups_node.push_back(make_url_test_group(group_name, list)); + proxy_list.push_back(name); + bool 日本 = contain(name, "日本"); + bool 日本东京 = contain(name, "日本东京"); + bool 美国 = contain(name, "美国"); + bool 美国圣何塞 = contain(name, "美国圣何塞"); + bool 美国阿什本 = contain(name, "美国阿什本"); + bool 新加坡 = contain(name, "新加坡"); + bool 倍001 = contain(name, "0.01"); + bool 倍01 = contain(name, "0.1"); + bool 电信联通移动推荐 = contain(name, "电信联通移动推荐"); + if (日本) props.push("日本", name); + if (日本东京) props.push("日本东京", name); + if (美国) props.push("美国", name); + if (美国圣何塞) props.push("美国圣何塞", name); + if (新加坡) props.push("新加坡", name); + if (美国阿什本) props.push("美国阿什本", name); + bool 下载 = 倍001 || 倍01; + if (美国 && 电信联通移动推荐) { + props.push("美国电信联通移动推荐", name); + } + if (下载) { + if (美国 && 倍001) { + props.push("美国0.01倍", name); + } else if (日本 && 倍01) { + props.push("日本0.1倍", name); + } else if (新加坡 && 倍01) { + props.push("新加坡0.1倍", name); + } else if (美国 && 倍01) { + props.push("美国0.1倍", name); + }else { + props.push("其他低倍率", name); + } + } } + auto base_props = props.atomic_unit_proxy_group_list(); + for (auto& prop : base_props) { + //groups_node.push_back(make_smart_group(prop, props.data[prop])); + groups_node.push_back(make_url_test_group(prop, props.data[prop])); + } + + auto download = "📥下载"; + + + groups_node.push_back(make_fallback_group(download, {"🔥美国0.01倍", "🔥日本0.1倍", "🔥新加坡0.1倍", "🔥美国0.1倍", "🔥其他低倍率"}));; + groups_node.push_back(make_fallback_group("🤖AI网站", {"🔥美国圣何塞", "🔥美国电信联通移动推荐"})); + + + + std::vector proxy_group_list = {download, "DIRECT"}; + for (const auto& proxy_group : proxy_list) { + proxy_group_list.push_back(proxy_group); + } + groups_node.push_back(make_url_test_group("️♻️自动选择", proxy_group_list, false)); + groups_node.push_back(make_select_group("🚀节点选择", props.atomic_unit_proxy_group_list() + proxy_group_list)); + std::vector list2{"🚀节点选择"}; + for (const auto& li : proxy_group_list) { + list2.emplace_back(li); + } + groups_node.push_back(make_select_group("🎬媒体解锁", list2)); + groups_node.push_back(make_select_group("🐟漏网之鱼", list2)); + // groups_node.push_back(make_select_group("🤖AI网站", list2)); + groups_node.push_back(make_select_group("▶️油管视频", list2)); + groups_node.push_back(make_select_group("Ⓜ️微软服务", list2)); + + node["proxies"] = proxies; node["proxy-groups"] = groups_node; node["rule-providers"] = another_rules["rule-providers"]; node["sub-rules"] = another_rules["sub-rules"]; node["rules"] = final_rules; - - auto out_yml_dir = get_executable_dir() + "/../out_yml"; const std::string output_group_path = out_yml_dir + "/groups.yaml"; - const std::string final_path = out_yml_dir + "/config2.yaml"; + const std::string final_path = out_yml_dir + "/config.yaml"; write_yaml_file(output_group_path, groups_node); write_yaml_file(final_path, node); std::cout << "输出完成: " << output_group_path << std::endl; + std::cout << "输出完成: " << final_path << std::endl; return 0; } \ No newline at end of file diff --git a/src/cpp_filter2/main.cpp b/src/cpp_filter2/main.cpp deleted file mode 100644 index db7505c..0000000 --- a/src/cpp_filter2/main.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "base/export.h" -#ifdef _WIN32 -#include -#endif - -#include "base/groups.h" - - -std::string yaml_to_string(const YAML::Node& node) { - YAML::Emitter out; - out << YAML::Block << node; - return out.c_str(); -} -void write_yaml_file(const std::string& path, const YAML::Node& node) { - std::ofstream out(path, std::ios::binary); - out << yaml_to_string(node); -} - -struct Atomic_Unit_Proxy_Group_Map { - void push(const std::string& group_name, const std::string& key) { - data["🔥" + group_name].push_back(key); - } - std::map> data; - - [[nodiscard]] std::vector atomic_unit_proxy_group_list() const { - std::vector keys; - for (const auto& item : data) { - keys.emplace_back(item.first); - } - return keys; - } -}; -#define Key_Word(kw) bool kw = contain(name, #kw); \ -if (kw) map.push(#kw, name); -int main() { -#ifdef _WIN32 - SetConsoleOutputCP(CP_UTF8); - SetConsoleCP(CP_UTF8); -#endif - auto yml_dir = get_executable_dir() + "/../yml"; - YAML::Node origin = YAML::Load(read_file_content(yml_dir + "/origin.yaml")); - auto proxies = origin["proxies"]; - YAML::Node node = YAML::Load(read_file_content(yml_dir + "/base_ruler.yaml")); - YAML::Node another_rules = YAML::Load(read_file_content(yml_dir + "/another_rules.yaml")); - YAML::Node final_rules = another_rules["rules"]; - for (const auto& rule : origin["rules"]) { - final_rules.push_back(rule); - } - std::vector proxy_list; - YAML::Node groups_node(YAML::NodeType::Sequence); - groups_node.SetStyle(YAML::EmitterStyle::Block); - Atomic_Unit_Proxy_Group_Map props; - - - for (const auto& proxy : proxies) { - auto name = proxy["name"].as(); - if (contain(name, "剩余流量") || contain(name, "套餐到期")) { - continue; - } - proxy_list.push_back(name); - bool 日本 = contain(name, "日本"); - bool 日本东京 = contain(name, "日本东京"); - bool 美国 = contain(name, "美国"); - bool 美国圣何塞 = contain(name, "美国圣何塞"); - bool 美国阿什本 = contain(name, "美国阿什本"); - bool 新加坡 = contain(name, "新加坡"); - bool 倍001 = contain(name, "0.01"); - bool 倍01 = contain(name, "0.1"); - bool 电信联通移动推荐 = contain(name, "电信联通移动推荐"); - if (日本) props.push("日本", name); - if (日本东京) props.push("日本东京", name); - if (美国) props.push("美国", name); - if (美国圣何塞) props.push("美国圣何塞", name); - if (新加坡) props.push("新加坡", name); - if (美国阿什本) props.push("美国阿什本", name); - bool 下载 = 倍001 || 倍01; - if (美国 && 电信联通移动推荐) { - props.push("美国电信联通移动推荐", name); - } - if (下载) { - if (美国 && 倍001) { - props.push("美国0.01倍", name); - } else if (日本 && 倍01) { - props.push("日本0.1倍", name); - } else if (新加坡 && 倍01) { - props.push("新加坡0.1倍", name); - } else if (美国 && 倍01) { - props.push("美国0.1倍", name); - }else { - props.push("其他低倍率", name); - } - } - } - - auto base_props = props.atomic_unit_proxy_group_list(); - for (auto& prop : base_props) { - groups_node.push_back(make_url_test_group(prop, props.data[prop])); - } - - auto download = "📥下载"; - - - groups_node.push_back(make_fallback_group(download, {"🔥美国0.01倍", "🔥日本0.1倍", "🔥新加坡0.1倍", "🔥美国0.1倍", "🔥其他低倍率"}));; - groups_node.push_back(make_fallback_group("🤖AI网站", {"🔥美国圣何塞", "🔥美国电信联通移动推荐"})); - - - - std::vector proxy_group_list = {download, "DIRECT"}; - for (const auto& proxy_group : proxy_list) { - proxy_group_list.push_back(proxy_group); - } - groups_node.push_back(make_url_test_group("️♻️自动选择", proxy_group_list, false)); - groups_node.push_back(make_select_group("🚀节点选择", props.atomic_unit_proxy_group_list() + proxy_group_list)); - std::vector list2{"🚀节点选择"}; - for (const auto& li : proxy_group_list) { - list2.emplace_back(li); - } - groups_node.push_back(make_select_group("🎬媒体解锁", list2)); - groups_node.push_back(make_select_group("🐟漏网之鱼", list2)); - // groups_node.push_back(make_select_group("🤖AI网站", list2)); - groups_node.push_back(make_select_group("▶️油管视频", list2)); - groups_node.push_back(make_select_group("Ⓜ️微软服务", list2)); - - - node["proxies"] = proxies; - node["proxy-groups"] = groups_node; - node["rule-providers"] = another_rules["rule-providers"]; - node["sub-rules"] = another_rules["sub-rules"]; - node["rules"] = final_rules; - auto out_yml_dir = get_executable_dir() + "/../out_yml"; - const std::string output_group_path = out_yml_dir + "/groups.yaml"; - const std::string final_path = out_yml_dir + "/config.yaml"; - write_yaml_file(output_group_path, groups_node); - write_yaml_file(final_path, node); - std::cout << "输出完成: " << output_group_path << std::endl; - std::cout << "输出完成: " << final_path << std::endl; - return 0; -} \ No newline at end of file diff --git a/yml/base_ruler.yaml b/yml/base_ruler.yaml index 85c18a5..cc7f359 100644 --- a/yml/base_ruler.yaml +++ b/yml/base_ruler.yaml @@ -4,17 +4,33 @@ bind-address: '*' mode: rule log-level: info + + + + + +# ══ 外部控制(Web 面板)══════════════════════════════════════ external-controller: 0.0.0.0:7898 secret: "" - +external-ui: ./ui +external-ui-name: ui +external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" external-controller-cors: allow-private-network: true allow-origins: - '*' -external-ui: ./ui -external-ui-name: ui -external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" + + +# ══ GeoData 数据库地址 ════════════════════════════════════════ +geox-url: + geoip: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat" + geosite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat" + mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb" + + + + unified-delay: true tcp-concurrent: true @@ -56,4 +72,14 @@ tun: route-exclude-address: - 10.0.0.0/8 - 192.168.0.0/16 - - 127.0.0.0/8 \ No newline at end of file + - 127.0.0.0/8 + +# ── 机场订阅(有订阅的填这里,无订阅可删掉)────────────────── +proxy-providers: + 赔钱机场: + type: http + url: "https://dash.xn--cp3a08l.com/api/v1/pq/8424b1c22e1363a5906a156b2e849937?flag=meta" + interval: 86400 + path: ./proxy_providers/airport-a.yaml + health-check: { enable: true, url: "https://www.gstatic.com/generate_204", interval: 300, lazy: true } + exclude-filter: "(?i)(剩余|流量|到期|套餐|官网|公告)" diff --git a/yml/base_ruler2.yaml b/yml/base_ruler2.yaml new file mode 100644 index 0000000..85c18a5 --- /dev/null +++ b/yml/base_ruler2.yaml @@ -0,0 +1,59 @@ +mixed-port: 7897 +allow-lan: true +bind-address: '*' +mode: rule +log-level: info + +external-controller: 0.0.0.0:7898 +secret: "" + +external-controller-cors: + allow-private-network: true + allow-origins: + - '*' + +external-ui: ./ui +external-ui-name: ui +external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" + +unified-delay: true +tcp-concurrent: true +ipv6: false + +dns: + enable: true + ipv6: false + follow-rule: false + respect-rules: true + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + use-hosts: true + default-nameserver: + - 223.5.5.5 + - 119.29.29.29 + proxy-server-nameserver: + - https://dns.alidns.com/dns-query + - https://doh.pub/dns-query + nameserver: + - https://dns.alidns.com/dns-query + - https://doh.pub/dns-query + fallback: + - https://cloudflare-dns.com/dns-query + - https://dns.cloudflare.com/dns-query + - https://dns.alidns.com/dns-query + - https://doh.pub/dns-query + +tun: + enable: true + stack: gvisor + device: Mihomo + auto-route: true + auto-detect-interface: true + strict-route: false + dns-hijack: + - any:53 + mtu: 1500 + route-exclude-address: + - 10.0.0.0/8 + - 192.168.0.0/16 + - 127.0.0.0/8 \ No newline at end of file diff --git a/机场.http b/机场.http index 1851eca..3621390 100644 --- a/机场.http +++ b/机场.http @@ -15,7 +15,7 @@ Accept: */* -### Response code: 200 (OK); Time: 1682ms (1 s 682 ms); Content length: 160985 bytes (160.99 kB) +### Response code: 200 (OK); Time: 1682ms (1 s 682 ms); Content length: 160985 bytes (160.99 kB) 不支持您的代理软件 ### flag=clash GET {{host}}/api/v1/pq/{{token}}?flag=clash Accept: */*