feat(substore): 新增 YinYun 订阅重命名脚本

* 新增 yinyun-rename.js 专门处理 YinYun 节点命名
* 实现中文地区名转短码及地区内连续编号逻辑
* 忽略优选、(T)/(R) 线路标记等原始冗余信息
* 更新 README 文档,补充 YinYun 脚本职责与规则说明
* 在文档注意事项中增加对新脚本的使用建议说明
This commit is contained in:
2026-07-02 23:01:28 +08:00
parent 868e2b0931
commit 45a1b883a6
2 changed files with 152 additions and 2 deletions
+126
View File
@@ -0,0 +1,126 @@
/**
* YinYun 专用节点清理脚本
* - 将中文地区名压成国家短码并补齐国旗
* - 忽略 YinYun 原始的 优选、数字编号和 (T)/(R) 线路标记
* - 按地区出现顺序连续编号,方便全局 rename.js 再追加订阅后缀
*/
// noinspection JSUnusedGlobalSymbols
const COMMON_SCRIPT_VERSION = '20260608-1';
const COMMON_SCRIPT_URL = `https://git.orionc.me/orion/script/raw/branch/main/substore/common.js?v=${COMMON_SCRIPT_VERSION}`;
const hasCurrentRenameUtils = utils => Boolean(
utils && utils.version === COMMON_SCRIPT_VERSION
);
async function loadRenameUtils() {
if (typeof globalThis !== 'undefined' && hasCurrentRenameUtils(globalThis.SubStoreRenameUtils)) {
return globalThis.SubStoreRenameUtils;
}
const cache = typeof scriptResourceCache !== 'undefined' ? scriptResourceCache : null;
let script = cache && cache.get(COMMON_SCRIPT_URL);
if (!script) {
const response = await $substore.http.get({ url: COMMON_SCRIPT_URL });
script = response && (response.body || response.data || response);
if (cache) cache.set(COMMON_SCRIPT_URL, script);
}
new Function(String(script))();
if (!hasCurrentRenameUtils(globalThis.SubStoreRenameUtils)) {
throw new Error('Loaded common.js is missing current rename utilities');
}
return globalThis.SubStoreRenameUtils;
}
async function operator(proxies) {
const utils = await loadRenameUtils();
const regionAliases = [
['哈萨克斯坦', 'KZ'],
['印度尼西亚', 'ID'],
['孟加拉国', 'BD'],
['澳大利亚', 'AU'],
['保加利亚', 'BG'],
['格鲁吉亚', 'GE'],
['立陶宛', 'LT'],
['俄罗斯', 'RU'],
['新加坡', 'SG'],
['马来西亚', 'MY'],
['罗马尼亚', 'RO'],
['亚美尼亚', 'AM'],
['葡萄牙', 'PT'],
['拉脱维亚', 'LV'],
['加拿大', 'CA'],
['加拿大 01', 'CA'],
['澳洲', 'AU'],
['捷克', 'CZ'],
['南非', 'ZA'],
['芬兰', 'FI'],
['瑞士', 'CH'],
['墨西哥', 'MX'],
['瑞典', 'SE'],
['比利时', 'BE'],
['阿联酋', 'AE'],
['法国', 'FR'],
['波兰', 'PL'],
['荷兰', 'NL'],
['奥地利', 'AT'],
['巴西', 'BR'],
['土耳其', 'TR'],
['以色列', 'IL'],
['意大利', 'IT'],
['西班牙', 'ES'],
['香港', 'HK'],
['美国', 'US'],
['日本', 'JP'],
['韩国', 'KR'],
['台湾', 'TW']
];
const regionResolver = utils.createRegionResolver(regionAliases);
const stripExistingSuffix = name => String(name)
.replace(/\s+-\s*YinYun(?:\s+\d{2})?\s*$/i, '')
.trim();
const stripLineType = name => String(name)
.replace(/\s*[(]\s*[TR]\s*[)]\s*$/i, '')
.trim();
const leadingRegionPrefixOf = name => {
const match = String(name).match(/^([A-Z]{2})(?:\s+([A-Z]{3}))?(?=\s|$|-|\d|\|)/);
if (!match) return '';
return match[2] ? `${match[1]} ${match[2]}` : match[1];
};
const regionPrefixOf = name => {
if (/魔法节点/i.test(name)) return 'MAGIC';
return regionResolver.aliasRegionPrefixOf(name) || leadingRegionPrefixOf(name);
};
const regionCount = Object.create(null);
return proxies.map(proxy => {
if (typeof proxy.name !== 'string') {
return proxy;
}
const cleanName = stripExistingSuffix(proxy.name);
const { text } = utils.splitLeadingOrInlineFlag(cleanName);
const coreName = stripLineType(text);
const regionPrefix = regionPrefixOf(coreName);
if (!regionPrefix) {
proxy.name = coreName;
return proxy;
}
regionCount[regionPrefix] = (regionCount[regionPrefix] || 0) + 1;
const sequence = String(regionCount[regionPrefix]).padStart(2, '0');
const regionCode = regionPrefix.split(/\s+/)[0];
const flag = regionPrefix === 'MAGIC' ? '' : utils.regionCodeToFlag(regionCode);
proxy.name = [
flag,
regionPrefix,
sequence
].filter(Boolean).join(' ').trim();
return proxy;
});
}