Skip to content

Instantly share code, notes, and snippets.

@NHZEX
Last active July 14, 2025 15:28
Show Gist options
  • Save NHZEX/d32864d66a4c5bbcd273d669d271e2c8 to your computer and use it in GitHub Desktop.
Save NHZEX/d32864d66a4c5bbcd273d669d271e2c8 to your computer and use it in GitHub Desktop.
clash 自定义配置
// Define main function (script entry)
// 规则
const prependRules = [
'DOMAIN-SUFFIX,test,DIRECT,no-resolve',
'DOMAIN-SUFFIX,local,DIRECT,no-resolve',
'DOMAIN,cn.download.nvidia.com,DIRECT',
'DOMAIN,docker.1panel.live,DIRECT',
'DOMAIN-SUFFIX,dockerhub.icu,DIRECT',
'DOMAIN-SUFFIX,cursor.sh,[R]Cursor', // DIRECT
'DOMAIN-SUFFIX,jetbrains.com,[R]Jetbrains',
'DOMAIN,download-cdn.jetbrains.com,DIRECT',
'DOMAIN,download.jetbrains.com,DIRECT',
];
const appendRules = [];
const dns = {
'use-system-hosts': true,
'fake-ip-filter': [
"+.lan",
"+.test",
"+.local",
],
}
const defaultGroupOpts = {
url: 'http://cp.cloudflare.com',
interval: 35,
tolerance: 100,
lazy: true
}
const autoGroupSet = [
{ name: '[AUTO]香港', match: (name) => testStrMatch(name, ['香港', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]香港中转', match: (name) => testStrMatch(name, ['香港', '中转', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]台湾', match: (name) => testStrMatch(name, ['台湾', { not: '家宽' }, { not: 'IEPL' }], 'all'), type: 'url-test' },
{ name: '[AUTO]台湾IEPL', match: (name) => testStrMatch(name, ['台湾', 'IEPL', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]日本', match: (name) => testStrMatch(name, ['日本', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]日本IEPL', match: (name) => testStrMatch(name, ['日本', 'IEPL', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]韩国', match: (name) => testStrMatch(name, ['韩国', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]新加坡', match: (name) => testStrMatch(name, ['新加坡', { not: '家宽' }], 'all'), type: 'url-test' },
{ name: '[AUTO]美国', match: (name) => testStrMatch(name, ['美国', { not: '家宽' }], 'all'), type: 'url-test' },
];
const manualGroupSet = [
{ name: '[R]Cursor', match: () => false, type: 'url-test', appendProxies: ['[AUTO]香港', '[AUTO]台湾', '[AUTO]日本', '[AUTO]韩国', '[AUTO]新加坡', '[AUTO]美国'] },
{ name: '[R]Jetbrains', match: () => false, type: 'url-test', appendProxies: ['[AUTO]香港', '[AUTO]台湾', '[AUTO]日本', '[AUTO]韩国', '[AUTO]新加坡', '[AUTO]美国'] },
];
function buildGroups(proxies, buildSetRules) {
const addGroups = [];
for (const _g of buildSetRules) {
const groupProxies = []
for (const proxie of proxies) {
if (_g.match(proxie.name)) {
groupProxies.push(proxie.name)
}
}
if (_g.appendProxies && Array.isArray(_g.appendProxies)) {
groupProxies.push(..._g.appendProxies)
}
const groupInfo = {
name: _g.name,
type: _g.type,
proxies: groupProxies,
...defaultGroupOpts,
};
if (groupProxies.length > 0) {
addGroups.push(groupInfo)
}
}
return addGroups
}
function main(config, profileName) {
// 在数组末尾追加原本的配置
config.rules.unshift(...prependRules);
config.rules.push(...appendRules);
const addProxieGroups = buildGroups(config.proxies, autoGroupSet);
let proxyGroups = config['proxy-groups'] ?? []
if (addProxieGroups.length > 0) {
proxyGroups = insertSubArray(proxyGroups, 1, addProxieGroups)
const addGroupNames = addProxieGroups.map(v => v.name)
proxyGroups[0].proxies = insertSubArray(proxyGroups[0].proxies, 0, addGroupNames)
}
const addManualGroups = buildGroups(config.proxies, manualGroupSet);
if (addManualGroups.length > 0) {
proxyGroups.push(...addManualGroups);
}
// 覆盖配置
config.dns = {
...config['dns'],
...dns,
}
config['proxy-groups'] = proxyGroups
return config;
}
/**
* 测试字符串是否匹配给定条件(支持肯定/否定匹配,字符串/正则,任一/全部模式)
* @param {string} str - 要测试的目标字符串
* @param {string|RegExp|Object|Array<string|RegExp|Object>} patterns - 匹配模式
* - 字符串: 要求包含该字符串
* - 正则: 要求匹配该正则
* - 对象 { not: string|RegExp }: 要求不包含/不匹配该模式
* @param {string} [mode='any'] - 匹配模式: 'any'(任一匹配) 或 'all'(全部匹配)
* @returns {boolean} - 根据匹配模式返回测试结果
* @throws {TypeError} 当模式参数无效或匹配项类型错误时抛出异常
*/
function testStrMatch(str, patterns, mode = 'any') {
// 验证模式参数
const validModes = ['any', 'all'];
if (!validModes.includes(mode)) {
throw new Error(`无效的匹配模式: '${mode}'。请使用 'any' 或 'all'`);
}
// 处理无效输入
if (typeof str !== 'string') return false;
if (patterns === null || patterns === undefined) return false;
// 统一处理为数组形式
const testPatterns = Array.isArray(patterns) ? patterns : [patterns];
// 空数组处理
if (testPatterns.length === 0) {
return mode === 'all'; // 空数组时:all模式返回true,any模式返回false
}
// 根据模式进行测试
return testPatterns[mode === 'any' ? 'some' : 'every'](item => {
// 处理否定匹配(要求不存在)
if (typeof item === 'object' && item !== null && 'not' in item) {
const exclusion = item.not;
if (typeof exclusion === 'string') {
return !str.includes(exclusion);
}
else if (exclusion instanceof RegExp) {
return !exclusion.test(str);
}
else {
throw new TypeError('否定匹配的值必须是字符串或正则表达式');
}
}
// 处理肯定匹配(要求存在)
else if (typeof item === 'string') {
return str.includes(item);
}
else if (item instanceof RegExp) {
return item.test(str);
}
else {
throw new TypeError('匹配模式必须是字符串、正则表达式或包含"not"属性的对象');
}
});
}
/**
* 在数组的指定位置插入子数组
* @param {Array} arr - 原始数组
* @param {number} index - 插入位置的索引
* @param {Array} subArray - 要插入的子数组
* @returns {Array} - 插入后的新数组(不修改原数组)
*/
function insertSubArray(arr, index, subArray) {
// 创建原数组的副本(避免直接修改)
const newArr = [...arr];
// 使用 splice 在指定位置插入(扩展运算符展开子数组)
newArr.splice(index, 0, ...subArray);
return newArr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment