clash全局脚本

moxiaoying
3月22日发布 /正在检测是否收录...
// ==================== 用户配置区域 ====================
/**
 * 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 || {};
    }
}
喜欢就支持一下吧
点赞 0 分享 收藏
评论
所有页面的评论已关闭