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 | |
|---|---|---|---|
|
|
c09a945368 |
@@ -10,10 +10,10 @@
|
||||
Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.
|
||||
</p>
|
||||
|
||||
[](https://github.com/sub-store-org/Sub-Store/actions/workflows/main.yml)     
|
||||
[](https://github.com/sub-store-org/Sub-Store/actions/workflows/main.yml)     
|
||||
<a href="https://trendshift.io/repositories/4572" target="_blank"><img src="https://trendshift.io/api/badge/repositories/4572" alt="sub-store-org%2FSub-Store | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
[](https://www.buymeacoffee.com/PengYM)
|
||||
|
||||
|
||||
Core functionalities:
|
||||
|
||||
1. Conversion among various formats.
|
||||
@@ -21,7 +21,7 @@ Core functionalities:
|
||||
3. Collect multiple subscriptions in one URL.
|
||||
|
||||
> The following descriptions of features may not be updated in real-time. Please refer to the actual available features for accurate information.
|
||||
|
||||
|
||||
## 1. Subscription Conversion
|
||||
|
||||
### Supported Input Formats
|
||||
@@ -98,7 +98,7 @@ or
|
||||
esbuild(experimental)
|
||||
|
||||
```
|
||||
SUB_STORE_BACKEND_API_PORT=3000 pnpm run --parallel "/^dev:.*/"
|
||||
pnpm run --parallel "/^dev:.*/"
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
@@ -111,6 +111,7 @@ This project is under the GPL V3 LICENSE.
|
||||
|
||||
[](https://star-history.com/#sub-store-org/sub-store&Date)
|
||||
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- Special thanks to @KOP-XIAO for his awesome resource-parser. Please give a [star](https://github.com/KOP-XIAO/QuantumultX) for his great work!
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.372",
|
||||
"version": "2.14.355",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
isValidPortNumber,
|
||||
isNotBlank,
|
||||
ipAddress,
|
||||
getRandomPort,
|
||||
} from '@/utils';
|
||||
import PROXY_PROCESSORS, { ApplyProcessor } from './processors';
|
||||
import PROXY_PREPROCESSORS from './preprocessors';
|
||||
@@ -21,6 +20,23 @@ import { produceArtifact } from '@/restful/sync';
|
||||
import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
|
||||
import Gist from '@/utils/gist';
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function getRandomPort(portString) {
|
||||
let portParts = portString.split(',');
|
||||
let randomPart = portParts[Math.floor(Math.random() * portParts.length)];
|
||||
if (randomPart.includes('-')) {
|
||||
let [min, max] = randomPart.split('-').map(Number);
|
||||
return getRandomInt(min, max);
|
||||
} else {
|
||||
return Number(randomPart);
|
||||
}
|
||||
}
|
||||
|
||||
function preprocess(raw) {
|
||||
for (const processor of PROXY_PREPROCESSORS) {
|
||||
try {
|
||||
@@ -224,11 +240,12 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
|
||||
// 处理 端口跳跃
|
||||
if (proxy.ports) {
|
||||
if (!['ClashMeta'].includes(targetPlatform)) {
|
||||
if (!['ClashMeta', 'JSON'].includes(targetPlatform)) {
|
||||
proxy.ports = proxy.ports.replace(/\//g, ',');
|
||||
}
|
||||
if (!proxy.port) {
|
||||
if (!['ClashMeta', 'Stash', 'JSON'].includes(targetPlatform)) {
|
||||
proxy.port = getRandomPort(proxy.ports);
|
||||
delete proxy.ports;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +303,6 @@ export const ProxyUtils = {
|
||||
process: processFn,
|
||||
produce,
|
||||
ipAddress,
|
||||
getRandomPort,
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
isIP,
|
||||
@@ -424,12 +440,8 @@ function lastParse(proxy) {
|
||||
proxy[`${proxy.network}-opts`].path = [transportPath];
|
||||
}
|
||||
}
|
||||
if (['hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
if (proxy.ports) {
|
||||
proxy.ports = proxy.ports.replace(/\//g, ',');
|
||||
} else {
|
||||
delete proxy.ports;
|
||||
}
|
||||
if (['hysteria', 'hysteria2'].includes(proxy.type) && !proxy.ports) {
|
||||
delete proxy.ports;
|
||||
}
|
||||
if (
|
||||
['hysteria2'].includes(proxy.type) &&
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
isPresent,
|
||||
isNotBlank,
|
||||
getIfPresent,
|
||||
getRandomPort,
|
||||
} from '@/utils';
|
||||
import getSurgeParser from './peggy/surge';
|
||||
import getLoonParser from './peggy/loon';
|
||||
@@ -14,19 +13,6 @@ import getTrojanURIParser from './peggy/trojan-uri';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
function surge_port_hopping(raw) {
|
||||
const [parts, port_hopping] =
|
||||
raw.match(
|
||||
/,\s*?port-hopping\s*?=\s*?["']?\s*?((\d+(-\d+)?)([,;]\d+(-\d+)?)*)\s*?["']?\s*?/,
|
||||
) || [];
|
||||
return {
|
||||
port_hopping: port_hopping
|
||||
? port_hopping.replace(/;/g, ',')
|
||||
: undefined,
|
||||
line: parts ? raw.replace(parts, '') : raw,
|
||||
};
|
||||
}
|
||||
|
||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
||||
function URI_SS() {
|
||||
@@ -172,7 +158,7 @@ function URI_SSR() {
|
||||
for (const item of line) {
|
||||
let [key, val] = item.split('=');
|
||||
val = val.trim();
|
||||
if (val.length > 0 && val !== '(null)') {
|
||||
if (val.length > 0) {
|
||||
other_params[key] = val;
|
||||
}
|
||||
}
|
||||
@@ -310,11 +296,6 @@ function URI_VMess() {
|
||||
? !params.verify_cert
|
||||
: undefined,
|
||||
};
|
||||
if (!proxy['skip-cert-verify'] && isPresent(params.allowInsecure)) {
|
||||
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(
|
||||
params.allowInsecure,
|
||||
);
|
||||
}
|
||||
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||
if (proxy.tls && proxy.sni) {
|
||||
proxy.sni = params.sni;
|
||||
@@ -370,7 +351,6 @@ function URI_VMess() {
|
||||
transportPath = '/';
|
||||
}
|
||||
}
|
||||
// 传输层应该有配置, 暂时不考虑兼容不给配置的节点
|
||||
if (transportPath || transportHost) {
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[`${proxy.network}-opts`] = {
|
||||
@@ -559,42 +539,13 @@ function URI_Hysteria2() {
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split(/(hysteria2|hy2):\/\//)[2];
|
||||
// 端口跳跃有两种写法:
|
||||
// 1. 服务器的地址和可选端口。如果省略端口,则默认为 443。
|
||||
// 端口部分支持 端口跳跃 的「多端口地址格式」。
|
||||
// https://hysteria.network/zh/docs/advanced/Port-Hopping
|
||||
// 2. 参数 mport
|
||||
let ports;
|
||||
/* eslint-disable no-unused-vars */
|
||||
let [
|
||||
__,
|
||||
password,
|
||||
server,
|
||||
___,
|
||||
port,
|
||||
____,
|
||||
_____,
|
||||
______,
|
||||
_______,
|
||||
________,
|
||||
addons = '',
|
||||
name,
|
||||
] = /^(.*?)@(.*?)(:((\d+(-\d+)?)([,;]\d+(-\d+)?)*))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(
|
||||
line,
|
||||
);
|
||||
/* eslint-enable no-unused-vars */
|
||||
if (/^\d+$/.test(port)) {
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
}
|
||||
} else if (port) {
|
||||
ports = port;
|
||||
port = getRandomPort(ports);
|
||||
} else {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, password, server, ___, port, ____, addons = '', name] =
|
||||
/^(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line);
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
}
|
||||
|
||||
password = decodeURIComponent(password);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
@@ -606,7 +557,6 @@ function URI_Hysteria2() {
|
||||
name,
|
||||
server,
|
||||
port,
|
||||
ports,
|
||||
password,
|
||||
};
|
||||
|
||||
@@ -1339,12 +1289,7 @@ function Surge_Tuic() {
|
||||
const test = (line) => {
|
||||
return /^.*=\s*tuic(-v5)?/.test(line.split(',')[0]);
|
||||
};
|
||||
const parse = (raw) => {
|
||||
const { port_hopping, line } = surge_port_hopping(raw);
|
||||
const proxy = getSurgeParser().parse(line);
|
||||
proxy['ports'] = port_hopping;
|
||||
return proxy;
|
||||
};
|
||||
const parse = (line) => getSurgeParser().parse(line);
|
||||
return { name, test, parse };
|
||||
}
|
||||
function Surge_WireGuard() {
|
||||
@@ -1361,12 +1306,7 @@ function Surge_Hysteria2() {
|
||||
const test = (line) => {
|
||||
return /^.*=\s*hysteria2/.test(line.split(',')[0]);
|
||||
};
|
||||
const parse = (raw) => {
|
||||
const { port_hopping, line } = surge_port_hopping(raw);
|
||||
const proxy = getSurgeParser().parse(line);
|
||||
proxy['ports'] = port_hopping;
|
||||
return proxy;
|
||||
};
|
||||
const parse = (line) => getSurgeParser().parse(line);
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
|
||||
@@ -54,31 +54,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -172,8 +172,6 @@ vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseIn
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
|
||||
@@ -52,31 +52,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -170,8 +170,6 @@ vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseIn
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
|
||||
@@ -91,11 +91,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@@ -104,7 +104,7 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
handleShadowTLS();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
@@ -151,8 +151,6 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
port_hopping_interval = comma "port-hopping-interval" equals match:$[0-9]+ { proxy["hop-interval"] = parseInt(match.trim()); }
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
let start, end;
|
||||
|
||||
@@ -89,11 +89,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@@ -102,7 +102,7 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
handleShadowTLS();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
@@ -149,8 +149,6 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
port_hopping_interval = comma "port-hopping-interval" equals match:$[0-9]+ { proxy["hop-interval"] = parseInt(match.trim()); }
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
let start, end;
|
||||
|
||||
@@ -392,23 +392,18 @@ const DOMAIN_RESOLVERS = {
|
||||
const id = hex_md5(`CUSTOM:${url}:${domain}:${type}`);
|
||||
const cached = resourceCache.get(id);
|
||||
if (!noCache && cached) return cached;
|
||||
const answerType = type === 'IPv6' ? 'AAAA' : 'A';
|
||||
const res = await doh({
|
||||
url,
|
||||
domain,
|
||||
type: answerType,
|
||||
type: type === 'IPv6' ? 'AAAA' : 'A',
|
||||
timeout,
|
||||
edns,
|
||||
});
|
||||
|
||||
const { answers } = res;
|
||||
if (!Array.isArray(answers) || answers.length === 0) {
|
||||
throw new Error('No answers');
|
||||
}
|
||||
const result = answers
|
||||
.filter((i) => i?.type === answerType)
|
||||
.map((i) => i?.data)
|
||||
.filter((i) => i);
|
||||
const result = answers.map((i) => i?.data).filter((i) => i);
|
||||
if (result.length === 0) {
|
||||
throw new Error('No answers');
|
||||
}
|
||||
@@ -987,7 +982,6 @@ function createDynamicFunction(name, script, $arguments) {
|
||||
'scriptResourceCache',
|
||||
'flowUtils',
|
||||
'produceArtifact',
|
||||
'require',
|
||||
`${script}\n return ${name}`,
|
||||
)(
|
||||
$arguments,
|
||||
@@ -1003,7 +997,6 @@ function createDynamicFunction(name, script, $arguments) {
|
||||
scriptResourceCache,
|
||||
flowUtils,
|
||||
produceArtifact,
|
||||
eval(`typeof require !== "undefined"`) ? require : undefined,
|
||||
);
|
||||
} else {
|
||||
return new Function(
|
||||
@@ -1014,7 +1007,7 @@ function createDynamicFunction(name, script, $arguments) {
|
||||
'scriptResourceCache',
|
||||
'flowUtils',
|
||||
'produceArtifact',
|
||||
'require',
|
||||
|
||||
`${script}\n return ${name}`,
|
||||
)(
|
||||
$arguments,
|
||||
@@ -1024,7 +1017,6 @@ function createDynamicFunction(name, script, $arguments) {
|
||||
scriptResourceCache,
|
||||
flowUtils,
|
||||
produceArtifact,
|
||||
eval(`typeof require !== "undefined"`) ? require : undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,14 +153,6 @@ function trojan(proxy) {
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
@@ -223,14 +215,6 @@ function vmess(proxy) {
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
|
||||
// AEAD
|
||||
if (isPresent(proxy, 'aead')) {
|
||||
@@ -302,14 +286,6 @@ function vless(proxy) {
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
@@ -363,11 +339,6 @@ function socks5(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -447,14 +418,6 @@ function hysteria2(proxy) {
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,skip-cert-verify=${proxy['skip-cert-verify']}`,
|
||||
'skip-cert-verify',
|
||||
|
||||
@@ -204,6 +204,11 @@ const tlsParser = (proxy, parsedProxy) => {
|
||||
if (proxy.ca) parsedProxy.tls.certificate_path = `${proxy.ca}`;
|
||||
if (proxy.ca_str) parsedProxy.tls.certificate = [proxy.ca_str];
|
||||
if (proxy['ca-str']) parsedProxy.tls.certificate = [proxy['ca-str']];
|
||||
if (proxy['client-fingerprint'] && proxy['client-fingerprint'] !== '')
|
||||
parsedProxy.tls.utls = {
|
||||
enabled: true,
|
||||
fingerprint: proxy['client-fingerprint'],
|
||||
};
|
||||
if (proxy['reality-opts']) {
|
||||
parsedProxy.tls.reality = { enabled: true };
|
||||
if (proxy['reality-opts']['public-key'])
|
||||
@@ -212,13 +217,7 @@ const tlsParser = (proxy, parsedProxy) => {
|
||||
if (proxy['reality-opts']['short-id'])
|
||||
parsedProxy.tls.reality.short_id =
|
||||
proxy['reality-opts']['short-id'];
|
||||
parsedProxy.tls.utls = { enabled: true };
|
||||
}
|
||||
if (proxy['client-fingerprint'] && proxy['client-fingerprint'] !== '')
|
||||
parsedProxy.tls.utls = {
|
||||
enabled: true,
|
||||
fingerprint: proxy['client-fingerprint'],
|
||||
};
|
||||
if (!parsedProxy.tls.enabled) delete parsedProxy.tls;
|
||||
};
|
||||
|
||||
|
||||
@@ -675,15 +675,6 @@ function tuic(proxy) {
|
||||
'alpn',
|
||||
);
|
||||
|
||||
if (isPresent(proxy, 'ports')) {
|
||||
result.append(`,port-hopping=${proxy.ports.replace(/,/g, ';')}`);
|
||||
}
|
||||
|
||||
result.appendIfPresent(
|
||||
`,port-hopping-interval=${proxy['hop-interval']}`,
|
||||
'hop-interval',
|
||||
);
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
@@ -944,15 +935,6 @@ function hysteria2(proxy) {
|
||||
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
if (isPresent(proxy, 'ports')) {
|
||||
result.append(`,port-hopping=${proxy.ports.replace(/,/g, ';')}`);
|
||||
}
|
||||
|
||||
result.appendIfPresent(
|
||||
`,port-hopping-interval=${proxy['hop-interval']}`,
|
||||
'hop-interval',
|
||||
);
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Result, isPresent } from './utils';
|
||||
import { Result } from './utils';
|
||||
import Surge_Producer from './surge';
|
||||
import { isIPv4, isIPv6 } from '@/utils';
|
||||
import { isIPv4, isIPv6, isPresent } from '@/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
const targetPlatform = 'SurgeMac';
|
||||
@@ -84,7 +84,6 @@ function shadowsocksr(proxy) {
|
||||
for (const [key, value] of Object.entries({
|
||||
cipher: '-m',
|
||||
obfs: '-o',
|
||||
'obfs-param': '-g',
|
||||
password: '-k',
|
||||
port: '-p',
|
||||
protocol: '-O',
|
||||
@@ -93,10 +92,8 @@ function shadowsocksr(proxy) {
|
||||
'local-port': '-l',
|
||||
'local-address': '-b',
|
||||
})) {
|
||||
if (external_proxy[key] != null) {
|
||||
external_proxy.args.push(value);
|
||||
external_proxy.args.push(external_proxy[key]);
|
||||
}
|
||||
external_proxy.args.push(value);
|
||||
external_proxy.args.push(external_proxy[key]);
|
||||
}
|
||||
|
||||
return external(external_proxy);
|
||||
|
||||
@@ -293,7 +293,7 @@ export function getFlag(name) {
|
||||
'沪俄',
|
||||
'Moscow',
|
||||
],
|
||||
'🇸🇦': ['Saudi', '沙特阿拉伯', '沙特', 'Riyadh', '利雅得'],
|
||||
'🇸🇦': ['Saudi', '沙特阿拉伯', '沙特'],
|
||||
'🇸🇪': ['Sweden', '瑞典'],
|
||||
'🇸🇬': [
|
||||
'Singapore',
|
||||
|
||||
@@ -94,23 +94,6 @@ function getPolicyDescriptor(str) {
|
||||
// };
|
||||
// })();
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function getRandomPort(portString) {
|
||||
let portParts = portString.split(/,|\//);
|
||||
let randomPart = portParts[Math.floor(Math.random() * portParts.length)];
|
||||
if (randomPart.includes('-')) {
|
||||
let [min, max] = randomPart.split('-').map(Number);
|
||||
return getRandomInt(min, max);
|
||||
} else {
|
||||
return Number(randomPart);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ipAddress,
|
||||
isIPv4,
|
||||
@@ -122,5 +105,4 @@ export {
|
||||
getIfPresent,
|
||||
// utf8ArrayToStr,
|
||||
getPolicyDescriptor,
|
||||
getRandomPort,
|
||||
};
|
||||
|
||||
@@ -28,9 +28,7 @@ export function getPlatformFromUserAgent({ ua, UA }) {
|
||||
return 'Stash';
|
||||
} else if (
|
||||
ua === 'meta' ||
|
||||
(ua.indexOf('clash') !== -1 && ua.indexOf('meta') !== -1) ||
|
||||
ua.indexOf('clash-verge') !== -1 ||
|
||||
ua.indexOf('flclash') !== -1
|
||||
(ua.indexOf('clash') !== -1 && ua.indexOf('meta') !== -1)
|
||||
) {
|
||||
return 'ClashMeta';
|
||||
} else if (ua.indexOf('clash') !== -1) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Sub-Store
|
||||
description: '支持 Surge 正式版的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *'
|
||||
compat_arguments:
|
||||
ability: http-client-policy
|
||||
cronexp: 55 23 * * *
|
||||
sync: '"Sub-Store Sync"'
|
||||
timeout: '120'
|
||||
engine: auto
|
||||
produce: '"# Sub-Store Produce"'
|
||||
produce_cronexp: 50 */6 * * *
|
||||
produce_sub: '"sub1,sub2"'
|
||||
produce_col: '"col1,col2"'
|
||||
compat_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\n定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 ''同步'' 或 ''同步配置''\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n脚本超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存\n\n6️⃣ produce\n\n自定义处理订阅的定时任务名\n一般用于定时处理耗时较长的订阅, 以更新缓存\n这样 Surge 中拉取的时候就能用到缓存, 不至于总是超时\n若设为 # 可取消此定时任务\n默认不开启\n\n7️⃣ produce_cronexp\n\n配置处理订阅的定时任务\n\n默认为每 6 小时\n\n9️⃣ produce_sub\n\n自定义需定时处理的单条订阅名\n多个用 , 连接\n\n🔟 produce_col\n\n自定义需定时处理的组合订阅名\n多个用 , 连接\n\n⚠️ 注意: 是 名称(name) 不是 显示名称(displayName)\n如果名称需要编码, 请编码后再用 , 连接\n顺序: 并发执行单条订阅, 然后并发执行组合订阅'
|
||||
scriptings:
|
||||
- http_request:
|
||||
name: Sub-Store Core
|
||||
match: ^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info)))
|
||||
script_url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js
|
||||
body_required: true
|
||||
- http_request:
|
||||
name: Sub-Store Simple
|
||||
match: ^https?:\/\/sub\.store
|
||||
script_url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js
|
||||
body_required: true
|
||||
- schedule:
|
||||
name: '{{{sync}}}'
|
||||
cron: '{{{cronexp}}}'
|
||||
script_url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
- schedule:
|
||||
name: '{{{produce}}}'
|
||||
cron: '{{{produce_cronexp}}}'
|
||||
script_url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
arguments:
|
||||
_compat.$argument: '"sub={{{produce_sub}}}&col={{{produce_col}}}"'
|
||||
mitm:
|
||||
hostnames:
|
||||
- sub.store
|
||||
@@ -45,9 +45,6 @@ Surge Mac 版如何支持 SSR, 如何去除 HTTP 传输层以支持 类似 VMess
|
||||
### 5. Shadowrocket
|
||||
安装使用 模块 [`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) 即可。
|
||||
|
||||
### 6. Egern
|
||||
安装使用 模块 [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Egern.yaml`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Egern.yaml) 即可。
|
||||
|
||||
## 使用 Sub-Store
|
||||
1. 使用 Safari 打开这个 https://sub.store 如网页正常打开并且未弹出任何错误提示,说明 Sub-Store 已经配置成功。
|
||||
2. 可以把 Sub-Store 添加到主屏幕,即可获得类似于 APP 的使用体验。
|
||||
@@ -59,4 +56,4 @@ https://github.com/sub-store-org/Sub-Store/wiki/%E9%93%BE%E6%8E%A5%E5%8F%82%E6%9
|
||||
|
||||
## 脚本使用说明
|
||||
|
||||
https://github.com/sub-store-org/Sub-Store/wiki/%E8%84%9A%E6%9C%AC%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
|
||||
https://github.com/sub-store-org/Sub-Store/wiki/%E8%84%9A%E6%9C%AC%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
|
||||
@@ -18,8 +18,8 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 10. `sni` 在某些协议里会自动与 `servername` 转换
|
||||
// 11. 读取节点的 ca-str 和 _ca (后端文件路径) 字段, 自动计算 fingerprint (参考 https://t.me/zhetengsha/1512)
|
||||
// 12. 以 Surge 为例, 最新的参数一般我都会跟进, 以 Surge 文档为例, 一些常用的: TUIC/Hysteria 2 的 `ecn`, Snell 的 `reuse` 连接复用, QUIC 策略 block-quic`, Hysteria 2 下载带宽 `down`
|
||||
|
||||
// require 为 Node.js 的 require, 在 Node.js 运行环境下 可以用来引入模块
|
||||
//
|
||||
// 如果只是为了快速修改或者筛选 可以参考 脚本操作支持节点快捷脚本 https://t.me/zhetengsha/970 和 脚本筛选支持节点快捷脚本 https://t.me/zhetengsha/1009
|
||||
|
||||
// $arguments 为传入的脚本参数
|
||||
|
||||
@@ -42,7 +42,6 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// parse, // 订阅解析
|
||||
// process, // 节点操作/文件操作
|
||||
// produce, // 输出订阅
|
||||
// getRandomPort, // 获取随机端口(参考 ports 端口跳跃的格式 443,8443,5000-6000)
|
||||
// ipAddress, // https://github.com/beaugunderson/ip-address
|
||||
// isIPv4,
|
||||
// isIPv6,
|
||||
@@ -54,8 +53,6 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// Gist, // Gist 类
|
||||
// }
|
||||
|
||||
// 如果只是为了快速修改或者筛选 可以参考 脚本操作支持节点快捷脚本 https://t.me/zhetengsha/970 和 脚本筛选支持节点快捷脚本 https://t.me/zhetengsha/1009
|
||||
// ⚠️ 注意: 函数式(即本文件这样的 function operator() {}) 和快捷操作(下面使用 $server) 只能二选一
|
||||
// 示例: 给节点名添加前缀
|
||||
// $server.name = `[${ProxyUtils.getISO($server.name)}] ${$server.name}`
|
||||
// 示例: 给节点名添加旗帜
|
||||
|
||||
Reference in New Issue
Block a user