// ==================== 用户配置区域 ====================
/**
* Clash配置生成器 - 简化版
*
* 配置说明:
* 1. rules - 字符串格式:
* - 不指定: 使用默认规则 (name.yaml)
* - 以.yaml结尾: 自定义路径 (如: "GitHub/GitHub.yaml")
* - 其他字符串: 规则变体 (如: "Classical")
*
* 2. proxy - 字符串格式:
* - "DIRECT": 直连
* - "REJECT": 拒绝连接
* - 区域节点名称: 使用特定区域节点 (如: "香港节点")
* - 其他字符串: 使用指定的代理组
*
* 3. icon - 图标配置:
* - 字符串名称: 使用预定义图标 (如: "GitHub")
* - URL: 使用自定义图标
*/
/**
* @typedef {Object} RuleProvider
* @property {string} name - 规则名称
* @property {string} [icon] - 分组图标
* @property {string} [proxy] - 代理设置,支持 "DIRECT"、"REJECT" 或分组名称
* @property {string} [rules] - 规则配置,支持以下格式:
* - 不指定: 使用默认规则 (name.yaml)
* - 以 .yaml 结尾: 视为自定义路径
* - 其他字符串: 视为规则变体名称 (name_variant.yaml)
*/
/**
* @typedef {Object} ProxyGroupOptions
* @property {number} [interval] - 测速间隔(秒)
* @property {number} [timeout] - 连接超时(毫秒)
* @property {string} [url] - 测速URL
* @property {boolean} [lazy] - 是否懒加载
*/
/**
* @typedef {Object} UserConfig
* @property {string[]} [customRules] - 自定义规则,优先级最高
* @property {string} [excludeRegex] - 需要排除的节点名称正则
* @property {RuleProvider[]} [ruleProviders] - 规则提供者配置
* @property {ProxyGroupOptions} [proxyGroupOptions] - 代理组通用配置
* @property {string} [iconCDN] - 图标CDN配置
* @property {Object} [dns] - DNS配置
*/
/**
* 用户配置对象
* @type {UserConfig}
*/
const userConfig = {
// 自定义规则 - 优先级最高
customRules: [
"IP-CIDR, 11.48.0.0/16,DIRECT,no-resolve",
"IP-CIDR, 10.57.0.0/16,DIRECT,no-resolve",
"IP-CIDR, 100.192.3.185/24,DIRECT,no-resolve",
...createWhiteDomainList([
"clivia.fun",
"baidu.com",
"360.net",
"hf.space",
"linux.do",
"pages.dev",
"nbcharts.com",
"workers.dev",
'deno.dev'
])
],
/**
* 规则提供者配置
* 规则名称参考: https://github.com/blackmatrix7/ios_rule_script/tree/master/rule/Clash#分类
* 图标获取参考: https://www.clashverge.dev/guide/group_icon/icon_sets/Qure.html
*
* @type {RuleProvider[]}
*/
ruleProviders: [
// 使用规则变体
{ name: 'ChinaMax', icon: 'China', rules: 'Classical', proxy: 'DIRECT' },
// 默认规则
{ name: 'Google' },
// 使用自定义图标和特定区域节点
{ name: 'Gemini', icon: 'https://registry.npmmirror.com/@lobehub/icons-static-png/latest/files/light/gemini-color.png', proxy: '非港澳节点' },
{ name: 'YouTube' },
{ name: 'Telegram' },
// 直连规则
{ name: 'Microsoft', icon: 'Microsoft', proxy: 'DIRECT' },
// 使用自定义路径
{ name: 'GitHub', rules: 'GitHub/GitHub.yaml' },
{ name: 'Cloudflare', proxy: 'DIRECT' },
{ name: '360', icon: 'Ninja', proxy: 'DIRECT' },
]
};
// ==================== 常量定义 ====================
const CONSTANTS = {
// 区域正则定义
REGION_REGEX: {
HONG_KONG: {
name: "香港节点",
regex: /港|HK|hk|Hong Kong|HongKong|hongkong|Hongkong|🇭🇰/u,
icon: "Hong_Kong"
},
TAIWAN: {
name: "台湾节点",
regex: /台|新北|彰化|TW|Taiwan/u,
icon: "TW"
},
US: {
name: "美国节点",
regex: /美|波特兰|达拉斯|俄勒冈|凤凰城|费利蒙|硅谷|拉斯维加斯|洛杉矶|圣何塞|圣克拉拉|西雅图|芝加哥|US|United States/u,
icon: "United_States"
},
JAPAN: {
name: "日本节点",
regex: /日本|川日|东京|大阪|泉日|埼玉|沪日|深日|JP|Japan|Tokyo/u,
icon: "Japan"
},
SINGAPORE: {
name: "狮城节点",
regex: /新加坡|坡|狮城|SG|Singapore/u,
icon: "Singapore"
},
NON_HK_MO: {
name: "非港澳节点",
regex: /^(?!.*(?:🇭🇰|🇲🇴|港|澳|hk|mo|hong kong|macau)).*/ui,
icon: "Area"
}
},
// 常用代理组名称
PROXY_GROUPS: {
AUTO_SELECT: "自动选择",
GLOBAL_DIRECT: "全球直连",
AD_BLOCK: "广告拦截",
FINAL: "漏网之鱼"
},
// 规则提供者基础URL
RULE_PROVIDER_BASE_URL: "https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash"
};
// ==================== 默认配置 ====================
const defaultConfig = {
// 自定义规则 - 优先级最高
customRules: [],
// 需要排除的节点名称正则
excludeRegex: "(?!.*(官网|网站|流量|套餐|到期|剩余|更新|产品|官方|客服|联系|邮箱|工单|群|频道|公告|推荐|返利|充值|付费|提示|警告|维护|测试|过期|失效)).*",
// 规则提供者配置
ruleProviders: [],
// 代理组通用配置
proxyGroupOptions: {
interval: 300,
timeout: 3000,
url: "https://www.google.com/generate_204",
// lazy: true
},
// 图标CDN配置
iconCDN: "https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color",
// DNS配置
dns: {
"enable": true,
"listen": "0.0.0.0:1053",
"ipv6": true,
"use-system-hosts": false,
"cache-algorithm": "arc",
"enhanced-mode": "fake-ip",
"fake-ip-range": "198.18.0.1/16",
"fake-ip-filter": [
"+.lan",
"+.local",
"+.msftconnecttest.com",
"+.msftncsi.com",
"localhost.ptlogin2.qq.com",
"localhost.sec.qq.com",
"localhost.work.weixin.qq.com"
],
"default-nameserver": [
"223.5.5.5",
"119.29.29.29",
"1.1.1.1",
"8.8.8.8"
],
"nameserver": [
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
"https://doh.360.cn/dns-query",
"https://1.1.1.1/dns-query",
"https://1.0.0.1/dns-query",
"https://208.67.222.222/dns-query",
"https://208.67.220.220/dns-query",
"https://194.242.2.2/dns-query",
"https://194.242.2.3/dns-query"
],
"proxy-server-nameserver": [
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
"https://doh.360.cn/dns-query",
"https://1.1.1.1/dns-query",
"https://1.0.0.1/dns-query",
"https://208.67.222.222/dns-query",
"https://208.67.220.220/dns-query",
"https://194.242.2.2/dns-query",
"https://194.242.2.3/dns-query"
],
"nameserver-policy": {
"geosite:private,cn,geolocation-cn": [
"https://dns.alidns.com/dns-query",
"https://doh.pub/dns-query",
"https://doh.360.cn/dns-query"
],
"geosite:google,youtube,telegram,gfw,geolocation-!cn": [
"https://1.1.1.1/dns-query",
"https://1.0.0.1/dns-query",
"https://208.67.222.222/dns-query",
"https://208.67.220.220/dns-query",
"https://194.242.2.2/dns-query",
"https://194.242.2.3/dns-query"
]
}
}
};
// ==================== 工具函数 ====================
/**
* 创建白名单域名列表
* @param {string[]} domainList - 域名列表
* @returns {string[]} - 白名单域名列表
*/
function createWhiteDomainList(domainList) {
return domainList.map(domain => `DOMAIN-SUFFIX,${domain},DIRECT`);
}
/**
* 工具函数集合
*/
const utils = {
/**
* 将任何值标准化为数组
* @param {*} value - 输入值
* @return {Array} 标准化后的数组
*/
toArray(value) {
return value ? (Array.isArray(value) ? value : [value]) : [];
},
/**
* 获取代理动作
* @param {string} proxy - 代理设置
* @param {string} groupName - 分组名称
* @return {string} - 处理后的代理配置
*/
getProxyAction(proxy, groupName) {
// 如果未指定代理,使用组名
if (proxy === undefined || proxy === null) {
return groupName;
}
// 只处理字符串类型
if (typeof proxy === 'string') {
const proxyValue = proxy.trim();
// 如果是空字符串,返回DIRECT
if (proxyValue === '') {
return 'DIRECT';
}
return proxyValue;
}
// 默认返回组名
return groupName;
},
/**
* 判断是否为URL
* @param {string} url - 待检测的字符串
* @returns {boolean} 是否为URL
*/
isUrl(url) {
if (typeof url !== 'string') return false;
return /^https?:\/\//i.test(url);
},
/**
* 判断是否为对象
* @param {*} item - 待检测的项
* @returns {boolean} 是否为对象
*/
isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
},
/**
* 合并对象,深度合并
* @param {Object} target - 目标对象
* @param {Object} source - 源对象
* @returns {Object} 合并后的对象
*/
mergeObjects(target, source) {
if (!source) return target;
if (!this.isObject(source)) return source;
if (!this.isObject(target)) {
return JSON.parse(JSON.stringify(source));
}
const merged = Object.assign({}, target);
Object.keys(source).forEach(key => {
const sourceValue = source[key];
if (this.isObject(sourceValue) && !Array.isArray(sourceValue)) {
if (!target[key] || !this.isObject(target[key])) {
merged[key] = JSON.parse(JSON.stringify(sourceValue));
} else {
merged[key] = this.mergeObjects(target[key], sourceValue);
}
}
else if (Array.isArray(sourceValue)) {
merged[key] = JSON.parse(JSON.stringify(sourceValue));
}
else {
merged[key] = sourceValue;
}
});
return merged;
},
/**
* 编译正则表达式
* @param {string|RegExp} pattern - 正则表达式或模式字符串
* @param {string} [flags] - 正则表达式标志
* @returns {RegExp} 编译后的正则表达式
*/
compileRegex(pattern, flags) {
if (pattern instanceof RegExp) {
return pattern;
}
if (typeof pattern !== 'string') {
return new RegExp('.*');
}
try {
return new RegExp(pattern, flags);
} catch (e) {
return new RegExp('.*');
}
}
};
/**
* 图标管理类
*/
class IconManager {
/**
* 创建图标管理器
* @param {UserConfig} config - 用户配置
*/
constructor(config) {
this.config = config || {};
this.iconCache = new Map();
this.defaultIcon = `${this.config.iconCDN || defaultConfig.iconCDN}/Global.png`;
}
/**
* 获取图标URL
* @param {string} iconName - 图标名称
* @returns {string} 图标URL
*/
getIcon(iconName) {
if (iconName === null || iconName === undefined) {
return this.defaultIcon;
}
const cacheKey = String(iconName);
if (this.iconCache.has(cacheKey)) {
return this.iconCache.get(cacheKey);
}
let iconUrl;
if (utils.isUrl(iconName)) {
iconUrl = iconName;
} else if (!iconName) {
iconUrl = this.defaultIcon;
} else {
const iconCDN = this.config.iconCDN || defaultConfig.iconCDN;
iconUrl = `${iconCDN}/${iconName}.png`;
}
this.iconCache.set(cacheKey, iconUrl);
return iconUrl;
}
/**
* 预加载常用图标到缓存
*/
preloadCommonIcons() {
const commonIcons = ['Direct', 'AdBlack', 'Final', 'Global', 'Auto'];
commonIcons.forEach(name => {
this.getIcon(name);
});
Object.values(CONSTANTS.REGION_REGEX).forEach(region => {
this.getIcon(region.icon);
});
}
}
/**
* 规则管理类
*/
class RuleManager {
/**
* 创建规则管理器
* @param {UserConfig} config - 用户配置
* @param {IconManager} iconManager - 图标管理器
*/
constructor(config, iconManager) {
this.config = config || {};
this.iconManager = iconManager;
}
/**
* 获取规则文件URL
* @param {string} name - 规则名称
* @param {string} rules - 规则配置
* @returns {string} 规则文件URL
* @private
*/
_getRuleUrl(name, rules) {
const baseUrl = CONSTANTS.RULE_PROVIDER_BASE_URL;
// 如果未提供rules,使用默认规则
if (!rules) {
return `${baseUrl}/${name}/${name}.yaml`;
}
// 如果rules以.yaml结尾,视为自定义路径
if (typeof rules === 'string' && rules.endsWith('.yaml')) {
return `${baseUrl}/${rules}`;
}
// 其他字符串视为变体名称
if (typeof rules === 'string') {
return `${baseUrl}/${name}/${name}_${rules}.yaml`;
}
// 默认返回
return `${baseUrl}/${name}/${name}.yaml`;
}
/**
* 获取规则文件本地路径
* @param {string} name - 规则名称
* @param {string} rules - 规则配置
* @returns {string} 本地路径
* @private
*/
_getLocalPath(name, rules) {
// 如果未提供rules,使用默认规则
if (!rules) {
return `./ruleset/tnnevol/${name}.yaml`;
}
// 如果rules以.yaml结尾,提取文件名部分
if (typeof rules === 'string' && rules.endsWith('.yaml')) {
const parts = rules.split('/');
return `./ruleset/tnnevol/${parts[parts.length - 1]}`;
}
// 其他字符串视为变体名称
if (typeof rules === 'string') {
return `./ruleset/tnnevol/${name}_${rules}.yaml`;
}
// 默认返回
return `./ruleset/tnnevol/${name}.yaml`;
}
/**
* 创建规则提供者配置
* @returns {Object} 规则提供者配置对象
*/
createRuleProviders() {
const providers = {};
const ruleProviders = this.config.ruleProviders || [];
ruleProviders.forEach(({ name, icon, rules }) => {
if (!name) return;
providers[name] = {
type: "http",
format: "yaml",
interval: 86400,
behavior: "classical",
url: this._getRuleUrl(name, rules),
icon: this.iconManager.getIcon(icon || name),
path: this._getLocalPath(name, rules)
};
});
return providers;
}
/**
* 创建规则列表
* @returns {string[]} 规则列表
*/
createRules() {
const ruleProviders = this.config.ruleProviders || [];
const rules = [];
for (let i = 0; i < ruleProviders.length; i++) {
const item = ruleProviders[i];
if (!item || !item.name) continue;
const action = utils.getProxyAction(item.proxy, item.name);
rules.push(`RULE-SET,${item.name},${action}`);
}
return rules;
}
/**
* 生成完整规则列表
* @returns {string[]} 完整规则列表
*/
generateRules() {
const pgNames = CONSTANTS.PROXY_GROUPS;
const customRules = this.config.customRules || [];
const ruleProviderRules = this.createRules();
const rules = [
...customRules,
...ruleProviderRules,
`GEOSITE,cn,${pgNames.GLOBAL_DIRECT},DIRECT`,
`GEOSITE,google-cn,${pgNames.GLOBAL_DIRECT},DIRECT`,
`GEOSITE,category-games@cn,${pgNames.GLOBAL_DIRECT},DIRECT`,
`GEOSITE,category-public-tracker,${pgNames.GLOBAL_DIRECT},DIRECT`,
`GEOSITE,gfw,${pgNames.AUTO_SELECT},PROXY`,
`GEOIP,LAN,${pgNames.GLOBAL_DIRECT},no-resolve`,
`GEOIP,CN,${pgNames.GLOBAL_DIRECT},no-resolve`,
`MATCH,${pgNames.FINAL}`
];
return rules;
}
}
/**
* 代理组管理类
*/
class ProxyGroupManager {
/**
* 创建代理组管理器
* @param {UserConfig} config - 用户配置
* @param {IconManager} iconManager - 图标管理器
*/
constructor(config, iconManager) {
this.config = config || {};
this.iconManager = iconManager;
this.excludeRegex = utils.compileRegex(this.config.excludeRegex || "", "u");
this._regionDefinitions = Object.values(CONSTANTS.REGION_REGEX);
}
/**
* 获取符合正则表达式的代理列表
* @param {Array} proxies - 代理列表
* @param {string|RegExp} regexStr - 正则表达式或模式
* @param {Array} [concatProxies=[]] - 要合并的额外代理
* @returns {Array} 筛选后的代理列表
*/
getProxiesByRegex(proxies, regexStr, concatProxies = []) {
if (!proxies || !Array.isArray(proxies)) {
return [...concatProxies];
}
const regex = utils.compileRegex(regexStr, "u");
const filteredProxies = [];
for (let i = 0; i < proxies.length; i++) {
const proxy = proxies[i];
if (proxy && proxy.name &&
regex.test(proxy.name) &&
this.excludeRegex.test(proxy.name)) {
filteredProxies.push(proxy.name);
}
}
if (concatProxies.length > 0) {
filteredProxies.push(...concatProxies);
}
return filteredProxies;
}
/**
* 创建区域分组
* @param {Array} proxies - 代理列表
* @returns {Array} 区域分组列表
*/
createRegionGroups(proxies) {
if (!proxies || !Array.isArray(proxies)) {
proxies = [];
}
const regionGroups = [];
for (let i = 0; i < this._regionDefinitions.length; i++) {
const region = this._regionDefinitions[i];
const regionProxies = this.getProxiesByRegex(
proxies,
region.regex
);
if (regionProxies.length === 0) continue;
regionGroups.push({
...this.config.proxyGroupOptions,
name: region.name,
type: "url-test",
tolerance: 30,
proxies: regionProxies,
icon: this.iconManager.getIcon(region.icon)
});
}
return regionGroups;
}
/**
* 创建基础代理组
* @param {Array} regionGroups - 区域分组
* @returns {Array} 代理组列表
*/
createBaseProxyGroups(regionGroups) {
if (!regionGroups) {
regionGroups = [];
}
const pgNames = CONSTANTS.PROXY_GROUPS;
const regionGroupNames = regionGroups.map(group => group.name);
// 简化的公共代理列表,只包含自动选择和区域组
const commonProxies = [
pgNames.AUTO_SELECT,
...regionGroupNames,
"DIRECT",
];
// 简化的基础分组,只保留自动选择
const baseGroups = [
{
...this.config.proxyGroupOptions,
name: pgNames.AUTO_SELECT,
type: "url-test",
tolerance: 100,
filter: this.config.excludeRegex,
"include-all": true,
icon: this.iconManager.getIcon("Auto")
}
];
const ruleProviders = this.config.ruleProviders || [];
const ruleGroups = [];
for (let i = 0; i < ruleProviders.length; i++) {
const item = ruleProviders[i];
if (!item || !item.name) continue;
// 根据proxy设置决定代理列表
let proxyList;
if (item.proxy === "DIRECT") {
proxyList = ["DIRECT"];
} else if (item.proxy === "REJECT") {
proxyList = ["REJECT"];
} else if (typeof item.proxy === 'string' && item.proxy.trim() !== '') {
const proxyName = item.proxy.trim();
// 检查是否是已存在于commonProxies中的代理组名称
if (commonProxies.includes(proxyName)) {
// 如果是,使用过滤后的代理列表(将指定的代理放在首位)
proxyList = [proxyName, ...commonProxies.filter(p => p !== proxyName)];
} else {
// 如果不是,将其添加到列表首位
proxyList = [proxyName, ...commonProxies];
}
} else {
// 默认使用所有代理
proxyList = commonProxies;
}
ruleGroups.push({
...this.config.proxyGroupOptions,
name: item.name,
type: "select",
proxies: proxyList,
icon: this.iconManager.getIcon(item.icon || item.name)
});
}
// 简化特殊分组
const specialGroups = [
{
...this.config.proxyGroupOptions,
name: pgNames.GLOBAL_DIRECT,
type: "select",
proxies: ["DIRECT", pgNames.AUTO_SELECT],
icon: this.iconManager.getIcon("Direct")
},
{
...this.config.proxyGroupOptions,
name: pgNames.AD_BLOCK,
type: "select",
proxies: ["REJECT", "DIRECT"],
icon: this.iconManager.getIcon("AdBlack")
},
{
...this.config.proxyGroupOptions,
name: pgNames.FINAL,
type: "select",
proxies: commonProxies,
icon: this.iconManager.getIcon("Final")
}
];
return [
...baseGroups,
...ruleGroups,
...specialGroups,
...regionGroups
];
}
}
/**
* Clash配置生成器类
*/
class ClashConfigGenerator {
/**
* 创建Clash配置生成器
* @param {UserConfig} userConfig - 用户配置
*/
constructor(userConfig = {}) {
this.config = utils.mergeObjects(defaultConfig, userConfig);
this.iconManager = new IconManager(this.config);
this.ruleManager = new RuleManager(this.config, this.iconManager);
this.proxyGroupManager = new ProxyGroupManager(this.config, this.iconManager);
this.iconManager.preloadCommonIcons();
}
/**
* 生成Clash配置
* @param {Object} clashConfig - 原始Clash配置
* @returns {Object} 处理后的Clash配置
*/
generate(clashConfig) {
try {
if (!clashConfig) {
clashConfig = {};
}
const proxies = Array.isArray(clashConfig.proxies) ? clashConfig.proxies : [];
const regionGroups = this.proxyGroupManager.createRegionGroups(proxies);
const allProxyGroups = this.proxyGroupManager.createBaseProxyGroups(regionGroups);
const ruleProviders = this.ruleManager.createRuleProviders();
const rules = this.ruleManager.generateRules();
return Object.assign({}, clashConfig, {
dns: this.config.dns,
"rule-providers": ruleProviders,
rules: rules,
"proxy-groups": allProxyGroups
});
} catch (error) {
return clashConfig || {};
}
}
}
/**
* 主函数 - 处理配置
* @param {Object} config - 原始配置
* @returns {Object} 处理后的配置
*/
function main(config) {
try {
if (!main.generator) {
main.generator = new ClashConfigGenerator(userConfig);
}
return main.generator.generate(config);
} catch (error) {
return config || {};
}
}
版权属于:moxiaoying
作品采用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权