UNPKG

surgio

Version:

Generating rules for Surge, Clash, Quantumult like a PRO

240 lines 11.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getQuantumultXNodeNames = exports.getQuantumultXNodes = void 0; const logger_1 = require("@surgio/logger"); const lodash_1 = __importDefault(require("lodash")); const constant_1 = require("../constant"); const types_1 = require("../types"); const filters_1 = require("../filters"); const index_1 = require("./index"); const logger = (0, logger_1.createLogger)({ service: 'surgio:utils:quantumult' }); /** * @see https://github.com/crossutility/Quantumult-X/blob/master/sample.conf */ const getQuantumultXNodes = function (nodeList, filter) { const result = (0, filters_1.applyFilter)(nodeList, filter) .map(nodeListMapper) .filter((item) => item !== undefined) .map((item) => item[1]); return result.join('\n'); }; exports.getQuantumultXNodes = getQuantumultXNodes; const getQuantumultXNodeNames = function (nodeList, filter) { return (0, filters_1.applyFilter)(nodeList, filter) .map(nodeListMapper) .filter((item) => item !== undefined) .map((item) => item[0]) .join(', '); }; exports.getQuantumultXNodeNames = getQuantumultXNodeNames; function nodeListMapper(nodeConfig) { switch (nodeConfig.type) { case types_1.NodeTypeEnum.Vless: case types_1.NodeTypeEnum.Vmess: { if (!constant_1.QUANTUMULT_X_SUPPORTED_VMESS_NETWORK.includes(nodeConfig.network)) { logger.warn(`Quantumult X 不支持 ${nodeConfig.network} 的 Vmess 节点,节点 ${nodeConfig.nodeName} 会被省略`); return void 0; } const config = [ `${nodeConfig.hostname}:${nodeConfig.port}`, // method 为 auto 时 qx 会无法识别 nodeConfig.method === 'auto' ? `method=chacha20-poly1305` : `method=${nodeConfig.method}`, `password=${nodeConfig.uuid}`, ...(nodeConfig.udpRelay ? ['udp-relay=true'] : []), ...(nodeConfig.tfo ? ['fast-open=true'] : []), ...(nodeConfig.quantumultXConfig?.vmessAEAD ? ['aead=true'] : []), ]; switch (nodeConfig.network) { case 'ws': if ((nodeConfig.type === types_1.NodeTypeEnum.Vmess && nodeConfig.tls) || nodeConfig.type === types_1.NodeTypeEnum.Vless) { config.push(`obfs=wss`); // istanbul ignore next if (nodeConfig.skipCertVerify) { config.push('tls-verification=false'); } // istanbul ignore next if (nodeConfig.tls13) { config.push(`tls13=true`); } } else { config.push(`obfs=ws`); } if (nodeConfig.wsOpts) { const obfsHost = (0, index_1.getHeader)(nodeConfig.wsOpts.headers, 'Host'); config.push(`obfs-uri=${nodeConfig.wsOpts.path || '/'}`); if (obfsHost) { config.push(`obfs-host=${obfsHost}`); } } break; case 'tcp': if ((nodeConfig.type === types_1.NodeTypeEnum.Vmess && nodeConfig.tls) || nodeConfig.type === types_1.NodeTypeEnum.Vless) { config.push(`obfs=over-tls`); if (nodeConfig.skipCertVerify) { config.push('tls-verification=false'); } else { config.push('tls-verification=true'); } // istanbul ignore next if (nodeConfig.tls13) { config.push(`tls13=true`); } } break; case 'http': if (nodeConfig.httpOpts) { const obfsHost = (0, index_1.getHeader)(nodeConfig.httpOpts.headers, 'Host'); config.push('obfs=http', `obfs-uri=${nodeConfig.httpOpts.path || '/'}`); if (obfsHost) { config.push(`obfs-host=${obfsHost}`); } } } if (typeof nodeConfig.testUrl === 'string') { config.push(`server_check_url=${nodeConfig['testUrl']}`); } config.push(`tag=${nodeConfig.nodeName}`); // istanbul ignore next if (nodeConfig.wsOpts?.headers && Object.keys(nodeConfig.wsOpts.headers).length > 1) { logger.warn(`Quantumult X 不支持自定义额外的 Header 字段,节点 ${nodeConfig.nodeName} 可能不可用`); } return [nodeConfig.nodeName, `${nodeConfig.type}=${config.join(', ')}`]; } case types_1.NodeTypeEnum.Shadowsocks: { const config = [ `${nodeConfig.hostname}:${nodeConfig.port}`, ...(0, index_1.pickAndFormatStringList)(nodeConfig, ['method', 'password']), ...(nodeConfig.obfs && ['http', 'tls'].includes(nodeConfig.obfs) ? [`obfs=${nodeConfig.obfs}`, `obfs-host=${nodeConfig.obfsHost}`] : []), ...(nodeConfig.obfs && ['ws', 'wss'].includes(nodeConfig.obfs) ? [ `obfs=${nodeConfig.obfs}`, `obfs-host=${nodeConfig.obfsHost || nodeConfig.hostname}`, `obfs-uri=${nodeConfig.obfsUri || '/'}`, ] : []), ...(nodeConfig.udpRelay ? [`udp-relay=true`] : []), ...(nodeConfig.tfo ? [`fast-open=${nodeConfig.tfo}`] : []), ]; if (nodeConfig.obfs === 'wss') { if (nodeConfig.skipCertVerify) { config.push('tls-verification=false'); } else { config.push('tls-verification=true'); } if (nodeConfig.tls13) { config.push('tls13=true'); } } // istanbul ignore next if (nodeConfig.wsHeaders && Object.keys(lodash_1.default.omit(nodeConfig.wsHeaders, ['host'])).length > 0) { logger.warn(`Quantumult X 不支持自定义额外的 Header 字段,节点 ${nodeConfig.nodeName} 可能不可用`); } if (typeof nodeConfig.testUrl === 'string') { config.push(`server_check_url=${nodeConfig['testUrl']}`); } config.push(`tag=${nodeConfig.nodeName}`); return [nodeConfig.nodeName, `shadowsocks=${config.join(', ')}`]; } case types_1.NodeTypeEnum.Shadowsocksr: { const config = [ `${nodeConfig.hostname}:${nodeConfig.port}`, ...(0, index_1.pickAndFormatStringList)(nodeConfig, ['method', 'password']), `ssr-protocol=${nodeConfig.protocol}`, `ssr-protocol-param=${nodeConfig.protoparam}`, `obfs=${nodeConfig.obfs}`, `obfs-host=${nodeConfig.obfsparam}`, ...(nodeConfig.udpRelay ? [`udp-relay=true`] : []), ...(nodeConfig.tfo ? [`fast-open=${nodeConfig.tfo}`] : []), ...(typeof nodeConfig.testUrl === 'string' ? [`server_check_url=${nodeConfig.testUrl}`] : []), `tag=${nodeConfig.nodeName}`, ].join(', '); return [nodeConfig.nodeName, `shadowsocks=${config}`]; } case types_1.NodeTypeEnum.HTTP: case types_1.NodeTypeEnum.HTTPS: { const config = [ `${nodeConfig.hostname}:${nodeConfig.port}`, ...(0, index_1.pickAndFormatStringList)(nodeConfig, ['username', 'password']), ...(nodeConfig.tfo ? [`fast-open=${nodeConfig.tfo}`] : []), ]; if (nodeConfig.type === types_1.NodeTypeEnum.HTTPS) { config.push('over-tls=true', `tls-verification=${nodeConfig.skipCertVerify !== true}`, ...(nodeConfig.tls13 ? [`tls13=${nodeConfig.tls13}`] : [])); } if (typeof nodeConfig.testUrl === 'string') { config.push(`server_check_url=${nodeConfig['testUrl']}`); } config.push(`tag=${nodeConfig.nodeName}`); return [nodeConfig.nodeName, `http=${config.join(', ')}`]; } case types_1.NodeTypeEnum.Trojan: { const config = [ `${nodeConfig.hostname}:${nodeConfig.port}`, ...(0, index_1.pickAndFormatStringList)(nodeConfig, ['password']), `tls-verification=${nodeConfig.skipCertVerify !== true}`, ...(nodeConfig.tfo ? [`fast-open=${nodeConfig.tfo}`] : []), ...(nodeConfig.udpRelay ? [`udp-relay=true`] : []), ...(nodeConfig.tls13 ? [`tls13=${nodeConfig.tls13}`] : []), ]; if (nodeConfig.network === 'ws') { /** * The obfs field is only supported with websocket over tls for trojan. When using websocket over * tls you should not set over-tls and tls-host options anymore, instead set obfs=wss and * obfs-host options. */ config.push('obfs=wss'); if (nodeConfig.wsPath) { config.push(`obfs-uri=${nodeConfig.wsPath}`); } const hostHeader = nodeConfig?.wsHeaders?.host; const sni = nodeConfig.sni; if (sni) { config.push(`obfs-host=${sni}`); } else if (hostHeader) { config.push(`obfs-host=${hostHeader}`); } if (sni && hostHeader) { logger.warn(`Quantumult X 不支持同时定义 sni 和 wsHeaders.host,配置以 sni 为准,节点 ${nodeConfig.nodeName} 可能不可用`); } if (nodeConfig?.wsHeaders) { // istanbul ignore next if (Object.keys(lodash_1.default.omit(nodeConfig.wsHeaders, ['host'])).length > 0) { logger.warn(`Quantumult X 不支持自定义额外的 Header 字段,节点 ${nodeConfig.nodeName} 可能不可用`); } } } else { config.push('over-tls=true'); if (nodeConfig.sni) { config.push(`tls-host=${nodeConfig.sni}`); } } if (typeof nodeConfig.testUrl === 'string') { config.push(`server_check_url=${nodeConfig['testUrl']}`); } config.push(`tag=${nodeConfig.nodeName}`); return [nodeConfig.nodeName, `trojan=${config.join(', ')}`]; } // istanbul ignore next default: logger.warn(`不支持为 QuantumultX 生成 ${nodeConfig.type} 的节点,节点 ${nodeConfig.nodeName} 会被省略`); return void 0; } } //# sourceMappingURL=quantumult.js.map