mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89dbe9ad3d |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.281",
|
||||
"version": "2.14.269",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -15,7 +15,6 @@ 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) {
|
||||
@@ -206,9 +205,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 (sni, tls-fingerprint and skip-cert-verify) will be modified.`,
|
||||
`Target platform ${targetPlatform} does not support sni off. Proxy's fields (tls-fingerprint and skip-cert-verify) will be modified.`,
|
||||
);
|
||||
proxy.sni = '';
|
||||
delete proxy.sni;
|
||||
proxy['skip-cert-verify'] = true;
|
||||
delete proxy['tls-fingerprint'];
|
||||
}
|
||||
@@ -259,8 +258,6 @@ export const ProxyUtils = {
|
||||
isIPv6,
|
||||
isIP,
|
||||
yaml: YAML,
|
||||
getFlag,
|
||||
getISO,
|
||||
};
|
||||
|
||||
function tryParse(parser, line) {
|
||||
@@ -415,9 +412,6 @@ function lastParse(proxy) {
|
||||
proxy.name = `${proxy.type} ${proxy.server}:${proxy.port}`;
|
||||
}
|
||||
}
|
||||
if (['', 'off'].includes(proxy.sni)) {
|
||||
proxy['disable-sni'] = true;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,9 +63,9 @@ function URI_SS() {
|
||||
/\d+/,
|
||||
)?.[0];
|
||||
|
||||
const userInfo = userInfoStr.match(/(^.*?):(.*$)/);
|
||||
proxy.cipher = userInfo[1];
|
||||
proxy.password = userInfo[2];
|
||||
const userInfo = userInfoStr.split(':');
|
||||
proxy.cipher = userInfo[0];
|
||||
proxy.password = userInfo[1];
|
||||
|
||||
// handle obfs
|
||||
const idx = content.indexOf('?plugin=');
|
||||
|
||||
@@ -490,7 +490,7 @@ const DOMAIN_RESOLVERS = {
|
||||
},
|
||||
});
|
||||
const answers = resp.body.split(';').map((i) => i.split(',')[0]);
|
||||
if (answers.length === 0 || String(answers) === '0') {
|
||||
if (answers.length === 0) {
|
||||
throw new Error('No answers');
|
||||
}
|
||||
const result = answers[answers.length - 1];
|
||||
@@ -550,25 +550,13 @@ function ResolveDomainOperator({ provider, type: _type, filter, cache }) {
|
||||
results[p.server],
|
||||
);
|
||||
if (server && port) {
|
||||
p._domain = p.server;
|
||||
p.server = server;
|
||||
p.port = port;
|
||||
p.resolved = true;
|
||||
p._IPv4 = p.server;
|
||||
if (!isIP(p._IP)) {
|
||||
p._IP = p.server;
|
||||
}
|
||||
} else {
|
||||
p.resolved = false;
|
||||
}
|
||||
} else {
|
||||
p._domain = p.server;
|
||||
p.server = results[p.server];
|
||||
p.resolved = true;
|
||||
p[`_${type}`] = p.server;
|
||||
if (!isIP(p._IP)) {
|
||||
p._IP = p.server;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p.resolved = false;
|
||||
@@ -629,8 +617,6 @@ function RegionFilter(regions) {
|
||||
SG: '🇸🇬',
|
||||
JP: '🇯🇵',
|
||||
UK: '🇬🇧',
|
||||
DE: '🇩🇪',
|
||||
KR: '🇰🇷',
|
||||
};
|
||||
return {
|
||||
name: 'Region Filter',
|
||||
|
||||
@@ -150,13 +150,6 @@ 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 || /^_/i.test(key)) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -165,13 +165,6 @@ 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 || /^_/i.test(key)) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -13,8 +13,7 @@ import singbox_Producer from './sing-box';
|
||||
|
||||
function JSON_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies, type) =>
|
||||
type === 'internal' ? proxies : JSON.stringify(proxies, null, 2);
|
||||
const produce = (proxies) => JSON.stringify(proxies, null, 2);
|
||||
return { type, produce };
|
||||
}
|
||||
|
||||
|
||||
@@ -168,13 +168,6 @@ 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 || /^_/i.test(key)) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -622,7 +622,7 @@ const wireguardParser = (proxy = {}) => {
|
||||
throw 'invalid port';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
if (typeof proxy.reserved === 'string') {
|
||||
parsedProxy.reserved = proxy.reserved;
|
||||
parsedProxy.reserved.push(proxy.reserved);
|
||||
} else if (Array.isArray(proxy.reserved)) {
|
||||
for (const r of proxy.reserved) parsedProxy.reserved.push(r);
|
||||
} else {
|
||||
@@ -789,9 +789,7 @@ export default function singbox_Producer() {
|
||||
$.error(e.message ?? e);
|
||||
}
|
||||
});
|
||||
return type === 'internal'
|
||||
? list
|
||||
: JSON.stringify({ outbounds: list }, null, 2);
|
||||
return type === 'internal' ? list : JSON.stringify(list, null, 2);
|
||||
};
|
||||
return { type, produce };
|
||||
}
|
||||
|
||||
@@ -257,13 +257,6 @@ 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 || /^_/i.test(key)) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -8,13 +8,6 @@ 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 || /^_/i.test(key)) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPlatformFromHeaders } from '@/utils/user-agent';
|
||||
import { getPlatformFromHeaders } from '@/utils/platform';
|
||||
import { COLLECTIONS_KEY, SUBS_KEY } from '@/constants';
|
||||
import { findByName } from '@/utils/database';
|
||||
import { getFlowHeaders } from '@/utils/flow';
|
||||
@@ -6,29 +6,10 @@ import $ from '@/core/app';
|
||||
import { failed } from '@/restful/response';
|
||||
import { InternalServerError, ResourceNotFoundError } from '@/restful/errors';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { isIPv4, isIPv6 } from '@/utils';
|
||||
import { getISO } from '@/utils/geo';
|
||||
import env from '@/utils/env';
|
||||
|
||||
export default function register($app) {
|
||||
$app.get('/download/collection/:name', downloadCollection);
|
||||
$app.get('/download/:name', downloadSubscription);
|
||||
$app.get(
|
||||
'/download/collection/:name/api/v1/server/details',
|
||||
async (req, res) => {
|
||||
req.query.platform = 'JSON';
|
||||
req.query.produceType = 'internal';
|
||||
req.query.resultFormat = 'nezha';
|
||||
await downloadCollection(req, res);
|
||||
},
|
||||
);
|
||||
$app.get('/download/:name/api/v1/server/details', async (req, res) => {
|
||||
req.query.platform = 'JSON';
|
||||
req.query.produceType = 'internal';
|
||||
req.query.resultFormat = 'nezha';
|
||||
await downloadSubscription(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
async function downloadSubscription(req, res) {
|
||||
@@ -47,7 +28,6 @@ async function downloadSubscription(req, res) {
|
||||
ignoreFailedRemoteSub,
|
||||
produceType,
|
||||
includeUnsupportedProxy,
|
||||
resultFormat,
|
||||
} = req.query;
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
@@ -82,7 +62,7 @@ async function downloadSubscription(req, res) {
|
||||
const sub = findByName(allSubs, name);
|
||||
if (sub) {
|
||||
try {
|
||||
let output = await produceArtifact({
|
||||
const output = await produceArtifact({
|
||||
type: 'subscription',
|
||||
name,
|
||||
platform,
|
||||
@@ -153,9 +133,6 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
|
||||
if (platform === 'JSON') {
|
||||
if (resultFormat === 'nezha') {
|
||||
output = nezhaTransform(output);
|
||||
}
|
||||
res.set('Content-Type', 'application/json;charset=utf-8').send(
|
||||
output,
|
||||
);
|
||||
@@ -203,12 +180,8 @@ async function downloadCollection(req, res) {
|
||||
|
||||
$.info(`正在下载组合订阅:${name}`);
|
||||
|
||||
let {
|
||||
ignoreFailedRemoteSub,
|
||||
produceType,
|
||||
includeUnsupportedProxy,
|
||||
resultFormat,
|
||||
} = req.query;
|
||||
let { ignoreFailedRemoteSub, produceType, includeUnsupportedProxy } =
|
||||
req.query;
|
||||
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
|
||||
@@ -226,7 +199,7 @@ async function downloadCollection(req, res) {
|
||||
|
||||
if (collection) {
|
||||
try {
|
||||
let output = await produceArtifact({
|
||||
const output = await produceArtifact({
|
||||
type: 'collection',
|
||||
name,
|
||||
platform,
|
||||
@@ -298,9 +271,6 @@ async function downloadCollection(req, res) {
|
||||
}
|
||||
|
||||
if (platform === 'JSON') {
|
||||
if (resultFormat === 'nezha') {
|
||||
output = nezhaTransform(output);
|
||||
}
|
||||
res.set('Content-Type', 'application/json;charset=utf-8').send(
|
||||
output,
|
||||
);
|
||||
@@ -337,66 +307,3 @@ async function downloadCollection(req, res) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function nezhaTransform(output) {
|
||||
const result = {
|
||||
code: 0,
|
||||
message: 'success',
|
||||
result: [],
|
||||
};
|
||||
output.map((proxy, index) => {
|
||||
// 如果节点上有数据 就取节点上的数据
|
||||
let CountryCode = proxy._geo?.countryCode || proxy._geo?.country;
|
||||
// 简单判断下
|
||||
if (!/^[a-z]{2}$/i.test(CountryCode)) {
|
||||
CountryCode = getISO(proxy.name);
|
||||
}
|
||||
// 简单判断下
|
||||
if (/^[a-z]{2}$/i.test(CountryCode)) {
|
||||
// 如果节点上有数据 就取节点上的数据
|
||||
let time = proxy._unavailable ? 0 : Date.now();
|
||||
result.result.push({
|
||||
id: index,
|
||||
name: proxy.name,
|
||||
tag: `${proxy._tag ?? ''}`,
|
||||
last_active: time,
|
||||
// 暂时不用处理 现在 VPings App 端的接口支持域名查询
|
||||
// 其他场景使用 自己在 Sub-Store 加一步域名解析
|
||||
valid_ip: proxy._IP || proxy.server,
|
||||
ipv4: proxy._IPv4 || proxy.server,
|
||||
ipv6: proxy._IPv6 || (isIPv6(proxy.server) ? proxy.server : ''),
|
||||
host: {
|
||||
Platform: 'Sub-Store',
|
||||
PlatformVersion: env.version,
|
||||
CPU: [],
|
||||
MemTotal: 1024,
|
||||
DiskTotal: 1024,
|
||||
SwapTotal: 1024,
|
||||
Arch: '',
|
||||
Virtualization: '',
|
||||
BootTime: time,
|
||||
CountryCode, // 目前需要
|
||||
Version: '',
|
||||
},
|
||||
status: {
|
||||
CPU: 0,
|
||||
MemUsed: 0,
|
||||
SwapUsed: 0,
|
||||
DiskUsed: 0,
|
||||
NetInTransfer: 0,
|
||||
NetOutTransfer: 0,
|
||||
NetInSpeed: 0,
|
||||
NetOutSpeed: 0,
|
||||
Uptime: parseInt(proxy._uptime ?? index, 10),
|
||||
Load1: 0,
|
||||
Load5: 0,
|
||||
Load15: 0,
|
||||
TcpConnCount: 0,
|
||||
UdpConnCount: 0,
|
||||
ProcessCount: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
return JSON.stringify(result, null, 2);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const customCacheKey = $arguments?.cacheKey
|
||||
? `#sub-store-cached-custom-${$arguments?.cacheKey}`
|
||||
: undefined;
|
||||
|
||||
// const downloadUrlMatch = url.match(/^\/api\/(file|module)\/(.+)/);
|
||||
// if (downloadUrlMatch) {
|
||||
@@ -119,24 +116,10 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
}
|
||||
if (shouldCache) {
|
||||
resourceCache.set(id, body);
|
||||
if (customCacheKey) {
|
||||
$.write(body, customCacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
result = body;
|
||||
} catch (e) {
|
||||
if (customCacheKey) {
|
||||
const cached = $.read(customCacheKey);
|
||||
if (cached) {
|
||||
$.info(
|
||||
`无法下载 URL ${url}: ${
|
||||
e.message ?? e
|
||||
}\n使用自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
throw new Error(`无法下载 URL ${url}: ${e.message ?? e}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +1,3 @@
|
||||
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
|
||||
@@ -167,7 +65,6 @@ export function getFlag(name) {
|
||||
'广德',
|
||||
'法兰克福',
|
||||
'Frankfurt',
|
||||
'德意志',
|
||||
],
|
||||
'🇩🇰': ['Denmark', '丹麦', '丹麥'],
|
||||
'🇪🇨': ['Ecuador', '厄瓜多尔'],
|
||||
@@ -386,6 +283,108 @@ 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] ||
|
||||
@@ -399,9 +398,7 @@ export function getFlag(name) {
|
||||
// 不精确匹配(只要包含就算,忽略大小写)
|
||||
keywords.some((keyword) => RegExp(`${keyword}`, 'i').test(name))
|
||||
) {
|
||||
if (/内蒙古/.test(name) && ['🇲🇳'].includes(flag)) {
|
||||
return (Flag = '🇨🇳');
|
||||
}
|
||||
//console.log(`newFlag = ${flag}`)
|
||||
return (Flag = flag);
|
||||
}
|
||||
}
|
||||
@@ -419,11 +416,6 @@ export function getFlag(name) {
|
||||
return (Flag = flag);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(`Final Flag = ${Flag}`)
|
||||
return Flag;
|
||||
}
|
||||
|
||||
export function getISO(name) {
|
||||
return ISOFlags[getFlag(name)]?.[0];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export function getUserAgentFromHeaders(headers) {
|
||||
export function getPlatformFromHeaders(headers) {
|
||||
const keys = Object.keys(headers);
|
||||
let UA = '';
|
||||
let ua = '';
|
||||
@@ -9,9 +9,6 @@ export function getUserAgentFromHeaders(headers) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { UA, ua };
|
||||
}
|
||||
export function getPlatformFromUserAgent({ ua, UA }) {
|
||||
if (UA.indexOf('Quantumult%20X') !== -1) {
|
||||
return 'QX';
|
||||
} else if (UA.indexOf('Surfboard') !== -1) {
|
||||
@@ -41,7 +38,3 @@ export function getPlatformFromUserAgent({ ua, UA }) {
|
||||
return 'JSON';
|
||||
}
|
||||
}
|
||||
export function getPlatformFromHeaders(headers) {
|
||||
const { UA, ua } = getUserAgentFromHeaders(headers);
|
||||
return getPlatformFromUserAgent({ ua, UA });
|
||||
}
|
||||
@@ -13,13 +13,15 @@ Telegram 频道: [`https://t.me/cool_scripts` ](https://t.me/cool_scripts)
|
||||
|
||||
### 2. Surge
|
||||
|
||||
0. 最新 Surge iOS TestFlight 版本 可使用 Beta 版(支持最新 Surge iOS TestFlight 版本的特性): [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Beta.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Beta.sgmodule)
|
||||
0. 最新 Surge iOS TestFlight 版本 可使用 Beta 版(支持最新 Surge iOS TestFlight 版本的分类和参数设置): [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Beta.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Beta.sgmodule)
|
||||
|
||||
1. 官方默认版模块(支持 App 内使用编辑参数): [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule)
|
||||
1. 官方默认版模块(目前不带 ability 参数, 不保证以后不会改动): [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule)
|
||||
|
||||
2. 经典版, 不支持编辑参数, 固定带 ability 参数版本, 使用 jsc 引擎时, 可能会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 请使用此带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule)
|
||||
2. 固定带 ability 参数版本,可能会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 请使用此带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule)
|
||||
|
||||
3. 经典版, 不支持编辑参数, 固定不带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule)
|
||||
> 最新 Surge iOS TestFlight 版本应该没有内存问题了 可以大胆尝试带 ability 参数版本
|
||||
|
||||
3. 固定不带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule)
|
||||
|
||||
|
||||
### 3. QX
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#!name=Sub-Store(β)
|
||||
#!desc=支持 Surge 正式版的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *
|
||||
#!desc=支持最新 Surge iOS TestFlight 版本的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *
|
||||
#!category=订阅管理
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync"
|
||||
#!arguments-desc="\n1️⃣ ability\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n⚠️ Surge 上时候可能会爆内存\n不需要使用的时候应该关闭\n填写任意其他值关闭\n\n2️⃣ cronexp\n同步配置定时任务\n默认为每天 23 点 55 分\n\n3️⃣ sync\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务"
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout={{{timeout}}},ability="{{{ability}}}",engine={{{engine}}}
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120,ability="{{{ability}}}"
|
||||
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout={{{timeout}}},engine={{{engine}}}
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 使用 jsc 引擎时, 可能会爆内存, 如果不需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用不带 ability 参数版本. 定时任务默认为每天 23 点 55 分
|
||||
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 可能会爆内存, 如果不需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用不带 ability 参数版本. 定时任务默认为每天 23 点 55 分
|
||||
#!category=订阅管理
|
||||
|
||||
[MITM]
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=支持 Surge 正式版的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用带 ability 参数. 定时任务默认为每天 23 点 55 分
|
||||
#!category=订阅管理
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout={{{timeout}}},ability="{{{ability}}}",engine={{{engine}}}
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout={{{timeout}}},engine={{{engine}}}
|
||||
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
Sub-Store Sync=type=cron,cronexp=55 23 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -9,8 +9,7 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 可在预览界面点击节点查看 JSON 结构 或查看 `target=JSON` 的通用订阅
|
||||
// 1. `no-resolve` 为不解析域名
|
||||
// 2. 域名解析后 会多一个 `resolved` 字段
|
||||
// 3. 域名解析后会有`_IPv4`, `_IPv6`, `_IP`(若有多个步骤, 只取第一次成功的 v4 或 v6 数据), `_domain` 字段
|
||||
// 4. 节点字段 `exec` 为 `ssr-local` 路径, 默认 `/usr/local/bin/ssr-local`; 端口从 10000 开始递增(暂不支持配置)
|
||||
// 3. 节点字段 `exec` 为 `ssr-local` 路径, 默认 `/usr/local/bin/ssr-local`; 端口从 10000 开始递增(暂不支持配置)
|
||||
|
||||
// $arguments 为传入的脚本参数
|
||||
|
||||
@@ -37,13 +36,8 @@ 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',
|
||||
|
||||
Reference in New Issue
Block a user