mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 20:44:38 +03:00
feat: add ducksoft link for VMess
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
#include "QUICBean.hpp"
|
||||
#include "db/ProxyEntity.hpp"
|
||||
#include "fmt/includes.h"
|
||||
|
||||
@@ -99,22 +98,69 @@ namespace NekoGui_fmt {
|
||||
}
|
||||
|
||||
QString VMessBean::ToShareLink() {
|
||||
QJsonObject N{
|
||||
{"v", "2"},
|
||||
{"ps", name},
|
||||
{"add", serverAddress},
|
||||
{"port", Int2String(serverPort)},
|
||||
{"id", uuid},
|
||||
{"aid", Int2String(aid)},
|
||||
{"net", stream->network},
|
||||
{"host", stream->host},
|
||||
{"path", stream->path},
|
||||
{"type", stream->header_type},
|
||||
{"scy", security},
|
||||
{"tls", stream->security == "tls" ? "tls" : ""},
|
||||
{"sni", stream->sni},
|
||||
};
|
||||
return "vmess://" + QJsonObject2QString(N, true).toUtf8().toBase64();
|
||||
if (NekoGui::dataStore->old_share_link_format) {
|
||||
// v2rayN format
|
||||
QJsonObject N{
|
||||
{"v", "2"},
|
||||
{"ps", name},
|
||||
{"add", serverAddress},
|
||||
{"port", Int2String(serverPort)},
|
||||
{"id", uuid},
|
||||
{"aid", Int2String(aid)},
|
||||
{"net", stream->network},
|
||||
{"host", stream->host},
|
||||
{"path", stream->path},
|
||||
{"type", stream->header_type},
|
||||
{"scy", security},
|
||||
{"tls", stream->security == "tls" ? "tls" : ""},
|
||||
{"sni", stream->sni},
|
||||
};
|
||||
return "vmess://" + QJsonObject2QString(N, true).toUtf8().toBase64();
|
||||
} else {
|
||||
// ducksoft format
|
||||
QUrl url;
|
||||
QUrlQuery query;
|
||||
url.setScheme("vmess");
|
||||
url.setUserName(uuid);
|
||||
url.setHost(serverAddress);
|
||||
url.setPort(serverPort);
|
||||
if (!name.isEmpty()) url.setFragment(name);
|
||||
|
||||
query.addQueryItem("encryption", security);
|
||||
|
||||
// security
|
||||
auto security = stream->security;
|
||||
if (security == "tls" && !stream->reality_pbk.trimmed().isEmpty()) security = "reality";
|
||||
query.addQueryItem("security", security);
|
||||
|
||||
if (!stream->sni.isEmpty()) query.addQueryItem("sni", stream->sni);
|
||||
if (stream->allow_insecure) query.addQueryItem("allowInsecure", "1");
|
||||
if (!stream->utlsFingerprint.isEmpty()) query.addQueryItem("fp", stream->utlsFingerprint);
|
||||
|
||||
if (security == "reality") {
|
||||
query.addQueryItem("pbk", stream->reality_pbk);
|
||||
if (!stream->reality_sid.isEmpty()) query.addQueryItem("sid", stream->reality_sid);
|
||||
if (!stream->reality_spx.isEmpty()) query.addQueryItem("spx", stream->reality_spx);
|
||||
}
|
||||
|
||||
// type
|
||||
query.addQueryItem("type", stream->network);
|
||||
|
||||
if (stream->network == "ws" || stream->network == "http") {
|
||||
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
|
||||
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
|
||||
} else if (stream->network == "grpc") {
|
||||
if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path);
|
||||
} else if (stream->network == "tcp") {
|
||||
if (stream->header_type == "http") {
|
||||
query.addQueryItem("headerType", "http");
|
||||
query.addQueryItem("host", stream->host);
|
||||
}
|
||||
}
|
||||
|
||||
url.setQuery(query);
|
||||
return url.toString(QUrl::FullyEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
QString NaiveBean::ToShareLink() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "QUICBean.hpp"
|
||||
#include "db/ProxyEntity.hpp"
|
||||
#include "fmt/includes.h"
|
||||
|
||||
@@ -153,9 +152,52 @@ namespace NekoGui_fmt {
|
||||
stream->security = objN["tls"].toString();
|
||||
// TODO quic & kcp
|
||||
return true;
|
||||
} else {
|
||||
// https://github.com/XTLS/Xray-core/discussions/716
|
||||
auto url = QUrl(link);
|
||||
if (!url.isValid()) return false;
|
||||
auto query = GetQuery(url);
|
||||
|
||||
name = url.fragment(QUrl::FullyDecoded);
|
||||
serverAddress = url.host();
|
||||
serverPort = url.port();
|
||||
uuid = url.userName();
|
||||
if (serverPort == -1) serverPort = 443;
|
||||
|
||||
aid = 0; // “此分享标准仅针对 VMess AEAD 和 VLESS。”
|
||||
security = GetQueryValue(query, "encryption", "auto");
|
||||
|
||||
// security
|
||||
stream->network = GetQueryValue(query, "type", "tcp");
|
||||
stream->security = GetQueryValue(query, "security", "tls").replace("reality", "tls");
|
||||
auto sni1 = GetQueryValue(query, "sni");
|
||||
auto sni2 = GetQueryValue(query, "peer");
|
||||
if (!sni1.isEmpty()) stream->sni = sni1;
|
||||
if (!sni2.isEmpty()) stream->sni = sni2;
|
||||
if (!query.queryItemValue("allowInsecure").isEmpty()) stream->allow_insecure = true;
|
||||
stream->reality_pbk = GetQueryValue(query, "pbk", "");
|
||||
stream->reality_sid = GetQueryValue(query, "sid", "");
|
||||
stream->reality_spx = GetQueryValue(query, "spx", "");
|
||||
stream->utlsFingerprint = GetQueryValue(query, "fp", "");
|
||||
|
||||
// type
|
||||
if (stream->network == "ws") {
|
||||
stream->path = GetQueryValue(query, "path", "");
|
||||
stream->host = GetQueryValue(query, "host", "");
|
||||
} else if (stream->network == "http") {
|
||||
stream->path = GetQueryValue(query, "path", "");
|
||||
stream->host = GetQueryValue(query, "host", "").replace("|", ",");
|
||||
} else if (stream->network == "grpc") {
|
||||
stream->path = GetQueryValue(query, "serviceName", "");
|
||||
} else if (stream->network == "tcp") {
|
||||
if (GetQueryValue(query, "headerType") == "http") {
|
||||
stream->header_type = "http";
|
||||
stream->host = GetQueryValue(query, "host", "");
|
||||
}
|
||||
}
|
||||
return !(uuid.isEmpty() || serverAddress.isEmpty());
|
||||
}
|
||||
|
||||
// Std Format
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace NekoGui {
|
||||
QString test_download_url = "http://cachefly.cachefly.net/10mb.test";
|
||||
int test_download_timeout = 30;
|
||||
int test_concurrent = 5;
|
||||
bool old_share_link_format = true;
|
||||
int traffic_loop_interval = 1000;
|
||||
bool connection_statistics = false;
|
||||
int current_group = 0; // group id
|
||||
|
||||
@@ -163,6 +163,14 @@
|
||||
<source>Advanced system proxy settings. Please select a format.</source>
|
||||
<translation>高级系统代理设置。请选择一种格式。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Old Share Link Format</source>
|
||||
<translation>旧分享链接格式</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share VMess Link with v2rayN Format</source>
|
||||
<translation>用 v2rayN 的格式分享 VMess 链接</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear servers before updating subscription</source>
|
||||
<translation>更新订阅前清除服务器</translation>
|
||||
|
||||
@@ -81,6 +81,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
D_LOAD_INT(test_download_timeout)
|
||||
D_LOAD_STRING(test_latency_url)
|
||||
D_LOAD_STRING(test_download_url)
|
||||
D_LOAD_BOOL(old_share_link_format)
|
||||
|
||||
connect(ui->custom_inbound_edit, &QPushButton::clicked, this, [=] {
|
||||
C_EDIT_JSON_ALLOW_EMPTY(custom_inbound)
|
||||
@@ -272,6 +273,7 @@ void DialogBasicSettings::accept() {
|
||||
D_SAVE_INT(test_download_timeout)
|
||||
D_SAVE_STRING(test_latency_url)
|
||||
D_SAVE_STRING(test_download_url)
|
||||
D_SAVE_BOOL(old_share_link_format)
|
||||
|
||||
// Style
|
||||
|
||||
|
||||
@@ -213,6 +213,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="old_share_link_format">
|
||||
<property name="toolTip">
|
||||
<string>Share VMess Link with v2rayN Format</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Old Share Link Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sys_proxy_format">
|
||||
<property name="text">
|
||||
|
||||
Reference in New Issue
Block a user