[PATCHED - 告别DNS泄露] singbox一样的丝滑的Clash覆写脚本

2026-04-11 14:191阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

本脚本已停止维护,建议移步新帖

[Mihomo覆写] DNS泄露 & AI分流 の 最终解决方案 —— sing-mix 开发调优
往期: v1 v2 序 折腾来折腾去,现在看来之前两版根本不能看(死慢的rule provider外加各类史山)。秉着“事不过三”原则,把最终版(?)在这里发了,日后若更新亦避免开新帖,将脚本托管到github了 ↓↓↓ https://raw.githubusercontent.com/Sakyvo/sing-mix/refs/heads/main/sing-mix_lite ↑↑↑ …

从 [NO DNS LEAK] 如何让mihomo拥有singbox一样的丝滑体验 - Clash覆写脚本分享 继续讨论

上个脚本居然把googlecn放在了ads前面导致广告没被屏蔽反而走直连了,奈何ublock origin过于强大一周了也没发现问题,闲来无事看下连接日志才发现不对劲,直接进行一个修复。顺带出了一个精简版本,因为发现各种HK TW SG什么的区域分组对个人而言用处不是很大就删掉了,只留全部和AI分组


完整版

function main(config) { // ==================== // 0. 手动维护的直连域名 // ==================== const bypassDomains = [ "example.com", "none.com" ]; // ==================== // 1. 常量配置 // ==================== const SETTINGS = { ICON_BASE: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/", RULE_BASE: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/", TEST_URL: "https://www.gstatic.com/generate_204", REGION_ORDER: ["HK", "TW", "SG", "JP", "KR", "AS", "US"], URL_TEST_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FALLBACK_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FILTER_REGEX: /群|邀请|返利|官网|官方|网址|订阅|购买|续费|剩余|到期|过期|流量|备用|邮箱|客服|联系|工单|倒卖|防止|梯子|tg|telegram|电报|发布|重置/i, BASIC_FAKE_IP_FILTER: [ "*.lan", "+.lan", "*.local", "+.local", "+.localdomain", "+.home.arpa", "+.msftconnecttest.com", "+.msftncsi.com", "+.gstatic.com", "connectivitycheck.gstatic.com", "+.captive.apple.com", "time.*.com", "time.*.gov", "ntp.*.com", "ntp.*.org", "pool.ntp.org", "+.pool.ntp.org", "+.stun.*.*", "+.stun.*.*.*", "+.stun.*.*.*.*", "localhost.ptlogin2.qq.com", "WORKGROUP" ], FORCE_DOMAIN: [ "+.openai.com", "+.chat.com", "+.chatgpt.com", "+.oaistatic.com", "+.oaiusercontent.com", "+.sora.com", "+.anthropic.com", "+.claude.ai", "+.claude.com", "+.claudeusercontent.com", "+.gemini.google.com", "+.aistudio.google.com", "+.generativelanguage.googleapis.com", "+.makersuite.google.com", "+.notebooklm.google.com" ], DIRECT_FIX_RULES: [ "DOMAIN-SUFFIX,ol.epicgames.com,DIRECT", "DOMAIN-SUFFIX,dizhensubao.getui.com,DIRECT", "DOMAIN-SUFFIX,tracking-protection.cdn.mozilla.net,DIRECT", "DOMAIN,origin-a.akamaihd.net,DIRECT", "DOMAIN,fairplay.l.qq.com,DIRECT", "DOMAIN,livew.l.qq.com,DIRECT", "DOMAIN,vd.l.qq.com,DIRECT", "DOMAIN,errlog.umeng.com,DIRECT", "DOMAIN,msg.umeng.com,DIRECT", "DOMAIN,msg.umengcloud.com,DIRECT", "DOMAIN,tracking.miui.com,DIRECT", "DOMAIN,app.adjust.com,DIRECT", "DOMAIN,bdtj.tagtic.cn,DIRECT", "DOMAIN,rewards.hypixel.net,DIRECT", "DOMAIN-SUFFIX,koodomobile.com,DIRECT", "DOMAIN-SUFFIX,koodomobile.ca,DIRECT" ] }; // ==================== // 2. 基础工具 // ==================== const uniq = (arr = []) => [...new Set(arr.filter(Boolean))]; const escapeRegex = (s = "") => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const normalizeName = (name = "") => String(name) .replace(/(IEPL|IPLC|BGP|RELAY|PRO|V\d+)/ig, " $1 ") .replace(/[【】\[\]()()|_\-.,/:~]/g, " ") .replace(/🇭🇰/g, " HK ") .replace(/🇹🇼/g, " TW ") .replace(/🇸🇬/g, " SG ") .replace(/🇯🇵/g, " JP ") .replace(/🇰🇷/g, " KR ") .replace(/🇻🇳|🇹🇭|🇲🇾|🇮🇩|🇵🇭/g, " AS ") .replace(/🇺🇸/g, " US ") .toUpperCase() .replace(/\s+/g, " ") .trim(); const buildRegex = (arr = []) => { const patterns = arr.map((raw) => { const token = String(raw).trim().toUpperCase(); const escaped = escapeRegex(token); if (/^[A-Z]{2,3}$/.test(token)) return `(?:^|[^A-Z])${escaped}(?:[^A-Z]|$)`; return escaped; }); return new RegExp(patterns.join("|"), "i"); }; const buildRegions = () => ([ { name: "HK", pattern: ["香港", "HK", "HKG", "HONGKONG", "HONG KONG"], icon: "Hong_Kong.png" }, { name: "TW", pattern: ["台湾", "台北", "新北", "TW", "TWN", "TAIWAN", "TAIPEI", "TPE"], icon: "Taiwan.png" }, { name: "SG", pattern: ["新加坡", "狮城", "SG", "SGP", "SINGAPORE", "SIN"], icon: "Singapore.png" }, { name: "JP", pattern: ["日本", "东京", "大阪", "JP", "JPN", "JAPAN", "TOKYO", "OSAKA", "NRT", "HND", "TYO"], icon: "Japan.png" }, { name: "KR", pattern: ["韩国", "首尔", "KR", "KOR", "KOREA", "SEOUL", "ICN"], icon: "Korea.png" }, { name: "AS", pattern: [ "越南", "泰国", "马来西亚", "印尼", "菲律宾", "VN", "TH", "MY", "ID", "PH", "VIETNAM", "THAILAND", "MALAYSIA", "INDONESIA", "PHILIPPINES", "MANILA" ], icon: "Available.png" }, { name: "US", pattern: [ "美国", "纽约", "洛杉矶", "旧金山", "圣何塞", "西雅图", "芝加哥", "达拉斯", "硅谷", "US", "USA", "UNITEDSTATES", "UNITED STATES", "NEWYORK", "NEW YORK", "LOSANGELES", "LOS ANGELES", "SANFRANCISCO", "SAN FRANCISCO", "SANJOSE", "SAN JOSE", "SEATTLE", "CHICAGO", "DALLAS", "LAX", "SJC", "SFO" ], icon: "United_States.png" } ]).map((r) => ({ ...r, regex: buildRegex(r.pattern) })); const REGIONS = buildRegions(); // ==================== // 3. 节点处理 // ==================== const ensureConfigObject = (input) => (input && typeof input === "object" ? input : {}); const getOriginalProxies = (input) => Array.isArray(input.proxies) ? input.proxies : []; const makeProxyNamesUnique = (proxies = []) => { const used = new Set(); const nextIdx = new Map(); proxies.forEach((p) => { if (!p?.name) return; const base = String(p.name); if (!used.has(base)) { used.add(base); nextIdx.set(base, 1); return; } let idx = nextIdx.get(base) ?? 1; let candidate = `${base}_${idx}`; while (used.has(candidate)) { idx += 1; candidate = `${base}_${idx}`; } p.name = candidate; used.add(candidate); nextIdx.set(base, idx + 1); }); }; const splitInfoAndNormalProxies = (proxies = [], filterRegex) => { const infoProxies = []; const normalProxies = []; proxies.forEach((proxy) => { if (!proxy?.name) return; if (filterRegex.test(proxy.name)) infoProxies.push(proxy); else normalProxies.push(proxy); }); return { infoProxies, normalProxies }; }; const classifyProxiesByRegion = (normalProxies = [], regions = []) => { const regionGroupsData = regions.map((r) => ({ name: r.name, icon: r.icon, proxies: [] })); const regionGroupMap = new Map(regionGroupsData.map((r) => [r.name, r])); const regionSeen = new Map(regionGroupsData.map((r) => [r.name, new Set()])); const otherProxyNames = []; const otherSeen = new Set(); normalProxies.forEach((proxy) => { const proxyName = proxy.name; const normName = normalizeName(proxyName); const matchedRegion = regions.find((r) => r.regex.test(normName)); if (matchedRegion) { const group = regionGroupMap.get(matchedRegion.name); const seen = regionSeen.get(matchedRegion.name); if (group && seen && !seen.has(proxyName)) { group.proxies.push(proxyName); seen.add(proxyName); } } else if (!otherSeen.has(proxyName)) { otherProxyNames.push(proxyName); otherSeen.add(proxyName); } }); const activeRegions = regionGroupsData .map((r) => ({ ...r, proxies: uniq(r.proxies) })) .filter((r) => r.proxies.length > 0); const activeRegionNameSet = new Set(activeRegions.map((r) => r.name)); const activeRegionMap = new Map(activeRegions.map((r) => [r.name, r])); return { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames: uniq(otherProxyNames) }; }; const buildAiProxyList = (activeRegions = [], otherProxyNames = [], allNormalNames = []) => { const nonHkProxyNames = uniq([ ...activeRegions.filter((r) => r.name !== "HK").flatMap((r) => r.proxies), ...otherProxyNames ]); return nonHkProxyNames.length > 0 ? nonHkProxyNames : allNormalNames; }; // ==================== // 4. 策略组 // ==================== const createGroupFactory = (proxyGroups, iconBase) => { return (name, type, proxies, icon = "Available.png", extra = {}) => { const uniqueProxies = uniq(proxies); if (!name || uniqueProxies.length === 0) return; proxyGroups.push({ name, type, proxies: uniqueProxies, icon: iconBase + icon, ...extra }); }; }; const buildMainSelectOptions = ({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames }) => { const selectOptions = []; if (allNormalNames.length) selectOptions.push("全部"); if (aiProxies.length) selectOptions.push("AI"); regionOrder.forEach((name) => { if (activeRegionNameSet.has(name)) selectOptions.push(name); }); if (otherProxyNames.length) selectOptions.push("Other"); return uniq(selectOptions); }; const buildGlobalOptions = ({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames }) => { const options = []; if (allNormalNames.length) options.push("全部"); options.push(...buildMainSelectOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames })); return uniq(options); }; const buildProxyGroups = ({ allNormalNames, aiProxies, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }) => { const proxyGroups = []; const createGroup = createGroupFactory(proxyGroups, SETTINGS.ICON_BASE); const selectOptions = buildMainSelectOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder: SETTINGS.REGION_ORDER, otherProxyNames }); const selectFallback = allNormalNames.length ? ["全部"] : aiProxies.length ? ["AI"] : SETTINGS.REGION_ORDER.filter((rName) => activeRegionNameSet.has(rName)).slice(0, 1); const globalOptions = buildGlobalOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder: SETTINGS.REGION_ORDER, otherProxyNames }); createGroup("选择", "select", selectOptions.length ? selectOptions : selectFallback, "Proxy.png"); createGroup("GLOBAL", "select", globalOptions.length ? globalOptions : selectFallback, "Global.png"); if (allNormalNames.length) { createGroup("全部", "select", ["URL Test - 全部", "Fallback - 全部", ...allNormalNames], "Available.png"); } if (aiProxies.length) { createGroup("AI", "select", ["URL Test - AI", "Fallback - AI", ...aiProxies], "ChatGPT.png"); } SETTINGS.REGION_ORDER.forEach((rName) => { const region = activeRegionMap.get(rName); if (region) { createGroup(region.name, "select", [`URL Test - ${region.name}`, ...region.proxies], region.icon); } }); if (otherProxyNames.length) { createGroup("Other", "select", ["URL Test - Other", ...otherProxyNames], "Available.png"); } if (infoNames.length) { createGroup("Info", "select", infoNames, "Available.png"); } if (allNormalNames.length) { createGroup("URL Test - 全部", "url-test", allNormalNames, "Available.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - 全部", "fallback", allNormalNames, "Available.png", SETTINGS.FALLBACK_EXTRA); } if (aiProxies.length) { createGroup("URL Test - AI", "url-test", aiProxies, "ChatGPT.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - AI", "fallback", aiProxies, "ChatGPT.png", SETTINGS.FALLBACK_EXTRA); } SETTINGS.REGION_ORDER.forEach((rName) => { const region = activeRegionMap.get(rName); if (region) { createGroup(`URL Test - ${region.name}`, "url-test", region.proxies, region.icon, SETTINGS.URL_TEST_EXTRA); } }); if (otherProxyNames.length) { createGroup("URL Test - Other", "url-test", otherProxyNames, "Available.png", SETTINGS.URL_TEST_EXTRA); } return proxyGroups; }; // ==================== // 5. 规则 // ==================== const createRuleProvider = ({ key, file, behavior }) => ({ url: `${SETTINGS.RULE_BASE}${file}`, path: `./ruleset/metacubex/${key}.yaml`, behavior, interval: 86400, format: "yaml", type: "http" }); const buildRuleProviders = () => ({ PrivateDomain: createRuleProvider({ key: "private_domain", file: "geosite/private.yaml", behavior: "domain" }), PrivateIP: createRuleProvider({ key: "private_ip", file: "geoip/private.yaml", behavior: "ipcidr" }), GoogleCN: createRuleProvider({ key: "google_cn", file: "geosite/google-cn.yaml", behavior: "domain" }), Synology: createRuleProvider({ key: "synology", file: "geosite/synology.yaml", behavior: "domain" }), OpenAI: createRuleProvider({ key: "openai", file: "geosite/openai.yaml", behavior: "domain" }), Anthropic: createRuleProvider({ key: "anthropic", file: "geosite/anthropic.yaml", behavior: "domain" }), GoogleGemini: createRuleProvider({ key: "google_gemini", file: "geosite/google-gemini.yaml", behavior: "domain" }), Ads: createRuleProvider({ key: "ads", file: "geosite/category-ads-all.yaml", behavior: "domain" }), GFW: createRuleProvider({ key: "gfw", file: "geosite/gfw.yaml", behavior: "domain" }), ChinaDomain: createRuleProvider({ key: "cn", file: "geosite/cn.yaml", behavior: "domain" }) }); const buildRules = (bypass = []) => { const bypassUniq = uniq(bypass); return [ "RULE-SET,PrivateDomain,DIRECT", "RULE-SET,PrivateIP,DIRECT,no-resolve", "DOMAIN-SUFFIX,doubleclick.net,REJECT", "DOMAIN-SUFFIX,googlesyndication.com,REJECT", "DOMAIN-SUFFIX,googleadservices.com,REJECT", "DOMAIN-SUFFIX,googletagmanager.com,REJECT", "DOMAIN-SUFFIX,admob.com,REJECT", "RULE-SET,Ads,REJECT", "RULE-SET,GoogleCN,DIRECT", "RULE-SET,Synology,DIRECT", "DOMAIN-SUFFIX,sharepoint.com,DIRECT", ...bypassUniq.map((d) => `DOMAIN-SUFFIX,${d},DIRECT`), ...SETTINGS.DIRECT_FIX_RULES, "RULE-SET,OpenAI,AI", "RULE-SET,Anthropic,AI", "RULE-SET,GoogleGemini,AI", "RULE-SET,GFW,选择", "RULE-SET,ChinaDomain,DIRECT", "GEOIP,CN,DIRECT,no-resolve", "MATCH,选择" ]; }; // ==================== // 6. 网络配置 // ==================== const applySniffer = (inputConfig) => { inputConfig.sniffer = { ...inputConfig.sniffer, enable: true, "force-dns-mapping": true, "parse-pure-ip": true, "override-destination": true, sniff: { HTTP: { ports: [80, "8080-8880"], "override-destination": true }, TLS: { ports: [443, 8443] }, QUIC: { ports: [443, 8443] } }, "force-domain": SETTINGS.FORCE_DOMAIN }; }; const applyTun = (inputConfig) => { inputConfig.tun = { ...inputConfig.tun, enable: true, stack: "system", "auto-route": true, "auto-detect-interface": true, "strict-route": true, "dns-hijack": ["any:53", "tcp://any:53"] }; }; const applyDns = (inputConfig) => { const existingFakeIpFilter = Array.isArray(inputConfig.dns?.["fake-ip-filter"]) ? inputConfig.dns["fake-ip-filter"] : []; const mergedFakeIpFilter = uniq([...existingFakeIpFilter, ...SETTINGS.BASIC_FAKE_IP_FILTER]); inputConfig.dns = { ...inputConfig.dns, enable: true, "cache-algorithm": "arc", listen: inputConfig.dns?.listen, ipv6: inputConfig.dns?.ipv6, "enhanced-mode": "fake-ip", "fake-ip-filter": mergedFakeIpFilter, "default-nameserver": ["223.5.5.5", "119.29.29.29"], "nameserver-policy": { "geosite:cn": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT"], "geosite:private": "system", "rule-set:OpenAI": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:Anthropic": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:GoogleGemini": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"] }, nameserver: ["https://1.1.1.1/dns-query#选择", "https://8.8.8.8/dns-query#选择"], "proxy-server-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver-follow-policy": true }; }; const applyProfile = (inputConfig) => { inputConfig.profile = { ...inputConfig.profile, "store-selected": true, "store-fake-ip": false }; }; const applyRuntime = (inputConfig) => { inputConfig.mode = "rule"; inputConfig["log-level"] = "debug"; }; // ==================== // 7. 主流程 // ==================== config = ensureConfigObject(config); const originalProxies = getOriginalProxies(config); if (originalProxies.length === 0) { applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); return config; } makeProxyNamesUnique(originalProxies); const { infoProxies, normalProxies } = splitInfoAndNormalProxies(originalProxies, SETTINGS.FILTER_REGEX); const baseProxies = normalProxies.length ? normalProxies : originalProxies; const allNormalNames = uniq(baseProxies.map((p) => p.name)); const infoNames = uniq(infoProxies.map((p) => p.name)); const { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames } = classifyProxiesByRegion(baseProxies, REGIONS); const aiProxies = buildAiProxyList(activeRegions, otherProxyNames, allNormalNames); config["proxy-groups"] = buildProxyGroups({ allNormalNames, aiProxies, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }); config["rule-providers"] = buildRuleProviders(); config.rules = buildRules(bypassDomains); applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); config.proxies = originalProxies; return config; }


精简版

function main(config) { // ==================== // 0. 手动维护的直连域名 // ==================== const bypassDomains = [ "example.com", "none.com" ]; // ==================== // 1. 常量配置 // ==================== const SETTINGS = { ICON_BASE: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/", RULE_BASE: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/", URL_TEST_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FALLBACK_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FILTER_REGEX: /群|邀请|返利|官网|官方|网址|订阅|购买|续费|剩余|到期|过期|流量|备用|邮箱|客服|联系|工单|倒卖|防止|梯子|tg|telegram|电报|发布|重置/i, BASIC_FAKE_IP_FILTER: [ "*.lan", "+.lan", "*.local", "+.local", "+.localdomain", "+.home.arpa", "+.msftconnecttest.com", "+.msftncsi.com", "+.gstatic.com", "connectivitycheck.gstatic.com", "+.captive.apple.com", "time.*.com", "time.*.gov", "ntp.*.com", "ntp.*.org", "pool.ntp.org", "+.pool.ntp.org", "+.stun.*.*", "+.stun.*.*.*", "+.stun.*.*.*.*", "localhost.ptlogin2.qq.com", "WORKGROUP" ], FORCE_DOMAIN: [ "+.openai.com", "+.chat.com", "+.chatgpt.com", "+.oaistatic.com", "+.oaiusercontent.com", "+.sora.com", "+.anthropic.com", "+.claude.ai", "+.claude.com", "+.claudeusercontent.com", "+.gemini.google.com", "+.aistudio.google.com", "+.generativelanguage.googleapis.com", "+.makersuite.google.com", "+.notebooklm.google.com" ], DIRECT_FIX_RULES: [ "DOMAIN-SUFFIX,ol.epicgames.com,DIRECT", "DOMAIN-SUFFIX,dizhensubao.getui.com,DIRECT", "DOMAIN-SUFFIX,tracking-protection.cdn.mozilla.net,DIRECT", "DOMAIN,origin-a.akamaihd.net,DIRECT", "DOMAIN,fairplay.l.qq.com,DIRECT", "DOMAIN,livew.l.qq.com,DIRECT", "DOMAIN,vd.l.qq.com,DIRECT", "DOMAIN,errlog.umeng.com,DIRECT", "DOMAIN,msg.umeng.com,DIRECT", "DOMAIN,msg.umengcloud.com,DIRECT", "DOMAIN,tracking.miui.com,DIRECT", "DOMAIN,app.adjust.com,DIRECT", "DOMAIN,bdtj.tagtic.cn,DIRECT", "DOMAIN,rewards.hypixel.net,DIRECT", "DOMAIN-SUFFIX,koodomobile.com,DIRECT", "DOMAIN-SUFFIX,koodomobile.ca,DIRECT" ] }; // ==================== // 2. 基础工具 // ==================== const uniq = (arr = []) => [...new Set(arr.filter(Boolean))]; const escapeRegex = (s = "") => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const normalizeName = (name = "") => String(name) .replace(/(IEPL|IPLC|BGP|RELAY|PRO|V\d+)/ig, " $1 ") .replace(/[【】\[\]()()|_\-.,/:~]/g, " ") .replace(/🇭🇰/g, " HK ") .toUpperCase() .replace(/\s+/g, " ") .trim(); const buildRegex = (arr = []) => { const patterns = arr.map((raw) => { const token = String(raw).trim().toUpperCase(); const escaped = escapeRegex(token); if (/^[A-Z]{2,3}$/.test(token)) return `(?:^|[^A-Z])${escaped}(?:[^A-Z]|$)`; return escaped; }); return new RegExp(patterns.join("|"), "i"); }; const HK_REGEX = buildRegex(["香港", "HK", "HKG", "HONGKONG", "HONG KONG"]); // ==================== // 3. 节点处理 // ==================== const ensureConfigObject = (input) => (input && typeof input === "object" ? input : {}); const getOriginalProxies = (input) => Array.isArray(input.proxies) ? input.proxies : []; const makeProxyNamesUnique = (proxies = []) => { const used = new Set(); const nextIdx = new Map(); proxies.forEach((p) => { if (!p?.name) return; const base = String(p.name); if (!used.has(base)) { used.add(base); nextIdx.set(base, 1); return; } let idx = nextIdx.get(base) ?? 1; let candidate = `${base}_${idx}`; while (used.has(candidate)) { idx += 1; candidate = `${base}_${idx}`; } p.name = candidate; used.add(candidate); nextIdx.set(base, idx + 1); }); }; const splitInfoAndNormalProxies = (proxies = [], filterRegex) => { const infoProxies = []; const normalProxies = []; proxies.forEach((proxy) => { if (!proxy?.name) return; if (filterRegex.test(proxy.name)) infoProxies.push(proxy); else normalProxies.push(proxy); }); return { infoProxies, normalProxies }; }; const buildAiProxyList = (allNames = []) => { const nonHk = allNames.filter((n) => !HK_REGEX.test(normalizeName(n))); return nonHk.length > 0 ? nonHk : allNames; }; // ==================== // 4. 策略组 // ==================== const createGroupFactory = (proxyGroups, iconBase) => { return (name, type, proxies, icon = "Available.png", extra = {}) => { const uniqueProxies = uniq(proxies); if (!name || uniqueProxies.length === 0) return; proxyGroups.push({ name, type, proxies: uniqueProxies, icon: iconBase + icon, ...extra }); }; }; const buildProxyGroups = ({ allNames, aiNames, infoNames }) => { const proxyGroups = []; const createGroup = createGroupFactory(proxyGroups, SETTINGS.ICON_BASE); if (allNames.length) { createGroup("URL Test - 全部", "url-test", allNames, "Available.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - 全部", "fallback", allNames, "Available.png", SETTINGS.FALLBACK_EXTRA); } if (aiNames.length) { createGroup("URL Test - AI", "url-test", aiNames, "ChatGPT.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - AI", "fallback", aiNames, "ChatGPT.png", SETTINGS.FALLBACK_EXTRA); } createGroup( "全部", "select", allNames.length ? ["URL Test - 全部", "Fallback - 全部", ...allNames] : [], "Available.png" ); createGroup( "AI", "select", aiNames.length ? ["URL Test - AI", "Fallback - AI", ...aiNames] : [], "ChatGPT.png" ); // 关键:给 global 模式显式提供 GLOBAL 组,并让“全部”排第一 createGroup( "GLOBAL", "select", [ ...(allNames.length ? ["全部"] : []), ...(aiNames.length ? ["AI"] : []) ], "Global.png" ); if (infoNames.length) { createGroup("Info", "select", infoNames, "Available.png"); } return proxyGroups; }; // ==================== // 5. 规则 // ==================== const createRuleProvider = ({ key, file, behavior }) => ({ url: `${SETTINGS.RULE_BASE}${file}`, path: `./ruleset/metacubex/${key}.yaml`, behavior, interval: 86400, format: "yaml", type: "http" }); const buildRuleProviders = () => ({ PrivateDomain: createRuleProvider({ key: "private_domain", file: "geosite/private.yaml", behavior: "domain" }), PrivateIP: createRuleProvider({ key: "private_ip", file: "geoip/private.yaml", behavior: "ipcidr" }), GoogleCN: createRuleProvider({ key: "google_cn", file: "geosite/google-cn.yaml", behavior: "domain" }), Synology: createRuleProvider({ key: "synology", file: "geosite/synology.yaml", behavior: "domain" }), OpenAI: createRuleProvider({ key: "openai", file: "geosite/openai.yaml", behavior: "domain" }), Anthropic: createRuleProvider({ key: "anthropic", file: "geosite/anthropic.yaml", behavior: "domain" }), GoogleGemini: createRuleProvider({ key: "google_gemini", file: "geosite/google-gemini.yaml", behavior: "domain" }), Ads: createRuleProvider({ key: "ads", file: "geosite/category-ads-all.yaml", behavior: "domain" }), GFW: createRuleProvider({ key: "gfw", file: "geosite/gfw.yaml", behavior: "domain" }), ChinaDomain: createRuleProvider({ key: "cn", file: "geosite/cn.yaml", behavior: "domain" }) }); const buildRules = (bypass = []) => { const bypassUniq = uniq(bypass); return [ "RULE-SET,PrivateDomain,DIRECT", "RULE-SET,PrivateIP,DIRECT,no-resolve", "DOMAIN-SUFFIX,doubleclick.net,REJECT", "DOMAIN-SUFFIX,googlesyndication.com,REJECT", "DOMAIN-SUFFIX,googleadservices.com,REJECT", "DOMAIN-SUFFIX,googletagmanager.com,REJECT", "DOMAIN-SUFFIX,admob.com,REJECT", "RULE-SET,Ads,REJECT", "RULE-SET,GoogleCN,DIRECT", "RULE-SET,Synology,DIRECT", "DOMAIN-SUFFIX,sharepoint.com,DIRECT", ...bypassUniq.map((d) => `DOMAIN-SUFFIX,${d},DIRECT`), ...SETTINGS.DIRECT_FIX_RULES, "RULE-SET,OpenAI,AI", "RULE-SET,Anthropic,AI", "RULE-SET,GoogleGemini,AI", "RULE-SET,GFW,全部", "RULE-SET,ChinaDomain,DIRECT", "GEOIP,CN,DIRECT,no-resolve", "MATCH,全部" ]; }; // ==================== // 6. 网络配置 // ==================== const applySniffer = (inputConfig) => { inputConfig.sniffer = { ...inputConfig.sniffer, enable: true, "force-dns-mapping": true, "parse-pure-ip": true, "override-destination": true, sniff: { HTTP: { ports: [80, "8080-8880"], "override-destination": true }, TLS: { ports: [443, 8443] }, QUIC: { ports: [443, 8443] } }, "force-domain": SETTINGS.FORCE_DOMAIN }; }; const applyTun = (inputConfig) => { inputConfig.tun = { ...inputConfig.tun, enable: true, stack: "system", "auto-route": true, "auto-detect-interface": true, "strict-route": true, "dns-hijack": ["any:53", "tcp://any:53"] }; }; const applyDns = (inputConfig) => { const existingFakeIpFilter = Array.isArray(inputConfig.dns?.["fake-ip-filter"]) ? inputConfig.dns["fake-ip-filter"] : []; const mergedFakeIpFilter = uniq([...existingFakeIpFilter, ...SETTINGS.BASIC_FAKE_IP_FILTER]); inputConfig.dns = { ...inputConfig.dns, enable: true, "cache-algorithm": "arc", listen: inputConfig.dns?.listen, ipv6: inputConfig.dns?.ipv6, "enhanced-mode": "fake-ip", "fake-ip-filter": mergedFakeIpFilter, "default-nameserver": ["223.5.5.5", "119.29.29.29"], "nameserver-policy": { "geosite:cn": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT"], "geosite:private": "system", "rule-set:OpenAI": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:Anthropic": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:GoogleGemini": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"] }, nameserver: ["https://1.1.1.1/dns-query#全部", "https://8.8.8.8/dns-query#全部"], "proxy-server-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver-follow-policy": true }; }; const applyProfile = (inputConfig) => { inputConfig.profile = { ...inputConfig.profile, "store-selected": true, "store-fake-ip": false }; }; const applyRuntime = (inputConfig) => { inputConfig.mode = "rule"; inputConfig["log-level"] = "debug"; }; // ==================== // 7. 主流程 // ==================== config = ensureConfigObject(config); const originalProxies = getOriginalProxies(config); if (originalProxies.length === 0) { applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); return config; } makeProxyNamesUnique(originalProxies); const { infoProxies, normalProxies } = splitInfoAndNormalProxies(originalProxies, SETTINGS.FILTER_REGEX); const baseProxies = normalProxies.length ? normalProxies : originalProxies; const allNames = uniq(baseProxies.map((p) => p.name)); const infoNames = uniq(infoProxies.map((p) => p.name)); const aiNames = buildAiProxyList(allNames); config["proxy-groups"] = buildProxyGroups({ allNames, aiNames, infoNames }); config["rule-providers"] = buildRuleProviders(); config.rules = buildRules(bypassDomains); applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); config.proxies = originalProxies; return config; }


Showcase:
image1114×715 68.2 KB
image1684×1164 164 KB

更多使用效果展示见旧贴

使用的代理软件: GitHub - appshubcc/Bettbox: Another Better Mihomo Client · GitHub

网友解答:
--【壹】--:

手机在用,确实丝滑。
op上尝试用ai改了改,但改的不是很理想,放弃里- -#。

感谢大佬。


--【贰】--:

开头“手动维护的直连域名”,
跟下面的“DOMAIN-SUFFIX”有什么不同?

比如我想绕过bilibili.com这个网站,需要在哪里填写?


--【叁】--:

还没试过,没折腾过软路由


--【肆】--:

支持佬,收藏了


--【伍】--:

太强了,大佬


--【陆】--:

我使用https://browserleaks.com/ip测试会漏,使用的是bettbox,佬可以试试


--【柒】--:

不知道啊,我的跟鼻屎一样大没感觉了


--【捌】--:

感谢分享


--【玖】--:

v2rayn自带防泄漏


--【拾】--:

为什么你发的表情这么小我发的这么大


--【拾壹】--:

最上面的ip address吗,那个是依照延迟判断的所以会分流到国内的ip查询商,下面的DNS leak没出现国旗就没问题

IMG_20260321_2351151080×7710 1.04 MB


--【拾贰】--:

openclash能用吗


--【拾叁】--:

什么推流


--【拾肆】--:

哦,明白了,谢谢佬


--【拾伍】--:

牛蛙牛蛙~我也折腾了好久这个玩意。


--【拾陆】--:

这个热度还不如上个


--【拾柒】--:

好东西值得更多人知道


--【拾捌】--:

对,这个看个人情况的所以没加预设了


--【拾玖】--:

手动维护的直连域名,就是把需要直连的替换更改上去吗

问题描述:

本脚本已停止维护,建议移步新帖

[Mihomo覆写] DNS泄露 & AI分流 の 最终解决方案 —— sing-mix 开发调优
往期: v1 v2 序 折腾来折腾去,现在看来之前两版根本不能看(死慢的rule provider外加各类史山)。秉着“事不过三”原则,把最终版(?)在这里发了,日后若更新亦避免开新帖,将脚本托管到github了 ↓↓↓ https://raw.githubusercontent.com/Sakyvo/sing-mix/refs/heads/main/sing-mix_lite ↑↑↑ …

从 [NO DNS LEAK] 如何让mihomo拥有singbox一样的丝滑体验 - Clash覆写脚本分享 继续讨论

上个脚本居然把googlecn放在了ads前面导致广告没被屏蔽反而走直连了,奈何ublock origin过于强大一周了也没发现问题,闲来无事看下连接日志才发现不对劲,直接进行一个修复。顺带出了一个精简版本,因为发现各种HK TW SG什么的区域分组对个人而言用处不是很大就删掉了,只留全部和AI分组


完整版

function main(config) { // ==================== // 0. 手动维护的直连域名 // ==================== const bypassDomains = [ "example.com", "none.com" ]; // ==================== // 1. 常量配置 // ==================== const SETTINGS = { ICON_BASE: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/", RULE_BASE: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/", TEST_URL: "https://www.gstatic.com/generate_204", REGION_ORDER: ["HK", "TW", "SG", "JP", "KR", "AS", "US"], URL_TEST_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FALLBACK_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FILTER_REGEX: /群|邀请|返利|官网|官方|网址|订阅|购买|续费|剩余|到期|过期|流量|备用|邮箱|客服|联系|工单|倒卖|防止|梯子|tg|telegram|电报|发布|重置/i, BASIC_FAKE_IP_FILTER: [ "*.lan", "+.lan", "*.local", "+.local", "+.localdomain", "+.home.arpa", "+.msftconnecttest.com", "+.msftncsi.com", "+.gstatic.com", "connectivitycheck.gstatic.com", "+.captive.apple.com", "time.*.com", "time.*.gov", "ntp.*.com", "ntp.*.org", "pool.ntp.org", "+.pool.ntp.org", "+.stun.*.*", "+.stun.*.*.*", "+.stun.*.*.*.*", "localhost.ptlogin2.qq.com", "WORKGROUP" ], FORCE_DOMAIN: [ "+.openai.com", "+.chat.com", "+.chatgpt.com", "+.oaistatic.com", "+.oaiusercontent.com", "+.sora.com", "+.anthropic.com", "+.claude.ai", "+.claude.com", "+.claudeusercontent.com", "+.gemini.google.com", "+.aistudio.google.com", "+.generativelanguage.googleapis.com", "+.makersuite.google.com", "+.notebooklm.google.com" ], DIRECT_FIX_RULES: [ "DOMAIN-SUFFIX,ol.epicgames.com,DIRECT", "DOMAIN-SUFFIX,dizhensubao.getui.com,DIRECT", "DOMAIN-SUFFIX,tracking-protection.cdn.mozilla.net,DIRECT", "DOMAIN,origin-a.akamaihd.net,DIRECT", "DOMAIN,fairplay.l.qq.com,DIRECT", "DOMAIN,livew.l.qq.com,DIRECT", "DOMAIN,vd.l.qq.com,DIRECT", "DOMAIN,errlog.umeng.com,DIRECT", "DOMAIN,msg.umeng.com,DIRECT", "DOMAIN,msg.umengcloud.com,DIRECT", "DOMAIN,tracking.miui.com,DIRECT", "DOMAIN,app.adjust.com,DIRECT", "DOMAIN,bdtj.tagtic.cn,DIRECT", "DOMAIN,rewards.hypixel.net,DIRECT", "DOMAIN-SUFFIX,koodomobile.com,DIRECT", "DOMAIN-SUFFIX,koodomobile.ca,DIRECT" ] }; // ==================== // 2. 基础工具 // ==================== const uniq = (arr = []) => [...new Set(arr.filter(Boolean))]; const escapeRegex = (s = "") => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const normalizeName = (name = "") => String(name) .replace(/(IEPL|IPLC|BGP|RELAY|PRO|V\d+)/ig, " $1 ") .replace(/[【】\[\]()()|_\-.,/:~]/g, " ") .replace(/🇭🇰/g, " HK ") .replace(/🇹🇼/g, " TW ") .replace(/🇸🇬/g, " SG ") .replace(/🇯🇵/g, " JP ") .replace(/🇰🇷/g, " KR ") .replace(/🇻🇳|🇹🇭|🇲🇾|🇮🇩|🇵🇭/g, " AS ") .replace(/🇺🇸/g, " US ") .toUpperCase() .replace(/\s+/g, " ") .trim(); const buildRegex = (arr = []) => { const patterns = arr.map((raw) => { const token = String(raw).trim().toUpperCase(); const escaped = escapeRegex(token); if (/^[A-Z]{2,3}$/.test(token)) return `(?:^|[^A-Z])${escaped}(?:[^A-Z]|$)`; return escaped; }); return new RegExp(patterns.join("|"), "i"); }; const buildRegions = () => ([ { name: "HK", pattern: ["香港", "HK", "HKG", "HONGKONG", "HONG KONG"], icon: "Hong_Kong.png" }, { name: "TW", pattern: ["台湾", "台北", "新北", "TW", "TWN", "TAIWAN", "TAIPEI", "TPE"], icon: "Taiwan.png" }, { name: "SG", pattern: ["新加坡", "狮城", "SG", "SGP", "SINGAPORE", "SIN"], icon: "Singapore.png" }, { name: "JP", pattern: ["日本", "东京", "大阪", "JP", "JPN", "JAPAN", "TOKYO", "OSAKA", "NRT", "HND", "TYO"], icon: "Japan.png" }, { name: "KR", pattern: ["韩国", "首尔", "KR", "KOR", "KOREA", "SEOUL", "ICN"], icon: "Korea.png" }, { name: "AS", pattern: [ "越南", "泰国", "马来西亚", "印尼", "菲律宾", "VN", "TH", "MY", "ID", "PH", "VIETNAM", "THAILAND", "MALAYSIA", "INDONESIA", "PHILIPPINES", "MANILA" ], icon: "Available.png" }, { name: "US", pattern: [ "美国", "纽约", "洛杉矶", "旧金山", "圣何塞", "西雅图", "芝加哥", "达拉斯", "硅谷", "US", "USA", "UNITEDSTATES", "UNITED STATES", "NEWYORK", "NEW YORK", "LOSANGELES", "LOS ANGELES", "SANFRANCISCO", "SAN FRANCISCO", "SANJOSE", "SAN JOSE", "SEATTLE", "CHICAGO", "DALLAS", "LAX", "SJC", "SFO" ], icon: "United_States.png" } ]).map((r) => ({ ...r, regex: buildRegex(r.pattern) })); const REGIONS = buildRegions(); // ==================== // 3. 节点处理 // ==================== const ensureConfigObject = (input) => (input && typeof input === "object" ? input : {}); const getOriginalProxies = (input) => Array.isArray(input.proxies) ? input.proxies : []; const makeProxyNamesUnique = (proxies = []) => { const used = new Set(); const nextIdx = new Map(); proxies.forEach((p) => { if (!p?.name) return; const base = String(p.name); if (!used.has(base)) { used.add(base); nextIdx.set(base, 1); return; } let idx = nextIdx.get(base) ?? 1; let candidate = `${base}_${idx}`; while (used.has(candidate)) { idx += 1; candidate = `${base}_${idx}`; } p.name = candidate; used.add(candidate); nextIdx.set(base, idx + 1); }); }; const splitInfoAndNormalProxies = (proxies = [], filterRegex) => { const infoProxies = []; const normalProxies = []; proxies.forEach((proxy) => { if (!proxy?.name) return; if (filterRegex.test(proxy.name)) infoProxies.push(proxy); else normalProxies.push(proxy); }); return { infoProxies, normalProxies }; }; const classifyProxiesByRegion = (normalProxies = [], regions = []) => { const regionGroupsData = regions.map((r) => ({ name: r.name, icon: r.icon, proxies: [] })); const regionGroupMap = new Map(regionGroupsData.map((r) => [r.name, r])); const regionSeen = new Map(regionGroupsData.map((r) => [r.name, new Set()])); const otherProxyNames = []; const otherSeen = new Set(); normalProxies.forEach((proxy) => { const proxyName = proxy.name; const normName = normalizeName(proxyName); const matchedRegion = regions.find((r) => r.regex.test(normName)); if (matchedRegion) { const group = regionGroupMap.get(matchedRegion.name); const seen = regionSeen.get(matchedRegion.name); if (group && seen && !seen.has(proxyName)) { group.proxies.push(proxyName); seen.add(proxyName); } } else if (!otherSeen.has(proxyName)) { otherProxyNames.push(proxyName); otherSeen.add(proxyName); } }); const activeRegions = regionGroupsData .map((r) => ({ ...r, proxies: uniq(r.proxies) })) .filter((r) => r.proxies.length > 0); const activeRegionNameSet = new Set(activeRegions.map((r) => r.name)); const activeRegionMap = new Map(activeRegions.map((r) => [r.name, r])); return { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames: uniq(otherProxyNames) }; }; const buildAiProxyList = (activeRegions = [], otherProxyNames = [], allNormalNames = []) => { const nonHkProxyNames = uniq([ ...activeRegions.filter((r) => r.name !== "HK").flatMap((r) => r.proxies), ...otherProxyNames ]); return nonHkProxyNames.length > 0 ? nonHkProxyNames : allNormalNames; }; // ==================== // 4. 策略组 // ==================== const createGroupFactory = (proxyGroups, iconBase) => { return (name, type, proxies, icon = "Available.png", extra = {}) => { const uniqueProxies = uniq(proxies); if (!name || uniqueProxies.length === 0) return; proxyGroups.push({ name, type, proxies: uniqueProxies, icon: iconBase + icon, ...extra }); }; }; const buildMainSelectOptions = ({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames }) => { const selectOptions = []; if (allNormalNames.length) selectOptions.push("全部"); if (aiProxies.length) selectOptions.push("AI"); regionOrder.forEach((name) => { if (activeRegionNameSet.has(name)) selectOptions.push(name); }); if (otherProxyNames.length) selectOptions.push("Other"); return uniq(selectOptions); }; const buildGlobalOptions = ({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames }) => { const options = []; if (allNormalNames.length) options.push("全部"); options.push(...buildMainSelectOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder, otherProxyNames })); return uniq(options); }; const buildProxyGroups = ({ allNormalNames, aiProxies, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }) => { const proxyGroups = []; const createGroup = createGroupFactory(proxyGroups, SETTINGS.ICON_BASE); const selectOptions = buildMainSelectOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder: SETTINGS.REGION_ORDER, otherProxyNames }); const selectFallback = allNormalNames.length ? ["全部"] : aiProxies.length ? ["AI"] : SETTINGS.REGION_ORDER.filter((rName) => activeRegionNameSet.has(rName)).slice(0, 1); const globalOptions = buildGlobalOptions({ allNormalNames, aiProxies, activeRegionNameSet, regionOrder: SETTINGS.REGION_ORDER, otherProxyNames }); createGroup("选择", "select", selectOptions.length ? selectOptions : selectFallback, "Proxy.png"); createGroup("GLOBAL", "select", globalOptions.length ? globalOptions : selectFallback, "Global.png"); if (allNormalNames.length) { createGroup("全部", "select", ["URL Test - 全部", "Fallback - 全部", ...allNormalNames], "Available.png"); } if (aiProxies.length) { createGroup("AI", "select", ["URL Test - AI", "Fallback - AI", ...aiProxies], "ChatGPT.png"); } SETTINGS.REGION_ORDER.forEach((rName) => { const region = activeRegionMap.get(rName); if (region) { createGroup(region.name, "select", [`URL Test - ${region.name}`, ...region.proxies], region.icon); } }); if (otherProxyNames.length) { createGroup("Other", "select", ["URL Test - Other", ...otherProxyNames], "Available.png"); } if (infoNames.length) { createGroup("Info", "select", infoNames, "Available.png"); } if (allNormalNames.length) { createGroup("URL Test - 全部", "url-test", allNormalNames, "Available.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - 全部", "fallback", allNormalNames, "Available.png", SETTINGS.FALLBACK_EXTRA); } if (aiProxies.length) { createGroup("URL Test - AI", "url-test", aiProxies, "ChatGPT.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - AI", "fallback", aiProxies, "ChatGPT.png", SETTINGS.FALLBACK_EXTRA); } SETTINGS.REGION_ORDER.forEach((rName) => { const region = activeRegionMap.get(rName); if (region) { createGroup(`URL Test - ${region.name}`, "url-test", region.proxies, region.icon, SETTINGS.URL_TEST_EXTRA); } }); if (otherProxyNames.length) { createGroup("URL Test - Other", "url-test", otherProxyNames, "Available.png", SETTINGS.URL_TEST_EXTRA); } return proxyGroups; }; // ==================== // 5. 规则 // ==================== const createRuleProvider = ({ key, file, behavior }) => ({ url: `${SETTINGS.RULE_BASE}${file}`, path: `./ruleset/metacubex/${key}.yaml`, behavior, interval: 86400, format: "yaml", type: "http" }); const buildRuleProviders = () => ({ PrivateDomain: createRuleProvider({ key: "private_domain", file: "geosite/private.yaml", behavior: "domain" }), PrivateIP: createRuleProvider({ key: "private_ip", file: "geoip/private.yaml", behavior: "ipcidr" }), GoogleCN: createRuleProvider({ key: "google_cn", file: "geosite/google-cn.yaml", behavior: "domain" }), Synology: createRuleProvider({ key: "synology", file: "geosite/synology.yaml", behavior: "domain" }), OpenAI: createRuleProvider({ key: "openai", file: "geosite/openai.yaml", behavior: "domain" }), Anthropic: createRuleProvider({ key: "anthropic", file: "geosite/anthropic.yaml", behavior: "domain" }), GoogleGemini: createRuleProvider({ key: "google_gemini", file: "geosite/google-gemini.yaml", behavior: "domain" }), Ads: createRuleProvider({ key: "ads", file: "geosite/category-ads-all.yaml", behavior: "domain" }), GFW: createRuleProvider({ key: "gfw", file: "geosite/gfw.yaml", behavior: "domain" }), ChinaDomain: createRuleProvider({ key: "cn", file: "geosite/cn.yaml", behavior: "domain" }) }); const buildRules = (bypass = []) => { const bypassUniq = uniq(bypass); return [ "RULE-SET,PrivateDomain,DIRECT", "RULE-SET,PrivateIP,DIRECT,no-resolve", "DOMAIN-SUFFIX,doubleclick.net,REJECT", "DOMAIN-SUFFIX,googlesyndication.com,REJECT", "DOMAIN-SUFFIX,googleadservices.com,REJECT", "DOMAIN-SUFFIX,googletagmanager.com,REJECT", "DOMAIN-SUFFIX,admob.com,REJECT", "RULE-SET,Ads,REJECT", "RULE-SET,GoogleCN,DIRECT", "RULE-SET,Synology,DIRECT", "DOMAIN-SUFFIX,sharepoint.com,DIRECT", ...bypassUniq.map((d) => `DOMAIN-SUFFIX,${d},DIRECT`), ...SETTINGS.DIRECT_FIX_RULES, "RULE-SET,OpenAI,AI", "RULE-SET,Anthropic,AI", "RULE-SET,GoogleGemini,AI", "RULE-SET,GFW,选择", "RULE-SET,ChinaDomain,DIRECT", "GEOIP,CN,DIRECT,no-resolve", "MATCH,选择" ]; }; // ==================== // 6. 网络配置 // ==================== const applySniffer = (inputConfig) => { inputConfig.sniffer = { ...inputConfig.sniffer, enable: true, "force-dns-mapping": true, "parse-pure-ip": true, "override-destination": true, sniff: { HTTP: { ports: [80, "8080-8880"], "override-destination": true }, TLS: { ports: [443, 8443] }, QUIC: { ports: [443, 8443] } }, "force-domain": SETTINGS.FORCE_DOMAIN }; }; const applyTun = (inputConfig) => { inputConfig.tun = { ...inputConfig.tun, enable: true, stack: "system", "auto-route": true, "auto-detect-interface": true, "strict-route": true, "dns-hijack": ["any:53", "tcp://any:53"] }; }; const applyDns = (inputConfig) => { const existingFakeIpFilter = Array.isArray(inputConfig.dns?.["fake-ip-filter"]) ? inputConfig.dns["fake-ip-filter"] : []; const mergedFakeIpFilter = uniq([...existingFakeIpFilter, ...SETTINGS.BASIC_FAKE_IP_FILTER]); inputConfig.dns = { ...inputConfig.dns, enable: true, "cache-algorithm": "arc", listen: inputConfig.dns?.listen, ipv6: inputConfig.dns?.ipv6, "enhanced-mode": "fake-ip", "fake-ip-filter": mergedFakeIpFilter, "default-nameserver": ["223.5.5.5", "119.29.29.29"], "nameserver-policy": { "geosite:cn": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT"], "geosite:private": "system", "rule-set:OpenAI": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:Anthropic": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:GoogleGemini": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"] }, nameserver: ["https://1.1.1.1/dns-query#选择", "https://8.8.8.8/dns-query#选择"], "proxy-server-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver-follow-policy": true }; }; const applyProfile = (inputConfig) => { inputConfig.profile = { ...inputConfig.profile, "store-selected": true, "store-fake-ip": false }; }; const applyRuntime = (inputConfig) => { inputConfig.mode = "rule"; inputConfig["log-level"] = "debug"; }; // ==================== // 7. 主流程 // ==================== config = ensureConfigObject(config); const originalProxies = getOriginalProxies(config); if (originalProxies.length === 0) { applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); return config; } makeProxyNamesUnique(originalProxies); const { infoProxies, normalProxies } = splitInfoAndNormalProxies(originalProxies, SETTINGS.FILTER_REGEX); const baseProxies = normalProxies.length ? normalProxies : originalProxies; const allNormalNames = uniq(baseProxies.map((p) => p.name)); const infoNames = uniq(infoProxies.map((p) => p.name)); const { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames } = classifyProxiesByRegion(baseProxies, REGIONS); const aiProxies = buildAiProxyList(activeRegions, otherProxyNames, allNormalNames); config["proxy-groups"] = buildProxyGroups({ allNormalNames, aiProxies, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }); config["rule-providers"] = buildRuleProviders(); config.rules = buildRules(bypassDomains); applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); config.proxies = originalProxies; return config; }


精简版

function main(config) { // ==================== // 0. 手动维护的直连域名 // ==================== const bypassDomains = [ "example.com", "none.com" ]; // ==================== // 1. 常量配置 // ==================== const SETTINGS = { ICON_BASE: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/", RULE_BASE: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/", URL_TEST_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FALLBACK_EXTRA: { hidden: true, url: "https://www.gstatic.com/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 3000 }, FILTER_REGEX: /群|邀请|返利|官网|官方|网址|订阅|购买|续费|剩余|到期|过期|流量|备用|邮箱|客服|联系|工单|倒卖|防止|梯子|tg|telegram|电报|发布|重置/i, BASIC_FAKE_IP_FILTER: [ "*.lan", "+.lan", "*.local", "+.local", "+.localdomain", "+.home.arpa", "+.msftconnecttest.com", "+.msftncsi.com", "+.gstatic.com", "connectivitycheck.gstatic.com", "+.captive.apple.com", "time.*.com", "time.*.gov", "ntp.*.com", "ntp.*.org", "pool.ntp.org", "+.pool.ntp.org", "+.stun.*.*", "+.stun.*.*.*", "+.stun.*.*.*.*", "localhost.ptlogin2.qq.com", "WORKGROUP" ], FORCE_DOMAIN: [ "+.openai.com", "+.chat.com", "+.chatgpt.com", "+.oaistatic.com", "+.oaiusercontent.com", "+.sora.com", "+.anthropic.com", "+.claude.ai", "+.claude.com", "+.claudeusercontent.com", "+.gemini.google.com", "+.aistudio.google.com", "+.generativelanguage.googleapis.com", "+.makersuite.google.com", "+.notebooklm.google.com" ], DIRECT_FIX_RULES: [ "DOMAIN-SUFFIX,ol.epicgames.com,DIRECT", "DOMAIN-SUFFIX,dizhensubao.getui.com,DIRECT", "DOMAIN-SUFFIX,tracking-protection.cdn.mozilla.net,DIRECT", "DOMAIN,origin-a.akamaihd.net,DIRECT", "DOMAIN,fairplay.l.qq.com,DIRECT", "DOMAIN,livew.l.qq.com,DIRECT", "DOMAIN,vd.l.qq.com,DIRECT", "DOMAIN,errlog.umeng.com,DIRECT", "DOMAIN,msg.umeng.com,DIRECT", "DOMAIN,msg.umengcloud.com,DIRECT", "DOMAIN,tracking.miui.com,DIRECT", "DOMAIN,app.adjust.com,DIRECT", "DOMAIN,bdtj.tagtic.cn,DIRECT", "DOMAIN,rewards.hypixel.net,DIRECT", "DOMAIN-SUFFIX,koodomobile.com,DIRECT", "DOMAIN-SUFFIX,koodomobile.ca,DIRECT" ] }; // ==================== // 2. 基础工具 // ==================== const uniq = (arr = []) => [...new Set(arr.filter(Boolean))]; const escapeRegex = (s = "") => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const normalizeName = (name = "") => String(name) .replace(/(IEPL|IPLC|BGP|RELAY|PRO|V\d+)/ig, " $1 ") .replace(/[【】\[\]()()|_\-.,/:~]/g, " ") .replace(/🇭🇰/g, " HK ") .toUpperCase() .replace(/\s+/g, " ") .trim(); const buildRegex = (arr = []) => { const patterns = arr.map((raw) => { const token = String(raw).trim().toUpperCase(); const escaped = escapeRegex(token); if (/^[A-Z]{2,3}$/.test(token)) return `(?:^|[^A-Z])${escaped}(?:[^A-Z]|$)`; return escaped; }); return new RegExp(patterns.join("|"), "i"); }; const HK_REGEX = buildRegex(["香港", "HK", "HKG", "HONGKONG", "HONG KONG"]); // ==================== // 3. 节点处理 // ==================== const ensureConfigObject = (input) => (input && typeof input === "object" ? input : {}); const getOriginalProxies = (input) => Array.isArray(input.proxies) ? input.proxies : []; const makeProxyNamesUnique = (proxies = []) => { const used = new Set(); const nextIdx = new Map(); proxies.forEach((p) => { if (!p?.name) return; const base = String(p.name); if (!used.has(base)) { used.add(base); nextIdx.set(base, 1); return; } let idx = nextIdx.get(base) ?? 1; let candidate = `${base}_${idx}`; while (used.has(candidate)) { idx += 1; candidate = `${base}_${idx}`; } p.name = candidate; used.add(candidate); nextIdx.set(base, idx + 1); }); }; const splitInfoAndNormalProxies = (proxies = [], filterRegex) => { const infoProxies = []; const normalProxies = []; proxies.forEach((proxy) => { if (!proxy?.name) return; if (filterRegex.test(proxy.name)) infoProxies.push(proxy); else normalProxies.push(proxy); }); return { infoProxies, normalProxies }; }; const buildAiProxyList = (allNames = []) => { const nonHk = allNames.filter((n) => !HK_REGEX.test(normalizeName(n))); return nonHk.length > 0 ? nonHk : allNames; }; // ==================== // 4. 策略组 // ==================== const createGroupFactory = (proxyGroups, iconBase) => { return (name, type, proxies, icon = "Available.png", extra = {}) => { const uniqueProxies = uniq(proxies); if (!name || uniqueProxies.length === 0) return; proxyGroups.push({ name, type, proxies: uniqueProxies, icon: iconBase + icon, ...extra }); }; }; const buildProxyGroups = ({ allNames, aiNames, infoNames }) => { const proxyGroups = []; const createGroup = createGroupFactory(proxyGroups, SETTINGS.ICON_BASE); if (allNames.length) { createGroup("URL Test - 全部", "url-test", allNames, "Available.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - 全部", "fallback", allNames, "Available.png", SETTINGS.FALLBACK_EXTRA); } if (aiNames.length) { createGroup("URL Test - AI", "url-test", aiNames, "ChatGPT.png", SETTINGS.URL_TEST_EXTRA); createGroup("Fallback - AI", "fallback", aiNames, "ChatGPT.png", SETTINGS.FALLBACK_EXTRA); } createGroup( "全部", "select", allNames.length ? ["URL Test - 全部", "Fallback - 全部", ...allNames] : [], "Available.png" ); createGroup( "AI", "select", aiNames.length ? ["URL Test - AI", "Fallback - AI", ...aiNames] : [], "ChatGPT.png" ); // 关键:给 global 模式显式提供 GLOBAL 组,并让“全部”排第一 createGroup( "GLOBAL", "select", [ ...(allNames.length ? ["全部"] : []), ...(aiNames.length ? ["AI"] : []) ], "Global.png" ); if (infoNames.length) { createGroup("Info", "select", infoNames, "Available.png"); } return proxyGroups; }; // ==================== // 5. 规则 // ==================== const createRuleProvider = ({ key, file, behavior }) => ({ url: `${SETTINGS.RULE_BASE}${file}`, path: `./ruleset/metacubex/${key}.yaml`, behavior, interval: 86400, format: "yaml", type: "http" }); const buildRuleProviders = () => ({ PrivateDomain: createRuleProvider({ key: "private_domain", file: "geosite/private.yaml", behavior: "domain" }), PrivateIP: createRuleProvider({ key: "private_ip", file: "geoip/private.yaml", behavior: "ipcidr" }), GoogleCN: createRuleProvider({ key: "google_cn", file: "geosite/google-cn.yaml", behavior: "domain" }), Synology: createRuleProvider({ key: "synology", file: "geosite/synology.yaml", behavior: "domain" }), OpenAI: createRuleProvider({ key: "openai", file: "geosite/openai.yaml", behavior: "domain" }), Anthropic: createRuleProvider({ key: "anthropic", file: "geosite/anthropic.yaml", behavior: "domain" }), GoogleGemini: createRuleProvider({ key: "google_gemini", file: "geosite/google-gemini.yaml", behavior: "domain" }), Ads: createRuleProvider({ key: "ads", file: "geosite/category-ads-all.yaml", behavior: "domain" }), GFW: createRuleProvider({ key: "gfw", file: "geosite/gfw.yaml", behavior: "domain" }), ChinaDomain: createRuleProvider({ key: "cn", file: "geosite/cn.yaml", behavior: "domain" }) }); const buildRules = (bypass = []) => { const bypassUniq = uniq(bypass); return [ "RULE-SET,PrivateDomain,DIRECT", "RULE-SET,PrivateIP,DIRECT,no-resolve", "DOMAIN-SUFFIX,doubleclick.net,REJECT", "DOMAIN-SUFFIX,googlesyndication.com,REJECT", "DOMAIN-SUFFIX,googleadservices.com,REJECT", "DOMAIN-SUFFIX,googletagmanager.com,REJECT", "DOMAIN-SUFFIX,admob.com,REJECT", "RULE-SET,Ads,REJECT", "RULE-SET,GoogleCN,DIRECT", "RULE-SET,Synology,DIRECT", "DOMAIN-SUFFIX,sharepoint.com,DIRECT", ...bypassUniq.map((d) => `DOMAIN-SUFFIX,${d},DIRECT`), ...SETTINGS.DIRECT_FIX_RULES, "RULE-SET,OpenAI,AI", "RULE-SET,Anthropic,AI", "RULE-SET,GoogleGemini,AI", "RULE-SET,GFW,全部", "RULE-SET,ChinaDomain,DIRECT", "GEOIP,CN,DIRECT,no-resolve", "MATCH,全部" ]; }; // ==================== // 6. 网络配置 // ==================== const applySniffer = (inputConfig) => { inputConfig.sniffer = { ...inputConfig.sniffer, enable: true, "force-dns-mapping": true, "parse-pure-ip": true, "override-destination": true, sniff: { HTTP: { ports: [80, "8080-8880"], "override-destination": true }, TLS: { ports: [443, 8443] }, QUIC: { ports: [443, 8443] } }, "force-domain": SETTINGS.FORCE_DOMAIN }; }; const applyTun = (inputConfig) => { inputConfig.tun = { ...inputConfig.tun, enable: true, stack: "system", "auto-route": true, "auto-detect-interface": true, "strict-route": true, "dns-hijack": ["any:53", "tcp://any:53"] }; }; const applyDns = (inputConfig) => { const existingFakeIpFilter = Array.isArray(inputConfig.dns?.["fake-ip-filter"]) ? inputConfig.dns["fake-ip-filter"] : []; const mergedFakeIpFilter = uniq([...existingFakeIpFilter, ...SETTINGS.BASIC_FAKE_IP_FILTER]); inputConfig.dns = { ...inputConfig.dns, enable: true, "cache-algorithm": "arc", listen: inputConfig.dns?.listen, ipv6: inputConfig.dns?.ipv6, "enhanced-mode": "fake-ip", "fake-ip-filter": mergedFakeIpFilter, "default-nameserver": ["223.5.5.5", "119.29.29.29"], "nameserver-policy": { "geosite:cn": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT"], "geosite:private": "system", "rule-set:OpenAI": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:Anthropic": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"], "rule-set:GoogleGemini": ["https://1.1.1.1/dns-query#AI", "https://8.8.8.8/dns-query#AI"] }, nameserver: ["https://1.1.1.1/dns-query#全部", "https://8.8.8.8/dns-query#全部"], "proxy-server-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver": ["223.5.5.5#DIRECT", "119.29.29.29#DIRECT", "system"], "direct-nameserver-follow-policy": true }; }; const applyProfile = (inputConfig) => { inputConfig.profile = { ...inputConfig.profile, "store-selected": true, "store-fake-ip": false }; }; const applyRuntime = (inputConfig) => { inputConfig.mode = "rule"; inputConfig["log-level"] = "debug"; }; // ==================== // 7. 主流程 // ==================== config = ensureConfigObject(config); const originalProxies = getOriginalProxies(config); if (originalProxies.length === 0) { applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); return config; } makeProxyNamesUnique(originalProxies); const { infoProxies, normalProxies } = splitInfoAndNormalProxies(originalProxies, SETTINGS.FILTER_REGEX); const baseProxies = normalProxies.length ? normalProxies : originalProxies; const allNames = uniq(baseProxies.map((p) => p.name)); const infoNames = uniq(infoProxies.map((p) => p.name)); const aiNames = buildAiProxyList(allNames); config["proxy-groups"] = buildProxyGroups({ allNames, aiNames, infoNames }); config["rule-providers"] = buildRuleProviders(); config.rules = buildRules(bypassDomains); applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); config.proxies = originalProxies; return config; }


Showcase:
image1114×715 68.2 KB
image1684×1164 164 KB

更多使用效果展示见旧贴

使用的代理软件: GitHub - appshubcc/Bettbox: Another Better Mihomo Client · GitHub

网友解答:
--【壹】--:

手机在用,确实丝滑。
op上尝试用ai改了改,但改的不是很理想,放弃里- -#。

感谢大佬。


--【贰】--:

开头“手动维护的直连域名”,
跟下面的“DOMAIN-SUFFIX”有什么不同?

比如我想绕过bilibili.com这个网站,需要在哪里填写?


--【叁】--:

还没试过,没折腾过软路由


--【肆】--:

支持佬,收藏了


--【伍】--:

太强了,大佬


--【陆】--:

我使用https://browserleaks.com/ip测试会漏,使用的是bettbox,佬可以试试


--【柒】--:

不知道啊,我的跟鼻屎一样大没感觉了


--【捌】--:

感谢分享


--【玖】--:

v2rayn自带防泄漏


--【拾】--:

为什么你发的表情这么小我发的这么大


--【拾壹】--:

最上面的ip address吗,那个是依照延迟判断的所以会分流到国内的ip查询商,下面的DNS leak没出现国旗就没问题

IMG_20260321_2351151080×7710 1.04 MB


--【拾贰】--:

openclash能用吗


--【拾叁】--:

什么推流


--【拾肆】--:

哦,明白了,谢谢佬


--【拾伍】--:

牛蛙牛蛙~我也折腾了好久这个玩意。


--【拾陆】--:

这个热度还不如上个


--【拾柒】--:

好东西值得更多人知道


--【拾捌】--:

对,这个看个人情况的所以没加预设了


--【拾玖】--:

手动维护的直连域名,就是把需要直连的替换更改上去吗