Skip to content

Instantly share code, notes, and snippets.

@lifesign
Forked from YRLi904/mihomo-override.js
Created December 11, 2025 11:33
Show Gist options
  • Select an option

  • Save lifesign/4553002104fa44ca139c65ec225a2b42 to your computer and use it in GitHub Desktop.

Select an option

Save lifesign/4553002104fa44ca139c65ec225a2b42 to your computer and use it in GitHub Desktop.
mihomo-override
/*
前言
因为是自己用着修修补补
所以注释写得不是很全还请见谅
本脚本的主要目的是实现
1. 根据拥有的节点自动生成xx地区的节点组
2. 为落地节点自动添加中转节点组
3. 识别Fam关键词,建立家宽组
推荐搭配substore使用该脚本
多数mihomo系列客户端自身有带
可以先在yt等地学习一下如何使用
常用内容
https://raw.githubusercontent.com/Keywos/rule/main/rename.js
https://clashparty.org/docs/guide/override/yaml
https://wiki.metacubex.one/en/config/
颜值党推荐搭配
zashboard
同理 多数mihomo系列客户端自身也有带的
记得在设置里开启概览,那个连接拓扑真好看~
---
正文说明
主体参考自 Substore 订阅转换脚本
https://github.com/powerfullz/override-rules
DNS修改参考自 Linuxdo DNS防泄露配置
https://linux.do/t/topic/1061825
参数乱糟糟的 推荐直接在yaml里写好拿去用
传入参数:
- loadbalance: 启用负载均衡 (默认false)
- full: 启用完整配置,用于纯内核启动 (默认false)
- keepalive: 启用 tcp-keep-alive (默认false)
新加入的:
- cleanManual: 从"手动选择"中移除已有地区分组的节点 (默认true)
- landingRegions: 指定额外生成落地节点组的地区,用逗号分隔 (默认空)
示例: landingRegions=澳门,麻大,英国
默认总是检查的地区: 香港、美国、日本、狮城
如指定参数,则同时检查这些额外地区,仅当存在符合条件的节点时生成对应组
- landingKeywords: 自定义落地节点的识别关键词,用逗号分隔 (默认"落地")
示例: landingKeywords=Bage,Alice
说明: "落地"关键词始终默认启用,参数中指定的关键词会追加到默认值
若参数为空或不指定,仅使用默认的"落地"关键词进行识别
默认启用参数:
- IPv6 支持(始终启用)
- DNS FakeIP 模式(始终启用)
- 落地节点功能(始终启用)
- 中转节点前置代理功能(始终启用)
注意事项:
- 有些偏冷地区的家宽/节点可能因数量不足 (<=2个) 而未建立地区组
如需使用这些节点,请前往"手动选择"代理组进行设置
- 中转节点会自动识别并配置前置代理:
· 节点名包含"中转M"的节点使用"美国前置"作为前置代理
· 节点名包含"中转R"的节点使用"日本前置"作为前置代理
· 节点名包含"中转G"的节点使用"香港前置"作为前置代理
· 节点名包含"中转A"的节点使用"前置代理"作为前置代理
- 有些参数没有提,可以看看代码开头那一连串的常量
*/
const inArg = typeof $arguments !== 'undefined' ? $arguments : {};
const loadBalance = parseBool(inArg.loadbalance) || false,
fullConfig = parseBool(inArg.full) || false,
keepAliveEnabled = parseBool(inArg.keepalive) || false,
cleanManualEnabled = parseBool(inArg.cleanManual) || true,
landingRegionsInput = (inArg.landingRegions || '').trim(),
landingKeywordsInput = (inArg.landingKeywords || '落地,独享').trim();
const LOW_COST_KEYWORDS = "0\\.[0-5]|低倍率|省流|大流量|实验性";
const LOW_COST_REGEX = new RegExp(LOW_COST_KEYWORDS, "i");
const LOW_COST_AUTO_GROUP_NAME = "低倍率自动";
const LOW_COST_MANUAL_GROUP_NAME = "低倍率手动";
const CUSTOM_PROXIED_NAME = "自定义代理";
const CUSTOM_DIRECT_NAME = "自定义直连";
const CUSTOM_MATCH_NAME = "漏网之鱼";
const FRONT_PROXY_KEYWORDS = ["香港", "日本", "美国"];
// 下面的东西尽量别动,除非知道要干什么;更下面有可以修改的地方
// 中转节点前置代理映射
const RELAY_KEYWORDS = {
"中转M": "美国前置",
"中转R": "日本前置",
"中转G": "香港前置",
"中转A": "前置代理"
};
// 默认落地地区
const DEFAULT_LANDING_REGIONS = ["香港", "美国", "日本", "狮城"];
// 解析额外指定的落地地区
const extraLandingRegions = landingRegionsInput
? landingRegionsInput.split(',').map(r => r.trim()).filter(r => r && countriesMeta[r])
: [];
// 合并默认和额外地区,去重
const allLandingRegions = Array.from(new Set([...DEFAULT_LANDING_REGIONS, ...extraLandingRegions]));
// 解析落地节点识别关键词,支持多个关键词用逗号分隔
const userLandingKeywords = landingKeywordsInput
? landingKeywordsInput.split(',').map(k => k.trim()).filter(k => k)
: [];
const landingKeywords = ["落地", ...userLandingKeywords];
// 去重处理,避免重复的关键词
const uniqueLandingKeywords = Array.from(new Set(landingKeywords));
// 下面的配置可以自定义 但是记得前后保持一致性
const ruleProviders = {
"FakeipFilter": {
"url": "https://cdn.jsdelivr.net/gh/juewuy/ShellCrash@dev/public/fake_ip_filter.list",
"path": "./ruleset/FakeipFilter.list",
"behavior": "domain", "interval": 86400, "format": "text", "type": "http"
},
"ADBlock": {
"type": "http", "behavior": "domain", "format": "text", "interval": 86400,
"url": "https://adrules.top/adrules_domainset.txt",
"path": "./ruleset/ADBlock.txt"
},
"AI": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://ruleset.skk.moe/Clash/non_ip/ai.txt",
"path": "./ruleset/AI.txt"
},
"TikTok": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/TikTok.list",
"path": "./ruleset/TikTok.list"
},
"EHentai": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/EHentai.list",
"path": "./ruleset/EHentai.list"
},
"SteamFix": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/SteamFix.list",
"path": "./ruleset/SteamFix.list"
},
"GoogleFCM": {
"type": "http", "behavior": "classical", "interval": 86400, "format": "text",
"path": "./ruleset/FirebaseCloudMessaging.list",
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/FirebaseCloudMessaging.list",
},
"AdditionalFilter": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/AdditionalFilter.list",
"path": "./ruleset/AdditionalFilter.list"
},
"Crypto": {
"type": "http", "behavior": "classical", "format": "text", "interval": 86400,
"url": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/ruleset/Crypto.list",
"path": "./ruleset/Crypto.list"
}
}
const rules = [
"DOMAIN-SUFFIX,ping0.cc,AI",
"DOMAIN-SUFFIX,ipdata.co,AI",
"DOMAIN-SUFFIX,pingip.cn,AI",
// 把一些ip检查地址丢给AI组了
"RULE-SET,ADBlock,广告拦截",
"RULE-SET,AdditionalFilter,广告拦截",
"RULE-SET,AI,AI",
"RULE-SET,Crypto,Crypto",
"RULE-SET,EHentai,E-Hentai",
"RULE-SET,TikTok,TikTok",
"RULE-SET,SteamFix,DIRECT",
"RULE-SET,GoogleFCM,DIRECT",
"GEOSITE,GOOGLE-PLAY@CN,DIRECT",
"GEOSITE,TELEGRAM,Telegram",
"GEOSITE,YOUTUBE,YouTube",
"GEOSITE,NETFLIX,Netflix",
"GEOSITE,SPOTIFY,Spotify",
"GEOSITE,BAHAMUT,Bahamut",
"GEOSITE,BILIBILI,Bilibili",
"GEOSITE,MICROSOFT@CN,DIRECT",
"GEOSITE,PIKPAK,PikPak",
"GEOSITE,GOOGLE,Google",
"GEOSITE,GFW,选择节点",
"GEOSITE,CN,DIRECT",
"GEOSITE,PRIVATE,DIRECT",
"GEOIP,NETFLIX,Netflix,no-resolve",
"GEOIP,TELEGRAM,Telegram,no-resolve",
"GEOIP,CN,DIRECT",
"GEOIP,PRIVATE,DIRECT",
"DST-PORT,22,SSH",
"MATCH,漏网之鱼"
];
const snifferConfig = {
"sniff": {
"TLS": {
"ports": [443, 8443],
},
"HTTP": {
"ports": [80, 8080, 8880],
},
"QUIC": {
"ports": [443, 8443],
}
},
"override-destination": false,
"enable": true,
"force-dns-mapping": true,
"skip-domain": [
"Mijia Cloud",
"dlg.io.mi.com",
"+.push.apple.com"
]
};
const dnsConfig = {
"enable": true,
"respect-rules": true,
// 让连接DNS服务器时能够遵循规则,也就是通过代理节点连接到上面设置的国外DNS,因为直连可能连接不上
"ipv6": true,
"prefer-h3": true,
"enhanced-mode": "fake-ip",
"fake-ip-filter": [
"rule-set:FakeipFilter",
"dig.io.mi.com",
],
"default-nameserver": [
// 基础DNS服务器,必须先有它才能使用域名类型的DNS配置,因此必须为IP\
"119.29.29.29",
"223.5.5.5",
],
"nameserver": [
// 在访问的国外网站没有匹配到任何域名规则,最终走了MATCH兜底规则时使用的DNS,为了确保不发生DNS泄漏和DNS污染,必须使用国外DNS
"https://cloudflare-dns.com/dns-query",
"https://dns.google/dns-query",
],
"direct-nameserver": [
// 规则匹配到直连时使用的DNS,既然是可以直连的,当然要用国内DNS
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
],
"proxy-server-nameserver": [
// 解析代理节点时使用的DNS,为了使respect-rules生效必须设置,最好设置为国内DNS,否则在代理的第一步就可能因为连接不上国外DNS而解析不出代理节点的IP,最终倒在起点
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
]
};
const geoxURL = {
"geoip": "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat",
"geosite": "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat",
"mmdb": "https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb",
"asn": "https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/GeoLite2-ASN.mmdb"
};
// 地区元数据
const countriesMeta = {
"香港": {
pattern: "(?i)香港|港|HK|hk|Hong Kong|HongKong|hongkong|🇭🇰",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Hong_Kong.png"
},
"澳门": {
pattern: "(?i)澳门|MO|Macau|🇲🇴",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Macao.png"
},
"台湾": {
pattern: "(?i)台|新北|彰化|TW|Taiwan|🇹🇼",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Taiwan.png"
},
"狮城": {
pattern: "(?i)新加坡|坡|狮城|SG|Singapore|🇸🇬",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Singapore.png"
},
"日本": {
pattern: "(?i)日本|川日|东京|大阪|泉日|埼玉|沪日|深日|JP|Japan|🇯🇵",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Japan.png"
},
"韩国": {
pattern: "(?i)KR|Korea|KOR|首尔|韩|韓|🇰🇷",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Korea.png"
},
"美国": {
pattern: "(?i)美国|美|US|United States|🇺🇸",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/United_States.png"
},
"麻大": {
pattern: "(?i)加拿大|Canada|CA|🇨🇦",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Canada.png"
},
"英国": {
pattern: "(?i)英国|United Kingdom|UK|伦敦|London|🇬🇧",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/United_Kingdom.png"
},
"澳洲": {
pattern: "(?i)澳洲|澳大利亚|AU|Australia|🇦🇺",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Australia.png"
},
"德国": {
pattern: "(?i)德国|德|DE|Germany|🇩🇪",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Germany.png"
},
"法国": {
pattern: "(?i)法国|法|FR|France|🇫🇷",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/France.png"
},
"俄联": {
pattern: "(?i)俄罗斯|俄|RU|Russia|🇷🇺",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Russia.png"
},
"泰国": {
pattern: "(?i)泰国|泰|TH|Thailand|🇹🇭",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Thailand.png"
},
"印度": {
pattern: "(?i)印度|IN|India|🇮🇳",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/India.png"
},
"马来": {
pattern: "(?i)马来西亚|马来|MY|Malaysia|🇲🇾",
icon: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Malaysia.png"
},
};
// 此处我因为使用了substore格式化了节点名称,故而进行了个性化调整
// 预编译地区匹配规则,国旗优先,其次按文本/代码匹配
const FLAG_SEQUENCE_REGEX = /[\u{1F1E6}-\u{1F1FF}]{2}/gu;
const countryMatchers = Object.freeze(Object.entries(countriesMeta).reduce((acc, [country, meta]) => {
const patternBody = meta.pattern.replace(/^\(\?i\)/, '');
const flags = patternBody.match(FLAG_SEQUENCE_REGEX) || [];
acc[country] = {
flags,
regex: new RegExp(patternBody, "i")
};
return acc;
}, {}));
function matchCountryFromName(name, candidateCountries) {
if (!name) return null;
const candidates = (candidateCountries && candidateCountries.length
? candidateCountries
: Object.keys(countryMatchers)).filter(country => countryMatchers[country]);
for (const country of candidates) {
const matcher = countryMatchers[country];
if (matcher.flags.length && matcher.flags.some(flag => name.includes(flag))) {
return country;
}
}
for (const country of candidates) {
const matcher = countryMatchers[country];
if (matcher.regex.test(name)) {
return country;
}
}
return null;
}
// ==================== 工具函数 ====================
function parseBool(value) {
if (typeof value === "boolean") return value;
if (typeof value === "string") {
return value.toLowerCase() === "true" || value === "1";
}
return false;
}
// 检查节点是否为落地节点
function isLandingNode(nodeName) {
return uniqueLandingKeywords.some(keyword => nodeName.includes(keyword));
}
// 构建落地节点排斥模式(用于正则表达式)
function buildLandingExcludePattern() {
return uniqueLandingKeywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
}
function identifyRelayNodes(proxies) {
// 为中转节点添加 dialer-proxy 字段
for (const proxy of proxies) {
const name = proxy.name || '';
// 按照优先级匹配:先匹配具体地区,最后匹配通用中转
if (name.includes("中转M")) {
proxy["dialer-proxy"] = "美国前置";
} else if (name.includes("中转R")) {
proxy["dialer-proxy"] = "日本前置";
} else if (name.includes("中转G")) {
proxy["dialer-proxy"] = "香港前置";
} else if (name.includes("中转")) {
proxy["dialer-proxy"] = "前置代理";
}
}
}
// ==================== 数据解析函数 ====================
function parseLandingNodesByCountry(config, targetCountries) {
// 解析落地节点,并按地区分类
// 返回: { country: [nodeNames...] } 的对象
const proxies = config.proxies || [];
const landingByCountry = Object.create(null);
// 逐个节点进行匹配与统计
for (const proxy of proxies) {
const name = proxy.name || '';
// 检查是否为落地节点
if (!isLandingNode(name)) continue;
const matchedCountry = matchCountryFromName(name, targetCountries);
// 如果找到匹配的地区,添加到结果中
if (matchedCountry) {
if (!landingByCountry[matchedCountry]) {
landingByCountry[matchedCountry] = [];
}
landingByCountry[matchedCountry].push(name);
}
}
return landingByCountry;
}
function hasLowCost(config) {
// 检查是否有低倍率节点
const proxies = config["proxies"];
for (const proxy of proxies) {
if (LOW_COST_REGEX.test(proxy.name)) {
return true;
}
}
return false;
}
function parseCountries(config) {
const proxies = config.proxies || [];
// 用来累计各国节点数
const countryCounts = Object.create(null);
const famCounts = Object.create(null); // 家宽节点计数
const selfBuiltCounts = Object.create(null); // 自建节点计数
const manualCounts = Object.create(null); // 手动组节点计数
const allCountries = Object.keys(countryMatchers);
// 逐个节点进行匹配与统计
for (const proxy of proxies) {
const name = proxy.name || '';
const isFam = /Fam/i.test(name); // 检查是否为家宽节点
const isSelfBuilt = /自建/i.test(name); // 检查是否为自建节点
const isLanding = isLandingNode(name); // 检查是否为落地节点
const matchedCountry = matchCountryFromName(name, allCountries);
if (!matchedCountry) continue;
// 常规地区节点统计(排除Fam\自建\落地)
if (!isFam && !isSelfBuilt) {
countryCounts[matchedCountry] = (countryCounts[matchedCountry] || 0) + 1;
}
// 家宽节点单独统计
if (isFam) {
famCounts[matchedCountry] = (famCounts[matchedCountry] || 0) + 1;
}
// 自建节点单独统计
if (isSelfBuilt) {
selfBuiltCounts[matchedCountry] = (selfBuiltCounts[matchedCountry] || 0) + 1;
}
// 手动组节点统计(包含所有节点)
if (!isLanding) {
manualCounts[matchedCountry] = (manualCounts[matchedCountry] || 0) + 1;
}
}
// 将结果对象转成数组形式
const result = [];
for (const [country, count] of Object.entries(countryCounts)) {
result.push({ country, count });
}
const famResult = [];
for (const [country, count] of Object.entries(famCounts)) {
famResult.push({ country, count });
}
const selfBuiltResult = [];
for (const [country, count] of Object.entries(selfBuiltCounts)) {
selfBuiltResult.push({ country, count });
}
const manualResult = [];
for (const [country, count] of Object.entries(manualCounts)) {
manualResult.push({ country, count });
}
return {
regular: result,
fam: famResult,
selfBuilt: selfBuiltResult,
manual: manualResult
};
}
// ==================== 列表构建函数 ====================
function buildBaseLists({ manualInfo, selfBuiltInfo, lowCost, famInfo, countryInfo, landingNodesByCountry }) {
const countryGroupNames = countryInfo
.filter(item => item.count > 2)
.map(item => item.country + "节点");
const famGroupNames = famInfo
.filter(item => item.count > 0)
.map(item => item.country + "家宽");
const famAvailableGroupNames = famInfo
.filter(item => item.count > 0)
.map(item => item.country + "家宽可用");
const selfBuiltGroupNames = selfBuiltInfo
.filter(item => item.count > 0)
.map(item => item.country + "自建");
const manualGroupNames = manualInfo
.filter(item => item.count > 2)
.map(item => item.country + "手动");
// 落地地区组名称
const landingCountryGroupNames = landingNodesByCountry
? Object.keys(landingNodesByCountry)
.filter(country => landingNodesByCountry[country] && landingNodesByCountry[country].length > 0)
.map(country => country + "落地")
: [];
const selector = ["故障转移", "落地节点", "手动选择", "DIRECT"];
selector.push(...selfBuiltGroupNames);
selector.push(...landingCountryGroupNames);
selector.push(...famAvailableGroupNames);
selector.push(...famGroupNames);
selector.push(...countryGroupNames);
selector.push(...manualGroupNames);
if (lowCost) {
selector.push(LOW_COST_AUTO_GROUP_NAME);
selector.push(LOW_COST_MANUAL_GROUP_NAME);
}
const defaultProxies = ["选择节点", "落地节点", "手动选择", "DIRECT"];
defaultProxies.push(...selfBuiltGroupNames);
defaultProxies.push(...landingCountryGroupNames);
defaultProxies.push(...famAvailableGroupNames);
defaultProxies.push(...famGroupNames);
defaultProxies.push(...countryGroupNames);
defaultProxies.push(...manualGroupNames);
if (lowCost) {
defaultProxies.push(LOW_COST_AUTO_GROUP_NAME);
defaultProxies.push(LOW_COST_MANUAL_GROUP_NAME);
}
const defaultProxiesDirect = ["选择节点", "手动选择", "DIRECT"];
defaultProxiesDirect.push(...selfBuiltGroupNames);
defaultProxiesDirect.push(...landingCountryGroupNames);
defaultProxiesDirect.push(...famAvailableGroupNames);
defaultProxiesDirect.push(...famGroupNames);
defaultProxiesDirect.push(...countryGroupNames);
defaultProxiesDirect.push(...manualGroupNames);
if (lowCost) {
defaultProxiesDirect.push(LOW_COST_AUTO_GROUP_NAME);
defaultProxiesDirect.push(LOW_COST_MANUAL_GROUP_NAME);
}
const defaultFallback = ["落地节点"];
defaultFallback.push(...selfBuiltGroupNames);
defaultFallback.push(...landingCountryGroupNames);
defaultFallback.push(...famAvailableGroupNames);
defaultFallback.push(...famGroupNames);
defaultFallback.push(...countryGroupNames);
defaultFallback.push(...manualGroupNames);
if (lowCost) {
defaultFallback.push(LOW_COST_AUTO_GROUP_NAME);
defaultFallback.push(LOW_COST_MANUAL_GROUP_NAME);
}
defaultFallback.push("手动选择");
defaultFallback.push("DIRECT");
return {
defaultProxies,
defaultProxiesDirect,
defaultSelector: selector,
defaultFallback,
countryGroupNames,
landingCountryGroupNames,
famGroupNames,
selfBuiltGroupNames,
manualGroupNames
};
}
// ==================== 代理组构建函数 ====================
function buildCountryProxyGroups(countryList) {
// 获取实际存在的地区列表
const countryProxyGroups = [];
const landingExcludePattern = buildLandingExcludePattern();
// 为实际存在的地区创建节点组
for (const country of countryList) {
// 确保地区名称在预设的地区配置中存在
if (countriesMeta[country]) {
const groupName = `${country}节点`;
const pattern = countriesMeta[country].pattern;
// 构建排除过滤器:排除 Fam、落地节点、低倍率节点
const excludePattern = `(?i)Fam|${landingExcludePattern}|${LOW_COST_KEYWORDS}`;
const groupConfig = {
"name": groupName,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": pattern,
"exclude-filter": excludePattern,
"type": (loadBalance) ? "load-balance" : "url-test",
"hidden": true // 隐藏节点列表,用户无需看到内部节点
};
if (!loadBalance) {
Object.assign(groupConfig, {
"url": "https://cp.cloudflare.com/generate_204",
"interval": 60,
"tolerance": 20,
"lazy": false
});
}
countryProxyGroups.push(groupConfig);
}
}
return countryProxyGroups;
}
function buildFamProxyGroups(famCountryList) {
const famProxyGroups = [];
for (const country of famCountryList) {
if (countriesMeta[country]) {
const pattern = countriesMeta[country].pattern;
const filterPattern = `(?i)(?=.*Fam)(?=.*(?:${pattern.replace(/^\(\?i\)/, '')}))`; // 同时匹配Fam和地区
// 1. URLTEST 子组 (选延迟最低)
const urltestSubGroupConfig = {
"name": `${country}-01-URLTEST`,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": filterPattern,
"type": "url-test",
"url": "https://cp.cloudflare.com/generate_204",
"interval": 180,
"tolerance": 20,
"lazy": false,
"hidden": true // 隐藏节点列表
};
// 2. FALLBACK 子组 (逐个容错)
const fallbackSubGroupConfig = {
"name": `${country}-02-FALLBACK`,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": filterPattern,
"type": "fallback",
"url": "https://cp.cloudflare.com/generate_204",
"interval": 180,
"tolerance": 20,
"lazy": false,
"hidden": true // 隐藏节点列表
};
// 3. 家宽可用组 (fallback,优先 URLTEST,URLTEST 全挂才用 FALLBACK)
const availableGroupConfig = {
"name": `${country}家宽可用`,
"icon": countriesMeta[country].icon,
"type": "fallback",
"url": "https://cp.cloudflare.com/generate_204",
"interval": 180,
"tolerance": 20,
"lazy": false,
"hidden": true, // 隐藏节点列表
"proxies": [`${country}-01-URLTEST`, `${country}-02-FALLBACK`]
};
// 4. 家宽手动组 (select 手动选择)
const selectGroupConfig = {
"name": `${country}家宽`,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": filterPattern,
"type": "select",
"proxies": [`${country}家宽可用`] // 首选自动选择组
};
famProxyGroups.push(urltestSubGroupConfig, fallbackSubGroupConfig, availableGroupConfig, selectGroupConfig);
}
}
return famProxyGroups;
}
function buildManualProxyGroups(manualCountryList) {
// 为手动组创建地区分组(selector类型,包含所有节点)
const manualProxyGroups = [];
for (const country of manualCountryList) {
if (countriesMeta[country]) {
const groupName = `${country}手动`;
const pattern = countriesMeta[country].pattern;
const groupConfig = {
"name": groupName,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": pattern,
"exclude-filter": "(?i)Fam", // 仅排除家宽节点,允许自建和落地节点
"type": "select"
};
manualProxyGroups.push(groupConfig);
}
}
return manualProxyGroups;
}
function buildSelfBuiltProxyGroups(selfBuiltCountryList) {
// 为自建节点创建地区分组(selector类型,包含所有自建节点)
const selfBuiltProxyGroups = [];
for (const country of selfBuiltCountryList) {
if (countriesMeta[country]) {
const groupName = `${country}自建`;
const pattern = countriesMeta[country].pattern;
const filterPattern = `(?i)(?=.*自建)(?=.*(?:${pattern.replace(/^\(\?i\)/, '')}))`; // 同时匹配"自建"和地区
const groupConfig = {
"name": groupName,
"icon": countriesMeta[country].icon,
"include-all": true,
"filter": filterPattern,
"type": "select"
};
selfBuiltProxyGroups.push(groupConfig);
}
}
return selfBuiltProxyGroups;
}
function buildCustomGroupProxies(defaultSelector) {
const proxies = Array.isArray(defaultSelector) ? defaultSelector.filter(Boolean) : [];
const ordered = ["DIRECT", ...proxies.filter(name => name !== "DIRECT")];
return ordered;
}
function buildRegionalFrontProxyGroups(countryGroupNames, frontProxySelector) {
// 构建三个地域前置代理组:美国前置、日本前置、香港前置
// 本质上复制前置代理组的选项,但如果有对应地区的节点组则优先选择
const regionalFrontGroups = [];
const regions = ["美国", "日本", "香港"];
for (const region of regions) {
const regionalNodeGroup = `${region}节点`;
// 过滤出不重复的proxies:排除该地区已经存在的节点组
let proxies = frontProxySelector.filter(name => name !== regionalNodeGroup);
// 如果存在该地区的节点组,将其置于首位作为默认选择
if (countryGroupNames.includes(regionalNodeGroup)) {
proxies.unshift(regionalNodeGroup);
}
regionalFrontGroups.push({
"name": `${region}前置`,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Area.png",
"type": "select",
"proxies": proxies
});
}
return regionalFrontGroups;
}
// ==================== 主代理组构建函数 ====================
function buildProxyGroups({
countryList,
countryProxyGroups,
famProxyGroups,
selfBuiltProxyGroups,
manualProxyGroups,
lowCost,
defaultProxies,
defaultProxiesDirect,
defaultSelector,
defaultFallback,
cleanManual,
countryGroupNames,
famGroupNames,
selfBuiltGroupNames,
landingNodesByCountry
}) {
// 查看是否有特定地区的节点
const hasTW = countryList.includes("台湾");
const hasHK = countryList.includes("香港");
const hasUS = countryList.includes("美国");
// 排除落地节点、选择节点和故障转移以避免死循环,并限制可作为前置的地区
const frontProxyCandidates = defaultSelector
.filter(name => name !== "落地节点" && name !== "故障转移");
const filteredFrontProxyCandidates = frontProxyCandidates
.filter(name => FRONT_PROXY_KEYWORDS.some(keyword => name.includes(keyword)));
const frontProxySelector = filteredFrontProxyCandidates.length > 0
? filteredFrontProxyCandidates
: frontProxyCandidates;
// 构建手动选择的排除过滤器
let manualSelectConfig = {
"name": "手动选择",
"icon": "https://cdn.jsdelivr.net/gh/shindgewongxj/WHATSINStash@master/icon/select.png",
"include-all": true,
"type": "select"
};
// 如果启用 cleanManual,构建排除已有地区分组节点的过滤器
if (cleanManual) {
const excludePatterns = [];
// 收集所有已建立地区组的地区正则(包括常规地区组、家宽组、自建组)
const allCountries = new Set([
...countryList, // 常规地区组的地区
...famGroupNames.map(n => n.replace(/家宽$/, '')), // 家宽组的地区
...selfBuiltGroupNames.map(n => n.replace(/自建$/, '')) // 自建组的地区
]);
for (const country of allCountries) {
if (countriesMeta[country]) {
const pattern = countriesMeta[country].pattern.replace(/^\(\?i\)/, '');
excludePatterns.push(`(?:${pattern})`);
}
}
// 构建排除正则:排除所有已有地区组的节点和落地节点
if (excludePatterns.length > 0) {
const combinedPattern = `(?i)(?=.*(?:${excludePatterns.join('|')}))(?!.*(?:${LOW_COST_KEYWORDS}))`;
manualSelectConfig["exclude-filter"] = combinedPattern;
}
// 同时排除落地节点
const landingExcludePattern = buildLandingExcludePattern();
if (manualSelectConfig["exclude-filter"]) {
manualSelectConfig["exclude-filter"] = `(?:${manualSelectConfig["exclude-filter"]})|(?i)${landingExcludePattern}`;
} else {
manualSelectConfig["exclude-filter"] = `(?i)${landingExcludePattern}`;
}
}
const customGroupProxies = buildCustomGroupProxies(defaultSelector);
// 构建地域前置代理组
const regionalFrontProxyGroups = buildRegionalFrontProxyGroups(
countryGroupNames,
frontProxySelector
);
// 构建分地区的落地节点组
const landingCountryGroups = [];
if (landingNodesByCountry && typeof landingNodesByCountry === 'object') {
for (const [country, nodes] of Object.entries(landingNodesByCountry)) {
if (nodes && nodes.length > 0 && countriesMeta[country]) {
landingCountryGroups.push({
"name": `${country}落地`,
"icon": countriesMeta[country].icon,
"type": "select",
"proxies": nodes
});
}
}
}
return [
{
"name": "选择节点",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png",
"type": "select",
"proxies": defaultSelector
},
{
"name": CUSTOM_PROXIED_NAME,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png",
"type": "select",
"proxies": customGroupProxies
},
{
"name": CUSTOM_DIRECT_NAME,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png",
"type": "select",
"proxies": customGroupProxies
},
manualSelectConfig,
...regionalFrontProxyGroups,
{
"name": "前置代理",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Area.png",
"type": "select",
"exclude-filter": `(?i)Fam|${buildLandingExcludePattern()}`,
"proxies": frontProxySelector
},
...landingCountryGroups,
{
"name": "落地节点",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Airport.png",
"type": "select",
"include-all": true,
"filter": `(?i)${buildLandingExcludePattern()}`,
},
{
"name": "故障转移",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Bypass.png",
"type": "fallback",
"url": "https://cp.cloudflare.com/generate_204",
"proxies": defaultFallback,
"interval": 180,
"tolerance": 20,
"lazy": false
},
{
"name": "AI",
"icon": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/icons/chatgpt.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "Google",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Google_Search.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "Telegram",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Telegram.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "YouTube",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/YouTube.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "Bilibili",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/bilibili.png",
"type": "select",
"proxies": (hasTW && hasHK) ? ["直连", "台湾节点", "香港节点"] : defaultProxiesDirect
},
{
"name": "Netflix",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Netflix.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "Spotify",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Spotify.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "TikTok",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/TikTok.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "E-Hentai",
"icon": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/icons/Ehentai.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "PikPak",
"icon": "https://cdn.jsdelivr.net/gh/powerfullz/override-rules@master/icons/PikPak.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "Bahamut",
"icon": "https://cdn.jsdmirror.com/gh/Koolson/Qure@master/IconSet/Color/Bahamut.png",
"type": "select",
"proxies": (hasTW) ? ["台湾节点", "选择节点", "手动选择", "直连"] : defaultProxies
},
{
"name": "Crypto",
"icon": "https://cdn.jsdmirror.com/gh/Koolson/Qure@master/IconSet/Color/Cryptocurrency_3.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "SSH",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Server.png",
"type": "select",
"proxies": defaultProxies
},
{
"name": "直连",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Direct.png",
"type": "select",
"proxies": [
"DIRECT", "选择节点"
]
},
{
"name": "广告拦截",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/AdBlack.png",
"type": "select",
"proxies": [
"REJECT", "直连"
]
},
...(lowCost ? [
{
"name": LOW_COST_AUTO_GROUP_NAME,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Lab.png",
"type": "url-test",
"url": "https://cp.cloudflare.com/generate_204",
"include-all": true,
"filter": `(?i)${LOW_COST_KEYWORDS}`,
"hidden": false // 低倍率自动组保持可见,便于确认状态
},
{
"name": LOW_COST_MANUAL_GROUP_NAME,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Lab.png",
"include-all": true,
"filter": `(?i)${LOW_COST_KEYWORDS}`,
"type": "select",
"hidden": false // 低倍率手动组同样可见,方便手动挑选
}
] : []),
...countryProxyGroups,
...selfBuiltProxyGroups,
...famProxyGroups,
...manualProxyGroups,
{
"name": CUSTOM_MATCH_NAME,
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Proxy.png",
"type": "select",
"proxies": customGroupProxies
}
].filter(Boolean); // 过滤掉 null 值
}
// ==================== 主函数 ====================
function main(config) {
config = { proxies: config.proxies };
// 解析地区、家宽、自建、手动组与低倍率信息
const { regular: countryInfo, fam: famInfo, selfBuilt: selfBuiltInfo, manual: manualInfo } = parseCountries(config);
const lowCost = hasLowCost(config);
// 解析落地节点,按地区分类(需要在buildBaseLists前进行,以便传入落地信息)
const landingNodesByCountry = parseLandingNodesByCountry(config, allLandingRegions);
// 构建基础数组
const {
defaultProxies,
defaultProxiesDirect,
defaultSelector,
defaultFallback,
countryGroupNames: targetCountryList,
landingCountryGroupNames: targetLandingCountryList,
famGroupNames: targetFamList,
selfBuiltGroupNames: targetSelfBuiltList,
manualGroupNames: targetManualList
} = buildBaseLists({ lowCost, countryInfo, famInfo, selfBuiltInfo, manualInfo, landingNodesByCountry });
// 为地区构建对应的 url-test / load-balance 组
const countryProxyGroups = buildCountryProxyGroups(targetCountryList.map(n => n.replace(/节点$/, '')));
// 为家宽节点构建地区分组
const famProxyGroups = buildFamProxyGroups(targetFamList.map(n => n.replace(/家宽$/, '')));
// 为自建节点构建地区分组
const selfBuiltProxyGroups = buildSelfBuiltProxyGroups(targetSelfBuiltList.map(n => n.replace(/自建$/, '')));
// 为手动组构建地区分组
const manualProxyGroups = buildManualProxyGroups(targetManualList.map(n => n.replace(/手动$/, '')));
// 生成代理组
const proxyGroups = buildProxyGroups({
countryList: targetCountryList.map(n => n.replace(/节点$/, '')),
countryProxyGroups,
famProxyGroups,
selfBuiltProxyGroups,
manualProxyGroups,
lowCost,
defaultProxies,
defaultProxiesDirect,
defaultSelector,
defaultFallback,
cleanManual: cleanManualEnabled,
countryGroupNames: targetCountryList,
famGroupNames: targetFamList,
selfBuiltGroupNames: targetSelfBuiltList,
landingNodesByCountry
});
// 为中转节点添加 dialer-proxy 字段
identifyRelayNodes(config.proxies);
const globalProxies = proxyGroups.map(item => item.name);
proxyGroups.push(
{
"name": "GLOBAL",
"icon": "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Global.png",
"include-all": true,
"type": "select",
"proxies": globalProxies
}
);
if (fullConfig) Object.assign(config, {
"mixed-port": 7890,
"redir-port": 7892,
"tproxy-port": 7893,
"routing-mark": 7894,
"allow-lan": true,
"ipv6": true,
"mode": "rule",
"unified-delay": true,
"tcp-concurrent": true,
"find-process-mode": "off",
"log-level": "info",
"geodata-loader": "standard",
"external-controller": ":9999",
"disable-keep-alive": !keepAliveEnabled,
"profile": {
"store-selected": true,
}
});
Object.assign(config, {
"proxy-groups": proxyGroups,
"rule-providers": ruleProviders,
"rules": rules,
"sniffer": snifferConfig,
"dns": dnsConfig,
"geodata-mode": true,
"geox-url": geoxURL,
});
return config;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment