feat: new target platform "Clash.Meta"

This commit is contained in:
xream 2023-08-28 13:10:48 +08:00
parent 4bebcff1d3
commit af8e965866
No known key found for this signature in database
GPG Key ID: 1D2C5225471789F9
6 changed files with 148 additions and 7 deletions

View File

@ -11,7 +11,7 @@ Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.
</p> </p>
[![Build](https://github.com/Peng-YM/Sub-Store/actions/workflows/main.yml/badge.svg)](https://github.com/Peng-YM/Sub-Store/actions/workflows/main.yml) ![GitHub](https://img.shields.io/github/license/Peng-YM/Sub-Store) ![GitHub issues](https://img.shields.io/github/issues/Peng-YM/Sub-Store) ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/Peng-Ym/Sub-Store) ![Lines of code](https://img.shields.io/tokei/lines/github/Peng-YM/Sub-Store) ![Size](https://img.shields.io/github/languages/code-size/Peng-YM/Sub-Store) [![Build](https://github.com/Peng-YM/Sub-Store/actions/workflows/main.yml/badge.svg)](https://github.com/Peng-YM/Sub-Store/actions/workflows/main.yml) ![GitHub](https://img.shields.io/github/license/Peng-YM/Sub-Store) ![GitHub issues](https://img.shields.io/github/issues/Peng-YM/Sub-Store) ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/Peng-Ym/Sub-Store) ![Lines of code](https://img.shields.io/tokei/lines/github/Peng-YM/Sub-Store) ![Size](https://img.shields.io/github/languages/code-size/Peng-YM/Sub-Store)
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/PengYM) [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/PengYM)
Core functionalities: Core functionalities:
@ -19,6 +19,8 @@ Core functionalities:
1. Conversion among various formats. 1. Conversion among various formats.
2. Subscription formatting. 2. Subscription formatting.
3. Collect multiple subscriptions in one URL. 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 ## 1. Subscription Conversion
@ -29,17 +31,25 @@ Core functionalities:
- [x] SSD URI - [x] SSD URI
- [x] V2RayN URI - [x] V2RayN URI
- [x] QX (SS, SSR, VMess, Trojan, HTTP) - [x] QX (SS, SSR, VMess, Trojan, HTTP)
- [x] Loon (SS, SSR, VMess, Trojan, HTTP) - [x] Loon (SS, SSR, VMess, Trojan, HTTP, WireGuard, VLESS)
- [x] Surge (SS, VMess, Trojan, HTTP) - [x] Surge (SS, VMess, Trojan, HTTP, TUIC, Snell)
- [x] Stash & Clash (SS, SSR, VMess, Trojan, HTTP) - [x] ShadowRocket (SS, SSR, VMess, Trojan, HTTP, Snell, VLESS, WireGuard, Hysteria)
- [x] Clash.Meta (SS, SSR, VMess, Trojan, HTTP, Snell, VLESS, WireGuard, Hysteria)
- [x] Stash (SS, SSR, VMess, Trojan, HTTP, Snell, VLESS, WireGuard, Hysteria)
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, Snell)
### Supported Target Platforms ### Supported Target Platforms
- [x] QX - [x] QX
- [x] Loon - [x] Loon
- [x] Surge - [x] Surge
- [x] Stash & Clash - [x] Stash
- [x] Clash.Meta
- [x] Clash
- [x] ShadowRocket - [x] ShadowRocket
- [x] V2Ray
- [x] V2Ray URI
- [x] Plain JSON
## 2. Subscription Formatting ## 2. Subscription Formatting
@ -61,6 +71,7 @@ Core functionalities:
- [x] **Regex rename operator**: replace by regex in proxy names. - [x] **Regex rename operator**: replace by regex in proxy names.
- [x] **Regex delete operator**: delete by regex in proxy names. - [x] **Regex delete operator**: delete by regex in proxy names.
- [x] **Script operator**: modify proxy by script. - [x] **Script operator**: modify proxy by script.
- [x] **Resolve Domain Operator**: resolve the domain of nodes to an IP address.
### Development ### Development

View File

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

View File

@ -0,0 +1,117 @@
import { isPresent } from '@/core/proxy-utils/producers/utils';
export default function ClashMeta_Producer() {
const type = 'ALL';
const produce = (proxies) => {
return (
'proxies:\n' +
proxies
.filter((proxy) => {
if (
proxy.type === 'snell' &&
String(proxy.version) === '4'
) {
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;
}
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;
}
// 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') {
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;
}
} 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'];
}
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'].includes(proxy.type)) {
delete proxy.tls;
}
delete proxy['tls-fingerprint'];
return ' - ' + JSON.stringify(proxy) + '\n';
})
.join('')
);
};
return { type, produce };
}

View File

@ -1,5 +1,6 @@
import Surge_Producer from './surge'; import Surge_Producer from './surge';
import Clash_Producer from './clash'; import Clash_Producer from './clash';
import ClashMeta_Producer from './clashmeta';
import Stash_Producer from './stash'; import Stash_Producer from './stash';
import Loon_Producer from './loon'; import Loon_Producer from './loon';
import URI_Producer from './uri'; import URI_Producer from './uri';
@ -18,6 +19,7 @@ export default {
Surge: Surge_Producer(), Surge: Surge_Producer(),
Loon: Loon_Producer(), Loon: Loon_Producer(),
Clash: Clash_Producer(), Clash: Clash_Producer(),
ClashMeta: ClashMeta_Producer(),
URI: URI_Producer(), URI: URI_Producer(),
V2Ray: V2Ray_Producer(), V2Ray: V2Ray_Producer(),
JSON: JSON_Producer(), JSON: JSON_Producer(),

View File

@ -1,6 +1,6 @@
import { isPresent } from '@/core/proxy-utils/producers/utils'; import { isPresent } from '@/core/proxy-utils/producers/utils';
export default function Stash_Producer() { export default function ShadowRocket_Producer() {
const type = 'ALL'; const type = 'ALL';
const produce = (proxies) => { const produce = (proxies) => {
return ( return (

View File

@ -1,9 +1,11 @@
export function getPlatformFromHeaders(headers) { export function getPlatformFromHeaders(headers) {
const keys = Object.keys(headers); const keys = Object.keys(headers);
let UA = ''; let UA = '';
let ua = '';
for (let k of keys) { for (let k of keys) {
if (/USER-AGENT/i.test(k)) { if (/USER-AGENT/i.test(k)) {
UA = headers[k]; UA = headers[k];
ua = UA.toLowerCase();
break; break;
} }
} }
@ -17,6 +19,15 @@ export function getPlatformFromHeaders(headers) {
return 'ShadowRocket'; return 'ShadowRocket';
} else if (UA.indexOf('Stash') !== -1) { } else if (UA.indexOf('Stash') !== -1) {
return 'Stash'; return 'Stash';
} else if (
ua === 'meta' ||
(ua.indexOf('clash') !== -1 && ua.indexOf('meta') !== -1)
) {
return 'ClashMeta';
} else if (ua.indexOf('clash') !== -1) {
return 'Clash';
} else if (ua.indexOf('v2ray') !== -1) {
return 'V2Ray';
} else { } else {
return 'JSON'; return 'JSON';
} }