mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2026-03-21 23:22:36 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbaac2de6f | ||
|
|
614438ae3d | ||
|
|
4966132397 | ||
|
|
059c4bd148 | ||
|
|
63887e3dad | ||
|
|
7fd585b5d4 |
16
README.md
16
README.md
@@ -31,9 +31,9 @@ Core functionalities:
|
||||
- [x] SSD URI
|
||||
- [x] V2RayN URI
|
||||
- [x] Hysteria2 URI
|
||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5)
|
||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
||||
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, SOCKS5, WireGuard, VLESS, Hysteria2)
|
||||
- [x] Surge (SS, VMess, Trojan, HTTP, SOCKS5, TUIC, Snell, Hysteria2, SSR(external, only for macOS), WireGuard(Surge to Surge))
|
||||
- [x] Surge (SS, VMess, Trojan, HTTP, SOCKS5, TUIC, Snell, Hysteria2, SSR(external, only for macOS), External Proxy Program(only for macOS), WireGuard(Surge to Surge))
|
||||
- [x] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, WireGuard(Surfboard to Surfboard))
|
||||
- [x] Shadowrocket (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria2, TUIC)
|
||||
- [x] Clash.Meta (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria2, TUIC)
|
||||
@@ -42,18 +42,18 @@ Core functionalities:
|
||||
|
||||
### Supported Target Platforms
|
||||
|
||||
- [x] QX
|
||||
- [x] Loon
|
||||
- [x] Surge
|
||||
- [x] Surfboard
|
||||
- [x] Plain JSON
|
||||
- [x] Stash
|
||||
- [x] Clash.Meta
|
||||
- [x] Clash.Meta(mihomo)
|
||||
- [x] Clash
|
||||
- [x] Surfboard
|
||||
- [x] Surge
|
||||
- [x] Loon
|
||||
- [x] Shadowrocket
|
||||
- [x] QX
|
||||
- [x] sing-box
|
||||
- [x] V2Ray
|
||||
- [x] V2Ray URI
|
||||
- [x] Plain JSON
|
||||
|
||||
## 2. Subscription Formatting
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.171",
|
||||
"version": "2.14.176",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -229,6 +229,11 @@ function lastParse(proxy) {
|
||||
delete proxy.network;
|
||||
}
|
||||
}
|
||||
if (['vless'].includes(proxy.type)) {
|
||||
if (!proxy.network) {
|
||||
proxy.network = 'tcp';
|
||||
}
|
||||
}
|
||||
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
proxy.tls = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { getIfNotBlank, isPresent, isNotBlank, getIfPresent } from '@/utils';
|
||||
import {
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
getIfNotBlank,
|
||||
isPresent,
|
||||
isNotBlank,
|
||||
getIfPresent,
|
||||
} from '@/utils';
|
||||
import getSurgeParser from './peggy/surge';
|
||||
import getLoonParser from './peggy/loon';
|
||||
import getQXParser from './peggy/qx';
|
||||
@@ -614,6 +621,15 @@ function QX_VMess() {
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function QX_VLESS() {
|
||||
const name = 'QX VLESS Parser';
|
||||
const test = (line) => {
|
||||
return /^vless\s*=/.test(line.split(',')[0].trim());
|
||||
};
|
||||
const parse = (line) => getQXParser().parse(line);
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function QX_Trojan() {
|
||||
const name = 'QX Trojan Parser';
|
||||
const test = (line) => {
|
||||
@@ -872,6 +888,76 @@ function Surge_Socks5() {
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function Surge_External() {
|
||||
const name = 'Surge External Parser';
|
||||
const test = (line) => {
|
||||
return /^.*=\s*external/.test(line.split(',')[0]);
|
||||
};
|
||||
const parse = (line) => {
|
||||
let parsed = /^\s*(.*?)\s*?=\s*?external\s*?,\s*(.*?)\s*$/.exec(line);
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [_, name, other] = parsed;
|
||||
line = other;
|
||||
|
||||
// exec = "/usr/bin/ssh" 或 exec = /usr/bin/ssh
|
||||
let exec = /(,|^)\s*?exec\s*?=\s*"(.*?)"\s*?(,|$)/.exec(line)?.[2];
|
||||
if (!exec) {
|
||||
exec = /(,|^)\s*?exec\s*?=\s*(.*?)\s*?(,|$)/.exec(line)?.[2];
|
||||
}
|
||||
|
||||
// local-port = "1080" 或 local-port = 1080
|
||||
let localPort = /(,|^)\s*?local-port\s*?=\s*"(.*?)"\s*?(,|$)/.exec(
|
||||
line,
|
||||
)?.[2];
|
||||
if (!localPort) {
|
||||
localPort = /(,|^)\s*?local-port\s*?=\s*(.*?)\s*?(,|$)/.exec(
|
||||
line,
|
||||
)?.[2];
|
||||
}
|
||||
|
||||
const argsRegex = /(,|^)\s*?args\s*?=\s*("(.*?)"|(.*?))(?=\s*?(,|$))/g;
|
||||
let argsMatch;
|
||||
const args = [];
|
||||
while ((argsMatch = argsRegex.exec(line)) !== null) {
|
||||
if (argsMatch[3] != null) {
|
||||
args.push(argsMatch[3]);
|
||||
} else if (argsMatch[4] != null) {
|
||||
args.push(argsMatch[4]);
|
||||
}
|
||||
}
|
||||
const addressesRegex =
|
||||
/(,|^)\s*?addresses\s*?=\s*("(.*?)"|(.*?))(?=\s*?(,|$))/g;
|
||||
let addressesMatch;
|
||||
const addresses = [];
|
||||
while ((addressesMatch = addressesRegex.exec(line)) !== null) {
|
||||
let ip;
|
||||
if (addressesMatch[3] != null) {
|
||||
ip = addressesMatch[3];
|
||||
} else if (addressesMatch[4] != null) {
|
||||
ip = addressesMatch[4];
|
||||
}
|
||||
if (ip != null) {
|
||||
ip = `${ip}`.trim().replace(/^\[/, '').replace(/\]$/, '');
|
||||
}
|
||||
if (isIP(ip)) {
|
||||
addresses.push(ip);
|
||||
}
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
type: 'external',
|
||||
name,
|
||||
exec,
|
||||
'local-port': localPort,
|
||||
args,
|
||||
addresses,
|
||||
};
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function Surge_Snell() {
|
||||
const name = 'Surge Snell Parser';
|
||||
const test = (line) => {
|
||||
@@ -907,6 +993,10 @@ function Surge_Hysteria2() {
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function isIP(ip) {
|
||||
return isIPv4(ip) || isIPv6(ip);
|
||||
}
|
||||
|
||||
export default [
|
||||
URI_SS(),
|
||||
URI_SSR(),
|
||||
@@ -924,6 +1014,7 @@ export default [
|
||||
Surge_WireGuard(),
|
||||
Surge_Hysteria2(),
|
||||
Surge_Socks5(),
|
||||
Surge_External(),
|
||||
Loon_SS(),
|
||||
Loon_SSR(),
|
||||
Loon_VMess(),
|
||||
@@ -935,6 +1026,7 @@ export default [
|
||||
QX_SS(),
|
||||
QX_SSR(),
|
||||
QX_VMess(),
|
||||
QX_VLESS(),
|
||||
QX_Trojan(),
|
||||
QX_Http(),
|
||||
QX_Socks5(),
|
||||
|
||||
@@ -38,7 +38,7 @@ const grammars = String.raw`
|
||||
}
|
||||
}
|
||||
|
||||
start = (trojan/shadowsocks/vmess/http/socks5) {
|
||||
start = (trojan/shadowsocks/vmess/vless/http/socks5) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
@@ -91,6 +91,13 @@ vmess = "vmess" equals address
|
||||
handleObfs();
|
||||
}
|
||||
|
||||
vless = "vless" equals address
|
||||
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
|
||||
proxy.type = "vless";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
handleObfs();
|
||||
}
|
||||
|
||||
http = "http" equals address
|
||||
(username/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)*{
|
||||
proxy.type = "http";
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
start = (trojan/shadowsocks/vmess/http/socks5) {
|
||||
start = (trojan/shadowsocks/vmess/vless/http/socks5) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
@@ -89,6 +89,13 @@ vmess = "vmess" equals address
|
||||
handleObfs();
|
||||
}
|
||||
|
||||
vless = "vless" equals address
|
||||
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
|
||||
proxy.type = "vless";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
handleObfs();
|
||||
}
|
||||
|
||||
http = "http" equals address
|
||||
(username/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)*{
|
||||
proxy.type = "http";
|
||||
|
||||
@@ -36,7 +36,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -46,7 +46,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -56,18 +56,18 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/
|
||||
}
|
||||
handleWebsocket();
|
||||
}
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleWebsocket();
|
||||
}
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "snell";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -76,10 +76,10 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
$set(proxy, "obfs-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/no_error_alert/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;
|
||||
}
|
||||
@@ -89,10 +89,10 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
proxy.tls = true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -44,7 +44,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -54,18 +54,18 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/
|
||||
}
|
||||
handleWebsocket();
|
||||
}
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleWebsocket();
|
||||
}
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "snell";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -74,10 +74,10 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
$set(proxy, "obfs-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/no_error_alert/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;
|
||||
}
|
||||
@@ -87,10 +87,10 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
proxy.tls = true;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,9 @@ function trojan(proxy) {
|
||||
result.append(
|
||||
`${proxy.name}=trojan,${proxy.server},${proxy.port},"${proxy.password}"`,
|
||||
);
|
||||
|
||||
if (proxy.network === 'tcp') {
|
||||
delete proxy.network;
|
||||
}
|
||||
// transport
|
||||
if (isPresent(proxy, 'network')) {
|
||||
if (proxy.network === 'ws') {
|
||||
@@ -106,6 +108,18 @@ function trojan(proxy) {
|
||||
`,host=${proxy['ws-opts']?.headers?.Host}`,
|
||||
'ws-opts.headers.Host',
|
||||
);
|
||||
} else if (proxy.network === 'http') {
|
||||
result.append(`,transport=http`);
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
result.appendIfPresent(
|
||||
`,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||
'http-opts.path',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||
'http-opts.headers.Host',
|
||||
);
|
||||
} else {
|
||||
throw new Error(`network ${proxy.network} is unsupported`);
|
||||
}
|
||||
@@ -134,7 +148,9 @@ function vmess(proxy) {
|
||||
result.append(
|
||||
`${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`,
|
||||
);
|
||||
|
||||
if (proxy.network === 'tcp') {
|
||||
delete proxy.network;
|
||||
}
|
||||
// transport
|
||||
if (isPresent(proxy, 'network')) {
|
||||
if (proxy.network === 'ws') {
|
||||
@@ -195,13 +211,15 @@ function vmess(proxy) {
|
||||
|
||||
function vless(proxy) {
|
||||
if (proxy['reality-opts']) {
|
||||
throw new Error(`reality is unsupported`);
|
||||
throw new Error(`VLESS REALITY is unsupported`);
|
||||
}
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=vless,${proxy.server},${proxy.port},"${proxy.uuid}"`,
|
||||
);
|
||||
|
||||
if (proxy.network === 'tcp') {
|
||||
delete proxy.network;
|
||||
}
|
||||
// transport
|
||||
if (isPresent(proxy, 'network')) {
|
||||
if (proxy.network === 'ws') {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isPresent, Result } from './utils';
|
||||
const targetPlatform = 'QX';
|
||||
|
||||
export default function QX_Producer() {
|
||||
const produce = (proxy) => {
|
||||
const produce = (proxy, type, opts = {}) => {
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy);
|
||||
@@ -17,6 +17,14 @@ export default function QX_Producer() {
|
||||
return http(proxy);
|
||||
case 'socks5':
|
||||
return socks5(proxy);
|
||||
case 'vless':
|
||||
if (opts['include-unsupported-proxy']) {
|
||||
return vless(proxy);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Platform ${targetPlatform}(App Store Release) does not support proxy type: ${proxy.type}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Platform ${targetPlatform} does not support proxy type: ${proxy.type}`,
|
||||
@@ -325,6 +333,105 @@ function vmess(proxy) {
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
function vless(proxy) {
|
||||
if (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) {
|
||||
throw new Error(`VLESS XTLS/REALITY is not supported`);
|
||||
}
|
||||
|
||||
const result = new Result(proxy);
|
||||
const append = result.append.bind(result);
|
||||
const appendIfPresent = result.appendIfPresent.bind(result);
|
||||
|
||||
append(`vless=${proxy.server}:${proxy.port}`);
|
||||
|
||||
// The method field for vless should be none.
|
||||
let cipher = 'none';
|
||||
// if (proxy.cipher === 'auto') {
|
||||
// cipher = 'chacha20-ietf-poly1305';
|
||||
// } else {
|
||||
// cipher = proxy.cipher;
|
||||
// }
|
||||
append(`,method=${cipher}`);
|
||||
|
||||
append(`,password=${proxy.uuid}`);
|
||||
|
||||
// obfs
|
||||
if (needTls(proxy)) {
|
||||
proxy.tls = true;
|
||||
}
|
||||
if (isPresent(proxy, 'network')) {
|
||||
if (proxy.network === 'ws') {
|
||||
if (proxy.tls) append(`,obfs=wss`);
|
||||
else append(`,obfs=ws`);
|
||||
} else if (proxy.network === 'http') {
|
||||
append(`,obfs=http`);
|
||||
} else if (!['tcp'].includes(proxy.network)) {
|
||||
throw new Error(`network ${proxy.network} is unsupported`);
|
||||
}
|
||||
let transportPath = proxy[`${proxy.network}-opts`]?.path;
|
||||
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||
appendIfPresent(
|
||||
`,obfs-uri=${
|
||||
Array.isArray(transportPath) ? transportPath[0] : transportPath
|
||||
}`,
|
||||
`${proxy.network}-opts.path`,
|
||||
);
|
||||
appendIfPresent(
|
||||
`,obfs-host=${
|
||||
Array.isArray(transportHost) ? transportHost[0] : transportHost
|
||||
}`,
|
||||
`${proxy.network}-opts.headers.Host`,
|
||||
);
|
||||
} else {
|
||||
// over-tls
|
||||
if (proxy.tls) append(`,obfs=over-tls`);
|
||||
}
|
||||
|
||||
if (needTls(proxy)) {
|
||||
appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
|
||||
appendIfPresent(
|
||||
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
|
||||
'tls-no-session-ticket',
|
||||
);
|
||||
appendIfPresent(
|
||||
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
|
||||
'tls-no-session-reuse',
|
||||
);
|
||||
// tls fingerprint
|
||||
appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
|
||||
// tls verification
|
||||
appendIfPresent(
|
||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||
'skip-cert-verify',
|
||||
);
|
||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||
}
|
||||
|
||||
// tfo
|
||||
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||
|
||||
// server_check_url
|
||||
result.appendIfPresent(
|
||||
`,server_check_url=${proxy['test-url']}`,
|
||||
'test-url',
|
||||
);
|
||||
|
||||
// tag
|
||||
append(`,tag=${proxy.name}`);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
function http(proxy) {
|
||||
const result = new Result(proxy);
|
||||
|
||||
@@ -2,241 +2,243 @@ import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
|
||||
export default function Stash_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies) => {
|
||||
const produce = (proxies, type, opts = {}) => {
|
||||
// https://stash.wiki/proxy-protocols/proxy-types#shadowsocks
|
||||
return (
|
||||
'proxies:\n' +
|
||||
proxies
|
||||
.filter((proxy) => {
|
||||
if (
|
||||
const list = proxies
|
||||
.filter((proxy) => {
|
||||
if (opts['include-unsupported-proxy']) return true;
|
||||
if (
|
||||
![
|
||||
'ss',
|
||||
'ssr',
|
||||
'vmess',
|
||||
'socks5',
|
||||
'http',
|
||||
'snell',
|
||||
'trojan',
|
||||
'tuic',
|
||||
'vless',
|
||||
'wireguard',
|
||||
'hysteria',
|
||||
'hysteria2',
|
||||
].includes(proxy.type) ||
|
||||
(proxy.type === 'ss' &&
|
||||
![
|
||||
'ss',
|
||||
'ssr',
|
||||
'vmess',
|
||||
'socks5',
|
||||
'http',
|
||||
'snell',
|
||||
'trojan',
|
||||
'tuic',
|
||||
'vless',
|
||||
'wireguard',
|
||||
'hysteria',
|
||||
'hysteria2',
|
||||
].includes(proxy.type) ||
|
||||
(proxy.type === 'ss' &&
|
||||
![
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'rc4-md5',
|
||||
'chacha20-ietf',
|
||||
'xchacha20',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' &&
|
||||
String(proxy.version) === '4') ||
|
||||
(proxy.type === 'vless' && proxy['reality-opts'])
|
||||
) {
|
||||
return false;
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'rc4-md5',
|
||||
'chacha20-ietf',
|
||||
'xchacha20',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
||||
(proxy.type === 'vless' && proxy['reality-opts'])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((proxy) => {
|
||||
if (proxy.type === 'vmess') {
|
||||
// handle vmess aead
|
||||
if (isPresent(proxy, 'aead')) {
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
}
|
||||
delete proxy.aead;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((proxy) => {
|
||||
if (proxy.type === 'vmess') {
|
||||
// handle vmess aead
|
||||
if (isPresent(proxy, 'aead')) {
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
}
|
||||
delete proxy.aead;
|
||||
}
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
delete proxy.sni;
|
||||
}
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml#L400
|
||||
// https://stash.wiki/proxy-protocols/proxy-types#vmess
|
||||
if (
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
proxy.cipher = 'auto';
|
||||
}
|
||||
} else if (proxy.type === 'tuic') {
|
||||
if (isPresent(proxy, 'alpn')) {
|
||||
proxy.alpn = Array.isArray(proxy.alpn)
|
||||
? proxy.alpn
|
||||
: [proxy.alpn];
|
||||
} else {
|
||||
proxy.alpn = ['h3'];
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/adapter/outbound/tuic.go#L197
|
||||
if (
|
||||
(!proxy.token || proxy.token.length === 0) &&
|
||||
!isPresent(proxy, 'version')
|
||||
) {
|
||||
proxy.version = 5;
|
||||
}
|
||||
} else if (proxy.type === 'hysteria') {
|
||||
// auth_str 将会在未来某个时候删除 但是有的机场不规范
|
||||
if (
|
||||
isPresent(proxy, 'auth_str') &&
|
||||
!isPresent(proxy, 'auth-str')
|
||||
) {
|
||||
proxy['auth-str'] = proxy['auth_str'];
|
||||
}
|
||||
if (isPresent(proxy, 'alpn')) {
|
||||
proxy.alpn = Array.isArray(proxy.alpn)
|
||||
? proxy.alpn
|
||||
: [proxy.alpn];
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'down') &&
|
||||
!isPresent(proxy, 'down-speed')
|
||||
) {
|
||||
proxy['down-speed'] = proxy.down;
|
||||
delete proxy.down;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'up') &&
|
||||
!isPresent(proxy, 'up-speed')
|
||||
) {
|
||||
proxy['up-speed'] = proxy.up;
|
||||
delete proxy.up;
|
||||
}
|
||||
if (isPresent(proxy, 'down-speed')) {
|
||||
proxy['down-speed'] =
|
||||
`${proxy['down-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
if (isPresent(proxy, 'up-speed')) {
|
||||
proxy['up-speed'] =
|
||||
`${proxy['up-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
} else if (proxy.type === 'hysteria2') {
|
||||
if (
|
||||
isPresent(proxy, 'password') &&
|
||||
!isPresent(proxy, 'auth')
|
||||
) {
|
||||
proxy.auth = proxy.password;
|
||||
delete proxy.password;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'down') &&
|
||||
!isPresent(proxy, 'down-speed')
|
||||
) {
|
||||
proxy['down-speed'] = proxy.down;
|
||||
delete proxy.down;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'up') &&
|
||||
!isPresent(proxy, 'up-speed')
|
||||
) {
|
||||
proxy['up-speed'] = proxy.up;
|
||||
delete proxy.up;
|
||||
}
|
||||
if (isPresent(proxy, 'down-speed')) {
|
||||
proxy['down-speed'] =
|
||||
`${proxy['down-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
if (isPresent(proxy, 'up-speed')) {
|
||||
proxy['up-speed'] =
|
||||
`${proxy['up-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
} else if (proxy.type === 'wireguard') {
|
||||
proxy.keepalive =
|
||||
proxy.keepalive ?? proxy['persistent-keepalive'];
|
||||
proxy['persistent-keepalive'] = proxy.keepalive;
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
delete proxy.sni;
|
||||
}
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
delete proxy.sni;
|
||||
}
|
||||
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml#L400
|
||||
// https://stash.wiki/proxy-protocols/proxy-types#vmess
|
||||
if (
|
||||
['vmess', 'vless'].includes(proxy.type) &&
|
||||
proxy.network === 'http'
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.path') &&
|
||||
!Array.isArray(httpPath)
|
||||
) {
|
||||
proxy['http-opts'].path = [httpPath];
|
||||
}
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||
!Array.isArray(httpHost)
|
||||
) {
|
||||
proxy['http-opts'].headers.Host = [httpHost];
|
||||
}
|
||||
proxy.cipher = 'auto';
|
||||
}
|
||||
} else if (proxy.type === 'tuic') {
|
||||
if (isPresent(proxy, 'alpn')) {
|
||||
proxy.alpn = Array.isArray(proxy.alpn)
|
||||
? proxy.alpn
|
||||
: [proxy.alpn];
|
||||
} else {
|
||||
proxy.alpn = ['h3'];
|
||||
}
|
||||
if (
|
||||
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
|
||||
proxy.type,
|
||||
)
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
delete proxy.tls;
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
if (proxy['tls-fingerprint']) {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['test-url']) {
|
||||
proxy['benchmark-url'] = proxy['test-url'];
|
||||
delete proxy['test-url'];
|
||||
}
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/adapter/outbound/tuic.go#L197
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
(!proxy.token || proxy.token.length === 0) &&
|
||||
!isPresent(proxy, 'version')
|
||||
) {
|
||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||
proxy.version = 5;
|
||||
}
|
||||
return ' - ' + JSON.stringify(proxy) + '\n';
|
||||
})
|
||||
.join('')
|
||||
);
|
||||
} else if (proxy.type === 'hysteria') {
|
||||
// auth_str 将会在未来某个时候删除 但是有的机场不规范
|
||||
if (
|
||||
isPresent(proxy, 'auth_str') &&
|
||||
!isPresent(proxy, 'auth-str')
|
||||
) {
|
||||
proxy['auth-str'] = proxy['auth_str'];
|
||||
}
|
||||
if (isPresent(proxy, 'alpn')) {
|
||||
proxy.alpn = Array.isArray(proxy.alpn)
|
||||
? proxy.alpn
|
||||
: [proxy.alpn];
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'down') &&
|
||||
!isPresent(proxy, 'down-speed')
|
||||
) {
|
||||
proxy['down-speed'] = proxy.down;
|
||||
delete proxy.down;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'up') &&
|
||||
!isPresent(proxy, 'up-speed')
|
||||
) {
|
||||
proxy['up-speed'] = proxy.up;
|
||||
delete proxy.up;
|
||||
}
|
||||
if (isPresent(proxy, 'down-speed')) {
|
||||
proxy['down-speed'] =
|
||||
`${proxy['down-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
if (isPresent(proxy, 'up-speed')) {
|
||||
proxy['up-speed'] =
|
||||
`${proxy['up-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
} else if (proxy.type === 'hysteria2') {
|
||||
if (
|
||||
isPresent(proxy, 'password') &&
|
||||
!isPresent(proxy, 'auth')
|
||||
) {
|
||||
proxy.auth = proxy.password;
|
||||
delete proxy.password;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'tfo') &&
|
||||
!isPresent(proxy, 'fast-open')
|
||||
) {
|
||||
proxy['fast-open'] = proxy.tfo;
|
||||
delete proxy.tfo;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'down') &&
|
||||
!isPresent(proxy, 'down-speed')
|
||||
) {
|
||||
proxy['down-speed'] = proxy.down;
|
||||
delete proxy.down;
|
||||
}
|
||||
if (
|
||||
isPresent(proxy, 'up') &&
|
||||
!isPresent(proxy, 'up-speed')
|
||||
) {
|
||||
proxy['up-speed'] = proxy.up;
|
||||
delete proxy.up;
|
||||
}
|
||||
if (isPresent(proxy, 'down-speed')) {
|
||||
proxy['down-speed'] =
|
||||
`${proxy['down-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
if (isPresent(proxy, 'up-speed')) {
|
||||
proxy['up-speed'] =
|
||||
`${proxy['up-speed']}`.match(/\d+/)?.[0] || 0;
|
||||
}
|
||||
} else if (proxy.type === 'wireguard') {
|
||||
proxy.keepalive =
|
||||
proxy.keepalive ?? proxy['persistent-keepalive'];
|
||||
proxy['persistent-keepalive'] = proxy.keepalive;
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
delete proxy.sni;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
['vmess', 'vless'].includes(proxy.type) &&
|
||||
proxy.network === 'http'
|
||||
) {
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.path') &&
|
||||
!Array.isArray(httpPath)
|
||||
) {
|
||||
proxy['http-opts'].path = [httpPath];
|
||||
}
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||
!Array.isArray(httpHost)
|
||||
) {
|
||||
proxy['http-opts'].headers.Host = [httpHost];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
|
||||
proxy.type,
|
||||
)
|
||||
) {
|
||||
delete proxy.tls;
|
||||
}
|
||||
if (proxy['tls-fingerprint']) {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['test-url']) {
|
||||
proxy['benchmark-url'] = proxy['test-url'];
|
||||
delete proxy['test-url'];
|
||||
}
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
) {
|
||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||
}
|
||||
return proxy;
|
||||
});
|
||||
return type === 'internal'
|
||||
? list
|
||||
: 'proxies:\n' +
|
||||
list
|
||||
.map((proxy) => ' - ' + JSON.stringify(proxy) + '\n')
|
||||
.join('');
|
||||
};
|
||||
return { type, produce };
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { Result } from './utils';
|
||||
import Surge_Producer from './surge';
|
||||
import { isIPv4, isIPv6, isPresent } from '@/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
// const targetPlatform = 'SurgeMac';
|
||||
const targetPlatform = 'SurgeMac';
|
||||
|
||||
const surge_Producer = Surge_Producer();
|
||||
|
||||
export default function SurgeMac_Producer() {
|
||||
const produce = (proxy) => {
|
||||
switch (proxy.type) {
|
||||
case 'external':
|
||||
return external(proxy);
|
||||
case 'ssr':
|
||||
return shadowsocksr(proxy);
|
||||
default:
|
||||
@@ -16,19 +20,67 @@ export default function SurgeMac_Producer() {
|
||||
};
|
||||
return { produce };
|
||||
}
|
||||
|
||||
function shadowsocksr(proxy) {
|
||||
function external(proxy) {
|
||||
const result = new Result(proxy);
|
||||
|
||||
proxy.local_port = '__SubStoreLocalPort__';
|
||||
proxy.local_address = proxy.local_address ?? '127.0.0.1';
|
||||
|
||||
if (!proxy.exec || !proxy['local-port']) {
|
||||
throw new Error(`${proxy.type}: exec and local-port are required`);
|
||||
}
|
||||
result.append(
|
||||
`${proxy.name} = external, exec = "${
|
||||
proxy.exec || '/usr/local/bin/ssr-local'
|
||||
}", address = "${proxy.server}", local-port = ${proxy.local_port}`,
|
||||
`${proxy.name}=external,exec="${proxy.exec}",local-port=${proxy['local-port']}`,
|
||||
);
|
||||
|
||||
if (Array.isArray(proxy.args)) {
|
||||
proxy.args.map((args) => {
|
||||
result.append(`,args="${args}"`);
|
||||
});
|
||||
}
|
||||
if (Array.isArray(proxy.addresses)) {
|
||||
proxy.addresses.map((addresses) => {
|
||||
result.append(`,addresses=${addresses}`);
|
||||
});
|
||||
}
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
'no-error-alert',
|
||||
);
|
||||
|
||||
// tfo
|
||||
if (isPresent(proxy, 'tfo')) {
|
||||
result.append(`,tfo=${proxy['tfo']}`);
|
||||
} else if (isPresent(proxy, 'fast-open')) {
|
||||
result.append(`,tfo=${proxy['fast-open']}`);
|
||||
}
|
||||
|
||||
// test-url
|
||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||
|
||||
// block-quic
|
||||
result.appendIfPresent(`,block-quic=${proxy['block-quic']}`, 'block-quic');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
function shadowsocksr(proxy) {
|
||||
const external_proxy = {
|
||||
...proxy,
|
||||
type: 'external',
|
||||
exec: proxy.exec || '/usr/local/bin/ssr-local',
|
||||
'local-port': '__SubStoreLocalPort__',
|
||||
args: [],
|
||||
addresses: [],
|
||||
'local-address':
|
||||
proxy.local_address ?? proxy['local-address'] ?? '127.0.0.1',
|
||||
};
|
||||
|
||||
// https://manual.nssurge.com/policy/external-proxy.html
|
||||
if (isIP(proxy.server)) {
|
||||
external_proxy.addresses.push(proxy.server);
|
||||
} else {
|
||||
$.log(
|
||||
`Platform ${targetPlatform}, proxy type ${proxy.type}: addresses should be an IP address, but got ${proxy.server}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries({
|
||||
cipher: '-m',
|
||||
obfs: '-o',
|
||||
@@ -37,14 +89,16 @@ function shadowsocksr(proxy) {
|
||||
protocol: '-O',
|
||||
'protocol-param': '-G',
|
||||
server: '-s',
|
||||
local_port: '-l',
|
||||
local_address: '-b',
|
||||
'local-port': '-l',
|
||||
'local-address': '-b',
|
||||
})) {
|
||||
result.appendIfPresent(
|
||||
`, args = "${value}", args = "${proxy[key]}"`,
|
||||
key,
|
||||
);
|
||||
external_proxy.args.push(value);
|
||||
external_proxy.args.push(external_proxy[key]);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
return external(external_proxy);
|
||||
}
|
||||
|
||||
function isIP(ip) {
|
||||
return isIPv4(ip) || isIPv6(ip);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user