surgio
Version:
Generating rules for Surge, Clash, Quantumult like a PRO
240 lines • 11.7 kB
JavaScript
;
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