Files
meta/META.js
T
xixu-me 4a65083c3f Reorder 'AUTO' and 'FALLBACK' in proxy group arrays
Moved 'AUTO' and 'FALLBACK' entries to appear after static proxy names and other load balancing options in proxy group definitions for improved logical grouping and consistency.
2025-11-10 19:34:34 +08:00

727 lines
20 KiB
JavaScript

// from https://github.com/xixu-me/META
// ################################################### this section can be flexibly customized ###################################################
// Number of STATIC proxy groups (for IP-sensitive services)
const STATIC_PROXY_COUNT = 3;
// Routing rules are matched in order from top to bottom, with the rule at the top of the list taking precedence over the rules below it.
const services = [
// Media & Content
{ name: "bilibili", tld: "com" },
{ name: "Medium", tld: "com" },
{ name: "Netflix", tld: "com" },
{ name: "Sora", domain: "sora.chatgpt.com" },
{ name: "Spotify", tld: "com" },
{ name: "YouTube", tld: "com" },
// AI/LLM Services
{ name: "Anthropic", tld: "com" },
{ name: "Gemini", tld: "google", alias: "Google-Gemini" },
{ name: "NotebookLM", tld: "google", alias: "Google-NotebookLM" },
{ name: "OpenAI", tld: "com" },
{ name: "Perplexity", tld: "ai" },
{ name: "xAI", tld: "ai", sld: "x" },
// Social Media & Communication
{ name: "Mastodon", tld: "social" },
{ name: "rednote", domain: "xiaohongshu.com", alias: "Xiaohongshu" },
{ name: "Telegram", tld: "org" },
{ name: "X", tld: "com" },
// Financial Services
{ name: "Binance", tld: "com" },
{ name: "Bybit", tld: "com" },
{ name: "OKX", tld: "com" },
{ name: "Pi", tld: "com", sld: "minepi" },
// Technology Companies
{ name: "Apple", tld: "com" },
{ name: "Google", tld: "google", sld: "about" },
{ name: "Microsoft", tld: "com" },
// Infrastructure
{ name: "Speedtest", tld: "net" },
{ name: "Xget", domain: "xuc.xi-xu.me" },
{ name: "Cloudflare", tld: "com" },
];
const LOCATION_ICON_SET_URL = "https://img.icons8.com/color/144/";
const locations = [
{
name: "Argentina 🇦🇷",
icon: `${LOCATION_ICON_SET_URL}argentina`,
filter: "(?i)\u963f\u6839\u5ef7|Argentina|ARG|AR|argentina|arg|ar|🇦🇷",
},
{
name: "Canada 🇨🇦",
icon: `${LOCATION_ICON_SET_URL}canada`,
filter: "(?i)\u52a0\u62ff\u5927|Canada|CAN|CA|canada|ca|🇨🇦",
},
{
name: "Finland 🇫🇮",
icon: `${LOCATION_ICON_SET_URL}finland`,
filter: "(?i)\u82ac\u5170|Finland|FIN|FI|finland|fin|fi|🇫🇮",
},
{
name: "France 🇫🇷",
icon: `${LOCATION_ICON_SET_URL}france`,
filter: "(?i)\u6cd5\u56fd|France|FR|france|fr|🇫🇷",
},
{
name: "Germany 🇩🇪",
icon: `${LOCATION_ICON_SET_URL}germany`,
filter: "(?i)\u5fb7\u56fd|Germany|GER|DE|germany|ger|de|🇩🇪",
},
{
name: "Hong Kong, China 🇭🇰",
icon: `${LOCATION_ICON_SET_URL}hongkong-circular`,
filter: "(?i)\u9999\u6e2f|Hong Kong|HK|hong kong|hk|🇭🇰",
},
{
name: "Iraq 🇮🇶",
icon: `${LOCATION_ICON_SET_URL}iraq`,
filter: "(?i)\u4f0a\u62c9\u514b|Iraq|IQ|iraq|iq|🇮🇶",
},
{
name: "Japan 🇯🇵",
icon: `${LOCATION_ICON_SET_URL}japan`,
filter: "(?i)\u65e5\u672c|Japan|JP|japan|jp|🇯🇵",
},
{
name: "Korea 🇰🇷",
icon: `${LOCATION_ICON_SET_URL}south-korea`,
filter: "(?i)\u97e9\u56fd|Korea|KR|korea|kr|🇰🇷",
},
{
name: "Russia 🇷🇺",
icon: `${LOCATION_ICON_SET_URL}russian-federation`,
filter:
"(?i)\u4fc4\u7f57\u65af|Russia Federation|Russia|RU|russia federation|russia|ru|🇷🇺",
},
{
name: "Singapore 🇸🇬",
icon: `${LOCATION_ICON_SET_URL}singapore`,
filter: "(?i)\u65b0\u52a0\u5761|Singapore|SG|singapore|sg|🇸🇬",
},
{
name: "Taiwan, China 🇨🇳",
icon: `${LOCATION_ICON_SET_URL}china-circular`,
filter: "(?i)\u53f0\u6e7e|Taiwan|TW|taiwan|tw|\uD83C\uDDF9\uD83C\uDDFC",
},
{
name: "Thailand 🇹🇭",
icon: `${LOCATION_ICON_SET_URL}thailand`,
filter: "(?i)\u6cf0\u56fd|Thailand|TH|thailand|th|🇹🇭",
},
{
name: "Türkiye 🇹🇷",
icon: `${LOCATION_ICON_SET_URL}turkey`,
filter:
"(?i)\u571f\u8033\u5176|Türkiye|Turkey|TUR|TR|türkiye|turkey|tur|tr|🇹🇷",
},
{
name: "Ukraine 🇺🇦",
icon: `${LOCATION_ICON_SET_URL}ukraine`,
filter: "(?i)\u4e4c\u514b\u5170|Ukraine|UKR|UA|ukraine|ukr|ua|🇺🇦",
},
{
name: "United Kingdom 🇬🇧",
icon: `${LOCATION_ICON_SET_URL}great-britain`,
filter:
"(?i)\u82f1\u56fd|United Kingdom|Great Britain|UK|GB|united kingdom|great britain|uk|gb|🇬🇧",
},
{
name: "United States 🇺🇸",
icon: `${LOCATION_ICON_SET_URL}usa`,
filter:
"(?i)\u7f8e\u56fd|United States of America|United States|USA|US|united states of america|united states|usa|us|🇺🇸",
},
{
name: "Global 🌐",
icon: "https://img.icons8.com/?size=144&id=3685&format=png&color=7bbbe9",
filter: "(?i)Global|GL|global|gl|Cloudflare|CF|cloudflare|cf|🌐",
},
];
// ####################################### DO NOT MODIFY THE CODE BELOW UNLESS YOU KNOW WHAT YOU ARE DOING #######################################
function extractFavicon(domain) {
return `https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://${domain}&size=128`;
}
// General Configuration
const generalConfig = {
"allow-lan": false,
mode: "rule",
"log-level": "info",
ipv6: true,
"find-process-mode": "strict",
"external-controller": "",
profile: {
"store-selected": true,
"store-fake-ip": true,
},
"unified-delay": true,
"tcp-concurrent": true,
"global-client-fingerprint": "chrome",
"global-ua": "\u0063\u006c\u0061\u0073\u0068.\u006D\u0065\u0074\u0061",
"etag-support": true,
};
// DNS
const chineseNameservers = [
"https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query",
];
const internationalNameservers = [
"quic://unfiltered.adguard-dns.com:784",
"https://dns.google/dns-query",
"https://doh.dns.sb/dns-query",
"https://dns.quad9.net/dns-query",
"https://doh.opendns.com/dns-query",
"https://dns.mullvad.net/dns-query",
"https://doh.umbrella.com/dns-query",
"https://wikimedia-dns.org/dns-query",
"https://doh.dns.apple.com/dns-query",
"https://cloudflare-dns.com/dns-query",
"https://common.dot.dns.yandex.net/dns-query",
"https://unfiltered.adguard-dns.com/dns-query",
];
const adguardDefaultNameservers = [
"quic://dns.adguard-dns.com:784",
"https://dns.adguard-dns.com/dns-query",
];
const adguardFamilyNameservers = [
"quic://family.adguard-dns.com:784",
"https://family.adguard-dns.com/dns-query",
];
const dns = {
enable: true,
"prefer-h3": false,
"use-hosts": true,
"use-system-hosts": false,
"respect-rules": true,
listen: "0.0.0.0:1053",
ipv6: true,
"enhanced-mode": "fake-ip",
"fake-ip-range": "198.18.0.1/16",
"fake-ip-filter-mode": "blacklist",
"fake-ip-filter": [
"rule-set:fake-ip-filter",
"rule-set:direct",
"rule-set:private",
"rule-set:connectivity-check",
],
"default-nameserver": [
"119.29.29.29",
"223.5.5.5",
"223.6.6.6",
"8.8.8.8",
"8.8.4.4",
"1.1.1.1",
"1.0.0.1",
"9.9.9.9",
"149.112.112.112",
"208.67.222.222",
"208.67.220.220",
],
"proxy-server-nameserver": chineseNameservers,
"nameserver-policy": {
"rule-set:private,direct,geolocation-cn": chineseNameservers,
"rule-set:proxy": internationalNameservers,
},
nameserver: [
...chineseNameservers,
...internationalNameservers,
// ...adguardDefaultNameservers,
// ...adguardFamilyNameservers,
],
};
// Hosts
const hosts = {};
// Domain Sniffing
const sniffer = {
enable: false,
};
// TUN
const tun = {
enable: true,
stack: "mixed",
"auto-route": true,
"auto-redirect": true,
"auto-detect-interface": true,
"strict-route": true,
"dns-hijack": ["any:53", "tcp://any:53"],
};
// Proxy Groups
const proxyGroupDefaults = {
url: "https://www.gstatic.com/generate_204",
interval: 300,
lazy: true,
timeout: 5000,
"max-failed-times": 5,
};
// Generate STATIC proxy group names
function generateStaticProxyNames() {
if (STATIC_PROXY_COUNT === 1) {
return ["STATIC"];
}
return Array.from(
{ length: STATIC_PROXY_COUNT },
(_, i) => `STATIC-${i + 1}`
);
}
const staticProxyNames = generateStaticProxyNames();
const serviceProxyGroupProxies = [
"PROXY",
...staticProxyNames,
"AUTO",
"DIRECT",
"Mainland China 🇨🇳",
...locations.map(({ name }) => name),
];
const locationPolicyProxyGroupDefaults = {
...proxyGroupDefaults,
proxies: ["REJECT"],
"include-all": true,
};
const SERVICE_ICON_SET_URL =
"https://cdn.jsdelivr.net/gh/xixu-me/favicons@assets/favicons/";
function getServiceIcon(service) {
if (service.icon) return service.icon;
if (service.domain) return extractFavicon(service.domain);
if (service.tld) {
const secondLevelDomain = service.sld || service.name.toLowerCase();
return `${SERVICE_ICON_SET_URL}${secondLevelDomain}.${service.tld}.png`;
}
console.warn(`No icon configuration for service: ${service.name}`);
return null;
}
services.forEach((service) => {
service.icon = getServiceIcon(service);
});
function generateServiceProxyGroups(items, defaultConfig) {
return items.map(({ name, icon }) => ({
...defaultConfig,
name,
icon,
type: "select",
proxies: serviceProxyGroupProxies,
}));
}
function generateLocationPolicyProxyGroups(
items,
defaultConfig,
type,
extraProps = {}
) {
const getStrategyName = (type, strategy) => {
switch (type) {
case "url-test":
return "AUTO";
case "fallback":
return "FALLBACK";
case "load-balance":
switch (strategy) {
case "consistent-hashing":
return "LOAD BALANCING (consistent hashing)";
case "round-robin":
return "LOAD BALANCING (round-robin)";
case "sticky-sessions":
return "LOAD BALANCING (sticky sessions)";
default:
return "LOAD BALANCING";
}
default:
return type.toUpperCase();
}
};
return items.map(({ name, icon, filter }) => {
const strategyName = getStrategyName(type, extraProps.strategy);
const emoji = name.split(" ").pop();
const newName = `${strategyName} ${emoji}`;
return {
...defaultConfig,
name: newName,
type,
icon,
filter,
hidden: true,
...extraProps,
};
});
}
function generateLocationSelectProxyGroups() {
return locations.map(({ name, icon }) => ({
...proxyGroupDefaults,
name,
type: "select",
icon,
proxies: [
`LOAD BALANCING (consistent hashing) ${name.split(" ").pop()}`,
`LOAD BALANCING (round-robin) ${name.split(" ").pop()}`,
`LOAD BALANCING (sticky sessions) ${name.split(" ").pop()}`,
`FALLBACK ${name.split(" ").pop()}`,
`AUTO ${name.split(" ").pop()}`,
],
}));
}
const BASE_ICON_SET_URL =
"https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/";
// Generate STATIC proxy groups
function generateStaticProxyGroups(defaultConfig) {
return staticProxyNames.map((name, index) => ({
...defaultConfig,
name,
type: "select",
"include-all": true,
icon: `${BASE_ICON_SET_URL}Static.png`,
}));
}
const proxyGroups = [
{
...proxyGroupDefaults,
name: "PROXY",
type: "select",
proxies: [
...staticProxyNames,
"AUTO",
"Mainland China 🇨🇳",
...locations.map(({ name }) => name),
],
icon: `${BASE_ICON_SET_URL}Proxy.png`,
},
{
...proxyGroupDefaults,
name: "AUTO",
type: "url-test",
tolerance: 50,
"include-all": true,
icon: `${BASE_ICON_SET_URL}Auto.png`,
hidden: true,
},
...generateStaticProxyGroups(proxyGroupDefaults),
...generateServiceProxyGroups(services, proxyGroupDefaults),
{
...proxyGroupDefaults,
name: "Others",
type: "select",
proxies: [...serviceProxyGroupProxies],
icon: `${BASE_ICON_SET_URL}Final.png`,
},
{
...proxyGroupDefaults,
name: "Advertising",
type: "select",
proxies: ["REJECT", "DIRECT", ...staticProxyNames],
icon: `${BASE_ICON_SET_URL}Advertising.png`,
},
{
...locationPolicyProxyGroupDefaults,
name: "Mainland China 🇨🇳",
type: "select",
icon: `${LOCATION_ICON_SET_URL}china`,
proxies: ["DIRECT"],
"include-all": true,
filter:
"(?i)\u5927\u9646|\u4e2d\u56fd|\u7535\u4fe1|\u79fb\u52a8|\u8054\u901a|Mainland|China|CN|mainland|china|cn|🇨🇳",
},
...generateLocationPolicyProxyGroups(
locations,
locationPolicyProxyGroupDefaults,
"url-test",
{
tolerance: 50,
}
),
...generateLocationPolicyProxyGroups(
locations,
locationPolicyProxyGroupDefaults,
"fallback"
),
...generateLocationPolicyProxyGroups(
locations,
locationPolicyProxyGroupDefaults,
"load-balance",
{
strategy: "round-robin",
}
),
...generateLocationPolicyProxyGroups(
locations,
locationPolicyProxyGroupDefaults,
"load-balance",
{
strategy: "consistent-hashing",
}
),
...generateLocationPolicyProxyGroups(
locations,
locationPolicyProxyGroupDefaults,
"load-balance",
{
strategy: "sticky-sessions",
}
),
...generateLocationSelectProxyGroups(),
];
// Routing Rules
function generateServiceRules(services) {
return services.map(({ name }) => `RULE-SET,${name.toLowerCase()},${name}`);
}
const rules = [
"RULE-SET,applications,DIRECT",
"RULE-SET,win-phonelink,DIRECT",
"RULE-SET,lancidr,DIRECT,no-resolve",
"RULE-SET,private,DIRECT",
...generateServiceRules(services),
"RULE-SET,reject,Advertising",
"RULE-SET,win-spy,Advertising",
"RULE-SET,direct,Mainland China 🇨🇳",
"RULE-SET,proxy,PROXY",
"RULE-SET,telegramcidr,Telegram",
"RULE-SET,xcidr,X",
"RULE-SET,googlecidr,Google",
"RULE-SET,cloudflarecidr,Cloudflare",
"RULE-SET,cncidr,Mainland China 🇨🇳",
"MATCH,Others",
];
// Rule Providers
const ruleProviderDefaults = {
type: "http",
interval: 14400,
};
function generateServiceRuleProviders(services, defaultConfig) {
return services.reduce((acc, { name, alias }) => {
const ruleName = alias || name;
acc[name.toLowerCase()] = {
...defaultConfig,
format: "mrs",
behavior: "domain",
url: `https://cdn.jsdelivr.net/gh/xixu-me/RFM@universal/${ruleName.toLowerCase()}.mrs`,
path: `./rulesets/${name.toLowerCase()}.mrs`,
};
return acc;
}, {});
}
const ruleProviders = {
"fake-ip-filter": {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/fake-ip-filter.mrs",
path: "./rulesets/fake-ip-filter.mrs",
},
"connectivity-check": {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@universal/connectivity-check.mrs",
path: "./rulesets/connectivity-check.mrs",
},
applications: {
...ruleProviderDefaults,
format: "yaml",
behavior: "classical",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/applications.yaml",
path: "./rulesets/applications.yaml",
},
"win-phonelink": {
...ruleProviderDefaults,
format: "yaml",
behavior: "classical",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@fixed/win-phonelink.yaml",
path: "./rulesets/win-phonelink.yaml",
},
lancidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/lancidr.mrs",
path: "./rulesets/lancidr.mrs",
},
cncidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/cncidr.mrs",
path: "./rulesets/cncidr.mrs",
},
cloudflarecidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/cloudflarecidr.mrs",
path: "./rulesets/cloudflarecidr.mrs",
},
googlecidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/googlecidr.mrs",
path: "./rulesets/googlecidr.mrs",
},
telegramcidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/telegramcidr.mrs",
path: "./rulesets/telegramcidr.mrs",
},
xcidr: {
...ruleProviderDefaults,
format: "mrs",
behavior: "ipcidr",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/xcidr.mrs",
path: "./rulesets/xcidr.mrs",
},
private: {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/private.mrs",
path: "./rulesets/private.mrs",
},
direct: {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/direct.mrs",
path: "./rulesets/direct.mrs",
},
proxy: {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/proxy.mrs",
path: "./rulesets/proxy.mrs",
},
"geolocation-cn": {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@universal/geolocation-cn.mrs",
path: "./rulesets/geolocation-cn.mrs",
},
reject: {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@basic/reject.mrs",
path: "./rulesets/reject.mrs",
},
"win-spy": {
...ruleProviderDefaults,
format: "mrs",
behavior: "domain",
url: "https://cdn.jsdelivr.net/gh/xixu-me/RFM@universal/win-spy.mrs",
path: "./rulesets/win-spy.mrs",
},
...generateServiceRuleProviders(services, ruleProviderDefaults),
};
// generate configuration using the above settings
function validateOriginalConfig(config) {
if (!config)
throw new Error("Configuration object cannot be null or undefined");
const proxyCount = Array.isArray(config.proxies) ? config.proxies.length : 0;
const proxyProviderCount = Object.keys(
config["proxy-providers"] || {}
).length;
if (proxyCount === 0 && proxyProviderCount === 0)
throw new Error(
"The original configuration must contain a non-empty proxies array (see https://wiki.\u006D\u0065\u0074\u0061\u0063\u0075\u0062\u0065\u0078.one/en/config/proxies/) or a proxy-providers object with at least one property (see https://wiki.\u006D\u0065\u0074\u0061\u0063\u0075\u0062\u0065\u0078.one/en/config/proxy-providers/)"
);
if (proxyCount > 0) {
config.proxies.forEach((proxy, index) => {
if (!proxy.name || !proxy.type || !proxy.server || !proxy.port) {
throw new Error(
`Invalid proxy number ${
index + 1
} configuration (see https://wiki.\u006D\u0065\u0074\u0061\u0063\u0075\u0062\u0065\u0078.one/en/config/proxies/)`
);
}
});
console.log(
`The original configuration contains ${proxyCount} proxies, which will be preserved`
);
}
if (proxyProviderCount > 0) {
Object.entries(config["proxy-providers"]).forEach(
([name, provider], index) => {
if (!provider.type || (provider.type === "http" && !provider.url)) {
throw new Error(
`Invalid proxy provider number ${
index + 1
} configuration (see https://wiki.\u006D\u0065\u0074\u0061\u0063\u0075\u0062\u0065\u0078.one/en/config/proxy-providers/)`
);
}
}
);
console.log(
`The original configuration contains ${proxyProviderCount} proxy providers, which will be preserved`
);
}
return true;
}
function main(config) {
try {
validateOriginalConfig(config);
Object.assign(config, generalConfig, {
dns,
hosts,
sniffer,
tun,
"proxy-groups": proxyGroups,
rules,
"rule-providers": ruleProviders,
});
console.log("The generated configuration is as follows");
console.log(config);
return config;
} catch (error) {
console.error(
`An error occurred during configuration generation: ${error.message}`
);
return { error: error.message, originalConfig: config };
}
}