Compare commits

..

10 Commits

14 changed files with 193 additions and 115 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "sub-store",
"version": "2.14.269",
"version": "2.14.275",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
"main": "src/main.js",
"scripts": {

View File

@@ -15,6 +15,7 @@ import $ from '@/core/app';
import { FILES_KEY, MODULES_KEY } from '@/constants';
import { findByName } from '@/utils/database';
import { produceArtifact } from '@/restful/sync';
import { getFlag, getISO } from '@/utils/geo';
function preprocess(raw) {
for (const processor of PROXY_PREPROCESSORS) {
@@ -205,9 +206,9 @@ function produce(proxies, targetPlatform, type, opts = {}) {
proxy.sni = 'off';
} else if (!['tuic'].includes(proxy.type)) {
$.error(
`Target platform ${targetPlatform} does not support sni off. Proxy's fields (tls-fingerprint and skip-cert-verify) will be modified.`,
`Target platform ${targetPlatform} does not support sni off. Proxy's fields (sni, tls-fingerprint and skip-cert-verify) will be modified.`,
);
delete proxy.sni;
proxy.sni = '';
proxy['skip-cert-verify'] = true;
delete proxy['tls-fingerprint'];
}
@@ -258,6 +259,8 @@ export const ProxyUtils = {
isIPv6,
isIP,
yaml: YAML,
getFlag,
getISO,
};
function tryParse(parser, line) {
@@ -412,6 +415,9 @@ function lastParse(proxy) {
proxy.name = `${proxy.type} ${proxy.server}:${proxy.port}`;
}
}
if (['', 'off'].includes(proxy.sni)) {
proxy['disable-sni'] = true;
}
return proxy;
}

View File

@@ -617,6 +617,8 @@ function RegionFilter(regions) {
SG: '🇸🇬',
JP: '🇯🇵',
UK: '🇬🇧',
DE: '🇩🇪',
KR: '🇰🇷',
};
return {
name: 'Region Filter',

View File

@@ -150,6 +150,13 @@ export default function Clash_Producer() {
delete proxy.subName;
delete proxy.collectionName;
delete proxy.id;
delete proxy.resolved;
for (const key in proxy) {
if (proxy[key] == null) {
delete proxy[key];
}
}
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]

View File

@@ -165,6 +165,13 @@ export default function ClashMeta_Producer() {
}
delete proxy.subName;
delete proxy.collectionName;
delete proxy.id;
delete proxy.resolved;
for (const key in proxy) {
if (proxy[key] == null) {
delete proxy[key];
}
}
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]

View File

@@ -13,7 +13,8 @@ import singbox_Producer from './sing-box';
function JSON_Producer() {
const type = 'ALL';
const produce = (proxies) => JSON.stringify(proxies, null, 2);
const produce = (proxies, type) =>
type === 'internal' ? proxies : JSON.stringify(proxies, null, 2);
return { type, produce };
}

View File

@@ -168,6 +168,13 @@ export default function ShadowRocket_Producer() {
}
delete proxy.subName;
delete proxy.collectionName;
delete proxy.id;
delete proxy.resolved;
for (const key in proxy) {
if (proxy[key] == null) {
delete proxy[key];
}
}
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]

View File

@@ -789,7 +789,9 @@ export default function singbox_Producer() {
$.error(e.message ?? e);
}
});
return type === 'internal' ? list : JSON.stringify(list, null, 2);
return type === 'internal'
? list
: JSON.stringify({ outbounds: list }, null, 2);
};
return { type, produce };
}

View File

@@ -257,6 +257,13 @@ export default function Stash_Producer() {
delete proxy.subName;
delete proxy.collectionName;
delete proxy.id;
delete proxy.resolved;
for (const key in proxy) {
if (proxy[key] == null) {
delete proxy[key];
}
}
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]

View File

@@ -8,6 +8,13 @@ export default function URI_Producer() {
let result = '';
delete proxy.subName;
delete proxy.collectionName;
delete proxy.id;
delete proxy.resolved;
for (const key in proxy) {
if (proxy[key] == null) {
delete proxy[key];
}
}
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
delete proxy.tls;
}

View File

@@ -1,4 +1,7 @@
import { getPlatformFromHeaders } from '@/utils/platform';
import {
getUserAgentFromHeaders,
getPlatformFromUserAgent,
} from '@/utils/user-agent';
import { COLLECTIONS_KEY, SUBS_KEY } from '@/constants';
import { findByName } from '@/utils/database';
import { getFlowHeaders } from '@/utils/flow';
@@ -16,10 +19,15 @@ async function downloadSubscription(req, res) {
let { name } = req.params;
name = decodeURIComponent(name);
const platform =
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
const userAgent = getUserAgentFromHeaders(req.headers);
const platform =
req.query.target || getPlatformFromUserAgent(userAgent) || 'JSON';
$.info(
`正在下载订阅:${name}\ntarget: ${platform}\n来源 User-Agent: ${userAgent.UA}`,
);
$.info(`正在下载订阅:${name}`);
let {
url,
ua,
@@ -172,13 +180,17 @@ async function downloadCollection(req, res) {
let { name } = req.params;
name = decodeURIComponent(name);
const userAgent = getUserAgentFromHeaders(req.headers);
const platform =
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
req.query.target || getPlatformFromUserAgent(userAgent) || 'JSON';
const allCols = $.read(COLLECTIONS_KEY);
const collection = findByName(allCols, name);
$.info(`正在下载组合订阅:${name}`);
$.info(
`正在下载组合订阅:${name}\ntarget: ${platform}\n来源 User-Agent: ${userAgent.UA}`,
);
let { ignoreFailedRemoteSub, produceType, includeUnsupportedProxy } =
req.query;

View File

@@ -1,3 +1,105 @@
const ISOFlags = {
'🏳️‍🌈': ['EXP', 'BAND'],
'🇸🇱': ['TEST', 'SOS'],
'🇦🇩': ['AD', 'AND'],
'🇦🇪': ['AE', 'ARE'],
'🇦🇫': ['AF', 'AFG'],
'🇦🇱': ['AL', 'ALB'],
'🇦🇲': ['AM', 'ARM'],
'🇦🇷': ['AR', 'ARG'],
'🇦🇹': ['AT', 'AUT'],
'🇦🇺': ['AU', 'AUS'],
'🇦🇿': ['AZ', 'AZE'],
'🇧🇦': ['BA', 'BIH'],
'🇧🇩': ['BD', 'BGD'],
'🇧🇪': ['BE', 'BEL'],
'🇧🇬': ['BG', 'BGR'],
'🇧🇭': ['BH', 'BHR'],
'🇧🇷': ['BR', 'BRA'],
'🇧🇾': ['BY', 'BLR'],
'🇨🇦': ['CA', 'CAN'],
'🇨🇭': ['CH', 'CHE'],
'🇨🇱': ['CL', 'CHL'],
'🇨🇴': ['CO', 'COL'],
'🇨🇷': ['CR', 'CRI'],
'🇨🇾': ['CY', 'CYP'],
'🇨🇿': ['CZ', 'CZE'],
'🇩🇪': ['DE', 'DEU'],
'🇩🇰': ['DK', 'DNK'],
'🇪🇨': ['EC', 'ECU'],
'🇪🇪': ['EE', 'EST'],
'🇪🇬': ['EG', 'EGY'],
'🇪🇸': ['ES', 'ESP'],
'🇪🇺': ['EU'],
'🇫🇮': ['FI', 'FIN'],
'🇫🇷': ['FR', 'FRA'],
'🇬🇧': ['GB', 'GBR', 'UK'],
'🇬🇪': ['GE', 'GEO'],
'🇬🇷': ['GR', 'GRC'],
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
'🇭🇷': ['HR', 'HRV'],
'🇭🇺': ['HU', 'HUN'],
'🇯🇴': ['JO', 'JOR'],
'🇯🇵': ['JP', 'JPN'],
'🇰🇪': ['KE', 'KEN'],
'🇰🇬': ['KG', 'KGZ'],
'🇰🇭': ['KH', 'KGZ'],
'🇰🇵': ['KP', 'PRK'],
'🇰🇷': ['KR', 'KOR', 'SEL'],
'🇰🇿': ['KZ', 'KAZ'],
'🇮🇩': ['ID', 'IDN'],
'🇮🇪': ['IE', 'IRL'],
'🇮🇱': ['IL', 'ISR'],
'🇮🇲': ['IM', 'IMN'],
'🇮🇳': ['IN', 'IND'],
'🇮🇷': ['IR', 'IRN'],
'🇮🇸': ['IS', 'ISL'],
'🇮🇹': ['IT', 'ITA'],
'🇱🇹': ['LT', 'LTU'],
'🇱🇺': ['LU', 'LUX'],
'🇱🇻': ['LV', 'LVA'],
'🇲🇦': ['MA', 'MAR'],
'🇲🇩': ['MD', 'MDA'],
'🇳🇬': ['NG', 'NGA'],
'🇲🇰': ['MK', 'MKD'],
'🇲🇳': ['MN', 'MNG'],
'🇲🇴': ['MO', 'MAC', 'CTM'],
'🇲🇹': ['MT', 'MLT'],
'🇲🇽': ['MX', 'MEX'],
'🇲🇾': ['MY', 'MYS'],
'🇳🇱': ['NL', 'NLD', 'AMS'],
'🇳🇴': ['NO', 'NOR'],
'🇳🇵': ['NP', 'NPL'],
'🇳🇿': ['NZ', 'NZL'],
'🇵🇦': ['PA', 'PAN'],
'🇵🇪': ['PE', 'PER'],
'🇵🇭': ['PH', 'PHL'],
'🇵🇰': ['PK', 'PAK'],
'🇵🇱': ['PL', 'POL'],
'🇵🇷': ['PR', 'PRI'],
'🇵🇹': ['PT', 'PRT'],
'🇵🇾': ['PY', 'PRY'],
'🇷🇴': ['RO', 'ROU'],
'🇷🇸': ['RS', 'SRB'],
'🇷🇪': ['RE', 'REU'],
'🇷🇺': ['RU', 'RUS'],
'🇸🇦': ['SA', 'SAU'],
'🇸🇪': ['SE', 'SWE'],
'🇸🇬': ['SG', 'SGP'],
'🇸🇮': ['SI', 'SVN'],
'🇸🇰': ['SK', 'SVK'],
'🇹🇭': ['TH', 'THA'],
'🇹🇳': ['TN', 'TUN'],
'🇹🇷': ['TR', 'TUR'],
'🇹🇼': ['TW', 'TWN', 'CHT', 'HINET', 'ROC'],
'🇺🇦': ['UA', 'UKR'],
'🇺🇸': ['US', 'USA', 'LAX', 'SFO', 'SJC'],
'🇺🇾': ['UY', 'URY'],
'🇻🇪': ['VE', 'VEN'],
'🇻🇳': ['VN', 'VNM'],
'🇿🇦': ['ZA', 'ZAF', 'JNB'],
'🇨🇳': ['CN', 'CHN', 'BACK'],
};
// get proxy flag according to its name
export function getFlag(name) {
// flags from @KOP-XIAO: https://github.com/KOP-XIAO/QuantumultX/blob/master/Scripts/resource-parser.js
@@ -65,6 +167,7 @@ export function getFlag(name) {
'广德',
'法兰克福',
'Frankfurt',
'德意志',
],
'🇩🇰': ['Denmark', '丹麦', '丹麥'],
'🇪🇨': ['Ecuador', '厄瓜多尔'],
@@ -283,108 +386,6 @@ export function getFlag(name) {
],
};
const ISOFlags = {
'🏳️‍🌈': ['EXP', 'BAND'],
'🇸🇱': ['TEST', 'SOS'],
'🇦🇩': ['AD', 'AND'],
'🇦🇪': ['AE', 'ARE'],
'🇦🇫': ['AF', 'AFG'],
'🇦🇱': ['AL', 'ALB'],
'🇦🇲': ['AM', 'ARM'],
'🇦🇷': ['AR', 'ARG'],
'🇦🇹': ['AT', 'AUT'],
'🇦🇺': ['AU', 'AUS'],
'🇦🇿': ['AZ', 'AZE'],
'🇧🇦': ['BA', 'BIH'],
'🇧🇩': ['BD', 'BGD'],
'🇧🇪': ['BE', 'BEL'],
'🇧🇬': ['BG', 'BGR'],
'🇧🇭': ['BH', 'BHR'],
'🇧🇷': ['BR', 'BRA'],
'🇧🇾': ['BY', 'BLR'],
'🇨🇦': ['CA', 'CAN'],
'🇨🇭': ['CH', 'CHE'],
'🇨🇱': ['CL', 'CHL'],
'🇨🇴': ['CO', 'COL'],
'🇨🇷': ['CR', 'CRI'],
'🇨🇾': ['CY', 'CYP'],
'🇨🇿': ['CZ', 'CZE'],
'🇩🇪': ['DE', 'DEU'],
'🇩🇰': ['DK', 'DNK'],
'🇪🇨': ['EC', 'ECU'],
'🇪🇪': ['EE', 'EST'],
'🇪🇬': ['EG', 'EGY'],
'🇪🇸': ['ES', 'ESP'],
'🇪🇺': ['EU'],
'🇫🇮': ['FI', 'FIN'],
'🇫🇷': ['FR', 'FRA'],
'🇬🇧': ['GB', 'GBR', 'UK'],
'🇬🇪': ['GE', 'GEO'],
'🇬🇷': ['GR', 'GRC'],
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
'🇭🇷': ['HR', 'HRV'],
'🇭🇺': ['HU', 'HUN'],
'🇯🇴': ['JO', 'JOR'],
'🇯🇵': ['JP', 'JPN'],
'🇰🇪': ['KE', 'KEN'],
'🇰🇬': ['KG', 'KGZ'],
'🇰🇭': ['KH', 'KGZ'],
'🇰🇵': ['KP', 'PRK'],
'🇰🇷': ['KR', 'KOR'],
'🇰🇿': ['KZ', 'KAZ'],
'🇮🇩': ['ID', 'IDN'],
'🇮🇪': ['IE', 'IRL'],
'🇮🇱': ['IL', 'ISR'],
'🇮🇲': ['IM', 'IMN'],
'🇮🇳': ['IN', 'IND'],
'🇮🇷': ['IR', 'IRN'],
'🇮🇸': ['IS', 'ISL'],
'🇮🇹': ['IT', 'ITA'],
'🇱🇹': ['LT', 'LTU'],
'🇱🇺': ['LU', 'LUX'],
'🇱🇻': ['LV', 'LVA'],
'🇲🇦': ['MA', 'MAR'],
'🇲🇩': ['MD', 'MDA'],
'🇳🇬': ['NG', 'NGA'],
'🇲🇰': ['MK', 'MKD'],
'🇲🇳': ['MN', 'MNG'],
'🇲🇴': ['MO', 'MAC', 'CTM'],
'🇲🇹': ['MT', 'MLT'],
'🇲🇽': ['MX', 'MEX'],
'🇲🇾': ['MY', 'MYS'],
'🇳🇱': ['NL', 'NLD'],
'🇳🇴': ['NO', 'NOR'],
'🇳🇵': ['NP', 'NPL'],
'🇳🇿': ['NZ', 'NZL'],
'🇵🇦': ['PA', 'PAN'],
'🇵🇪': ['PE', 'PER'],
'🇵🇭': ['PH', 'PHL'],
'🇵🇰': ['PK', 'PAK'],
'🇵🇱': ['PL', 'POL'],
'🇵🇷': ['PR', 'PRI'],
'🇵🇹': ['PT', 'PRT'],
'🇵🇾': ['PY', 'PRY'],
'🇷🇴': ['RO', 'ROU'],
'🇷🇸': ['RS', 'SRB'],
'🇷🇪': ['RE', 'REU'],
'🇷🇺': ['RU', 'RUS'],
'🇸🇦': ['SA', 'SAU'],
'🇸🇪': ['SE', 'SWE'],
'🇸🇬': ['SG', 'SGP'],
'🇸🇮': ['SI', 'SVN'],
'🇸🇰': ['SK', 'SVK'],
'🇹🇭': ['TH', 'THA'],
'🇹🇳': ['TN', 'TUN'],
'🇹🇷': ['TR', 'TUR'],
'🇹🇼': ['TW', 'TWN', 'CHT', 'HINET', 'ROC'],
'🇺🇦': ['UA', 'UKR'],
'🇺🇸': ['US', 'USA', 'LAX', 'SFO'],
'🇺🇾': ['UY', 'URY'],
'🇻🇪': ['VE', 'VEN'],
'🇻🇳': ['VN', 'VNM'],
'🇿🇦': ['ZA', 'ZAF'],
'🇨🇳': ['CN', 'CHN', 'BACK'],
};
// 原旗帜或空
let Flag =
name.match(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/)?.[0] ||
@@ -398,7 +399,9 @@ export function getFlag(name) {
// 不精确匹配(只要包含就算,忽略大小写)
keywords.some((keyword) => RegExp(`${keyword}`, 'i').test(name))
) {
//console.log(`newFlag = ${flag}`)
if (/内蒙古/.test(name) && ['🇲🇳'].includes(flag)) {
return (Flag = '🇨🇳');
}
return (Flag = flag);
}
}
@@ -416,6 +419,11 @@ export function getFlag(name) {
return (Flag = flag);
}
}
//console.log(`Final Flag = ${Flag}`)
return Flag;
}
export function getISO(name) {
return ISOFlags[getFlag(name)]?.[0];
}

View File

@@ -1,4 +1,4 @@
export function getPlatformFromHeaders(headers) {
export function getUserAgentFromHeaders(headers) {
const keys = Object.keys(headers);
let UA = '';
let ua = '';
@@ -9,6 +9,9 @@ export function getPlatformFromHeaders(headers) {
break;
}
}
return { UA, ua };
}
export function getPlatformFromUserAgent({ ua, UA }) {
if (UA.indexOf('Quantumult%20X') !== -1) {
return 'QX';
} else if (UA.indexOf('Surfboard') !== -1) {
@@ -38,3 +41,7 @@ export function getPlatformFromHeaders(headers) {
return 'JSON';
}
}
export function getPlatformFromHeaders(headers) {
const { UA, ua } = getUserAgentFromHeaders(headers);
return getPlatformFromUserAgent({ ua, UA });
}

View File

@@ -36,8 +36,13 @@ function operator(proxies = [], targetPlatform, context) {
// isIPv6,
// isIP,
// yaml, // yaml 解析和生成
// getFlag, // 获取 emoji 旗帜
// getISO, // 获取 ISO 3166-1 alpha-2 代码
// }
// 示例: 给节点名添加前缀
// $server.name = `[${ProxyUtils.getISO($server.name)}] ${$server.name}`
// 示例: 从 sni 文件中读取内容并进行节点操作
// const sni = await produceArtifact({
// type: 'file',