improve and support filter by country code

This commit is contained in:
parhelia512 2025-10-09 20:04:40 +08:00
parent a67b0fe5e2
commit 9e82c19451
13 changed files with 306 additions and 70 deletions

View File

@ -65,6 +65,7 @@ set(PROJECT_SOURCES
src/global/Utils.cpp src/global/Utils.cpp
src/global/HTTPRequestHelper.cpp src/global/HTTPRequestHelper.cpp
src/global/DeviceDetailsHelper.cpp src/global/DeviceDetailsHelper.cpp
src/global/CountryHelper.cpp
3rdparty/base64.cpp 3rdparty/base64.cpp
3rdparty/qrcodegen.cpp 3rdparty/qrcodegen.cpp

View File

@ -111,7 +111,6 @@ message SpeedTestResult {
optional string server_name = 6 [default = ""]; optional string server_name = 6 [default = ""];
optional string server_country = 7 [default = ""]; optional string server_country = 7 [default = ""];
optional bool cancelled = 8 [default = false]; optional bool cancelled = 8 [default = false];
optional string server_country_emoji = 9 [default = ""];
} }
message SpeedTestResponse { message SpeedTestResponse {

View File

@ -4,7 +4,6 @@ go 1.23.6
require ( require (
github.com/Mahdi-zarei/speedtest-go v1.7.12 github.com/Mahdi-zarei/speedtest-go v1.7.12
github.com/biter777/countries v1.7.5
github.com/chai2010/protorpc v0.0.0-00010101000000-000000000000 github.com/chai2010/protorpc v0.0.0-00010101000000-000000000000
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
github.com/gofrs/uuid/v5 v5.3.2 github.com/gofrs/uuid/v5 v5.3.2

View File

@ -12,8 +12,6 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc= github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=

View File

@ -1,25 +0,0 @@
package internal
import "github.com/biter777/countries"
var countryMap map[string]*countries.Country
func init() {
countryMap = make(map[string]*countries.Country)
allCountries := countries.AllInfo()
for _, country := range allCountries {
countryMap[country.Name] = country
}
}
func GetCountryByName(name string) *countries.Country {
return countryMap[name]
}
func GetCountryEmojiByName(name string) string {
country, ok := countryMap[name]
if !ok {
return ""
}
return country.Emoji
}

View File

@ -348,15 +348,14 @@ func (s *server) SpeedTest(in *gen.SpeedTestRequest, out *gen.SpeedTestResponse)
errStr = data.Error.Error() errStr = data.Error.Error()
} }
res = append(res, &gen.SpeedTestResult{ res = append(res, &gen.SpeedTestResult{
DlSpeed: To(data.DlSpeed), DlSpeed: To(data.DlSpeed),
UlSpeed: To(data.UlSpeed), UlSpeed: To(data.UlSpeed),
Latency: To(data.Latency), Latency: To(data.Latency),
OutboundTag: To(data.Tag), OutboundTag: To(data.Tag),
Error: To(errStr), Error: To(errStr),
ServerName: To(data.ServerName), ServerName: To(data.ServerName),
ServerCountry: To(data.ServerCountry), ServerCountry: To(data.ServerCountry),
ServerCountryEmoji: To(data.ServerCountryEmoji), Cancelled: To(data.Cancelled),
Cancelled: To(data.Cancelled),
}) })
} }
@ -371,15 +370,14 @@ func (s *server) QuerySpeedTest(in *gen.EmptyReq, out *gen.QuerySpeedTestRespons
errStr = res.Error.Error() errStr = res.Error.Error()
} }
out.Result = &gen.SpeedTestResult{ out.Result = &gen.SpeedTestResult{
DlSpeed: To(res.DlSpeed), DlSpeed: To(res.DlSpeed),
UlSpeed: To(res.UlSpeed), UlSpeed: To(res.UlSpeed),
Latency: To(res.Latency), Latency: To(res.Latency),
OutboundTag: To(res.Tag), OutboundTag: To(res.Tag),
Error: To(errStr), Error: To(errStr),
ServerName: To(res.ServerName), ServerName: To(res.ServerName),
ServerCountry: To(res.ServerCountry), ServerCountry: To(res.ServerCountry),
ServerCountryEmoji: To(res.ServerCountryEmoji), Cancelled: To(res.Cancelled),
Cancelled: To(res.Cancelled),
} }
out.IsRunning = To(isRunning) out.IsRunning = To(isRunning)
return nil return nil

View File

@ -53,15 +53,14 @@ func (u *URLTestReporter) Results() []*URLTestResult {
} }
type SpeedTestResult struct { type SpeedTestResult struct {
Tag string Tag string
DlSpeed string DlSpeed string
UlSpeed string UlSpeed string
Latency int32 Latency int32
ServerName string ServerName string
ServerCountry string ServerCountry string
ServerCountryEmoji string Error error
Error error Cancelled bool
Cancelled bool
} }
type SpeedTestResultQuerier struct { type SpeedTestResultQuerier struct {
@ -296,12 +295,6 @@ func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, n
} }
res.ServerName = srv[0].Name res.ServerName = srv[0].Name
res.ServerCountry = srv[0].Country res.ServerCountry = srv[0].Country
countryEmoji := internal.GetCountryEmojiByName(srv[0].Country)
if countryEmoji == "" {
fmt.Println("Failed to get country emoji for", srv[0].Name)
} else {
res.ServerCountryEmoji = countryEmoji
}
done := make(chan struct{}) done := make(chan struct{})

View File

@ -4,6 +4,9 @@
#include "include/stats/traffic/TrafficData.hpp" #include "include/stats/traffic/TrafficData.hpp"
#include "include/configs/proxy/AbstractBean.hpp" #include "include/configs/proxy/AbstractBean.hpp"
#include "include/configs/proxy/ExtraCore.h" #include "include/configs/proxy/ExtraCore.h"
#ifndef Q_MOC_RUN
#include "include/global/CountryHelper.hpp"
#endif
namespace Configs { namespace Configs {
class SocksHttpBean; class SocksHttpBean;
@ -41,7 +44,7 @@ namespace Configs {
int latency = 0; int latency = 0;
QString dl_speed; QString dl_speed;
QString ul_speed; QString ul_speed;
QString test_country_emoji; QString test_country;
std::shared_ptr<Configs::AbstractBean> bean; std::shared_ptr<Configs::AbstractBean> bean;
std::shared_ptr<Stats::TrafficData> traffic_data = std::make_shared<Stats::TrafficData>(""); std::shared_ptr<Stats::TrafficData> traffic_data = std::make_shared<Stats::TrafficData>("");

View File

@ -0,0 +1,255 @@
#pragma once
#include <QMap>
static const QMap<QString, QString> CountryMap = {
{ "Afghanistan", "AF" },
{ "Aland Islands", "AX" },
{ "Albania", "AL" },
{ "Algeria", "DZ" },
{ "American Samoa", "AS" },
{ "Andorra", "AD" },
{ "Angola", "AO" },
{ "Anguilla", "AI" },
{ "Antarctica", "AQ" },
{ "Antigua And Barbuda", "AG" },
{ "Argentina", "AR" },
{ "Armenia", "AM" },
{ "Netherlands Antilles", "AN" },
{ "Aruba", "AW" },
{ "Australia", "AU" },
{ "Austria", "AT" },
{ "Azerbaijan", "AZ" },
{ "Bahamas", "BS" },
{ "Bahrain", "BH" },
{ "Bangladesh", "BD" },
{ "Barbados", "BB" },
{ "Belarus", "BY" },
{ "Belgium", "BE" },
{ "Belize", "BZ" },
{ "Benin", "BJ" },
{ "Bermuda", "BM" },
{ "Bhutan", "BT" },
{ "Bolivia", "BO" },
{ "Bosnia And Herzegovina", "BA" },
{ "Botswana", "BW" },
{ "Bouvet Island", "BV" },
{ "Brazil", "BR" },
{ "British Indian Ocean Territory", "IO" },
{ "Brunei Darussalam", "BN" },
{ "Bulgaria", "BG" },
{ "Burkina Faso", "BF" },
{ "Burundi", "BI" },
{ "Cambodia", "KH" },
{ "Cameroon", "CM" },
{ "Canada", "CA" },
{ "Cape Verde", "CV" },
{ "Cayman Islands", "KY" },
{ "Central African Republic", "CF" },
{ "Chad", "TD" },
{ "Chile", "CL" },
{ "China", "CN" },
{ "Christmas Island", "CX" },
{ "Cocos (Keeling) Islands", "CC" },
{ "Colombia", "CO" },
{ "Comoros", "KM" },
{ "Congo", "CG" },
{ "Congo, Democratic Republic", "CD" },
{ "Cook Islands", "CK" },
{ "Costa Rica", "CR" },
{ "Cote D\"Ivoire", "CI" },
{ "Croatia", "HR" },
{ "Cuba", "CU" },
{ "Cyprus", "CY" },
{ "Czech Republic", "CZ" },
{ "Denmark", "DK" },
{ "Djibouti", "DJ" },
{ "Dominica", "DM" },
{ "Dominican Republic", "DO" },
{ "Ecuador", "EC" },
{ "Egypt", "EG" },
{ "El Salvador", "SV" },
{ "Equatorial Guinea", "GQ" },
{ "Eritrea", "ER" },
{ "Estonia", "EE" },
{ "Ethiopia", "ET" },
{ "Falkland Islands (Malvinas)", "FK" },
{ "Faroe Islands", "FO" },
{ "Fiji", "FJ" },
{ "Finland", "FI" },
{ "France", "FR" },
{ "French Guiana", "GF" },
{ "French Polynesia", "PF" },
{ "French Southern Territories", "TF" },
{ "Gabon", "GA" },
{ "Gambia", "GM" },
{ "Georgia", "GE" },
{ "Germany", "DE" },
{ "Ghana", "GH" },
{ "Gibraltar", "GI" },
{ "Greece", "GR" },
{ "Greenland", "GL" },
{ "Grenada", "GD" },
{ "Guadeloupe", "GP" },
{ "Guam", "GU" },
{ "Guatemala", "GT" },
{ "Guernsey", "GG" },
{ "Guinea", "GN" },
{ "Guinea-Bissau", "GW" },
{ "Guyana", "GY" },
{ "Haiti", "HT" },
{ "Heard Island & Mcdonald Islands", "HM" },
{ "Holy See (Vatican City State)", "VA" },
{ "Honduras", "HN" },
{ "Hong Kong", "HK" },
{ "Hungary", "HU" },
{ "Iceland", "IS" },
{ "India", "IN" },
{ "Indonesia", "ID" },
{ "Iran, Islamic Republic Of", "IR" },
{ "Iraq", "IQ" },
{ "Ireland", "IE" },
{ "Isle Of Man", "IM" },
{ "Israel", "IL" },
{ "Italy", "IT" },
{ "Jamaica", "JM" },
{ "Japan", "JP" },
{ "Jersey", "JE" },
{ "Jordan", "JO" },
{ "Kazakhstan", "KZ" },
{ "Kenya", "KE" },
{ "Kiribati", "KI" },
{ "Korea", "KR" },
{ "Kuwait", "KW" },
{ "Kyrgyzstan", "KG" },
{ "Lao People\"s Democratic Republic", "LA" },
{ "Latvia", "LV" },
{ "Lebanon", "LB" },
{ "Lesotho", "LS" },
{ "Liberia", "LR" },
{ "Libyan Arab Jamahiriya", "LY" },
{ "Liechtenstein", "LI" },
{ "Lithuania", "LT" },
{ "Luxembourg", "LU" },
{ "Macao", "MO" },
{ "Macedonia", "MK" },
{ "Madagascar", "MG" },
{ "Malawi", "MW" },
{ "Malaysia", "MY" },
{ "Maldives", "MV" },
{ "Mali", "ML" },
{ "Malta", "MT" },
{ "Marshall Islands", "MH" },
{ "Martinique", "MQ" },
{ "Mauritania", "MR" },
{ "Mauritius", "MU" },
{ "Mayotte", "YT" },
{ "Mexico", "MX" },
{ "Micronesia, Federated States Of", "FM" },
{ "Moldova", "MD" },
{ "Monaco", "MC" },
{ "Mongolia", "MN" },
{ "Montenegro", "ME" },
{ "Montserrat", "MS" },
{ "Morocco", "MA" },
{ "Mozambique", "MZ" },
{ "Myanmar", "MM" },
{ "Namibia", "NA" },
{ "Nauru", "NR" },
{ "Nepal", "NP" },
{ "Netherlands", "NL" },
{ "Netherlands Antilles", "AN" },
{ "New Caledonia", "NC" },
{ "New Zealand", "NZ" },
{ "Nicaragua", "NI" },
{ "Niger", "NE" },
{ "Nigeria", "NG" },
{ "Niue", "NU" },
{ "Norfolk Island", "NF" },
{ "Northern Mariana Islands", "MP" },
{ "Norway", "NO" },
{ "Oman", "OM" },
{ "Pakistan", "PK" },
{ "Palau", "PW" },
{ "Palestinian Territory, Occupied", "PS" },
{ "Panama", "PA" },
{ "Papua New Guinea", "PG" },
{ "Paraguay", "PY" },
{ "Peru", "PE" },
{ "Philippines", "PH" },
{ "Pitcairn", "PN" },
{ "Poland", "PL" },
{ "Portugal", "PT" },
{ "Puerto Rico", "PR" },
{ "Qatar", "QA" },
{ "Reunion", "RE" },
{ "Romania", "RO" },
{ "Russian Federation", "RU" },
{ "Rwanda", "RW" },
{ "Saint Barthelemy", "BL" },
{ "Saint Helena", "SH" },
{ "Saint Kitts And Nevis", "KN" },
{ "Saint Lucia", "LC" },
{ "Saint Martin", "MF" },
{ "Saint Pierre And Miquelon", "PM" },
{ "Saint Vincent And Grenadines", "VC" },
{ "Samoa", "WS" },
{ "San Marino", "SM" },
{ "Sao Tome And Principe", "ST" },
{ "Saudi Arabia", "SA" },
{ "Senegal", "SN" },
{ "Serbia", "RS" },
{ "Seychelles", "SC" },
{ "Sierra Leone", "SL" },
{ "Singapore", "SG" },
{ "Slovakia", "SK" },
{ "Slovenia", "SI" },
{ "Solomon Islands", "SB" },
{ "Somalia", "SO" },
{ "South Africa", "ZA" },
{ "South Georgia And Sandwich Isl.", "GS" },
{ "Spain", "ES" },
{ "Sri Lanka", "LK" },
{ "Sudan", "SD" },
{ "Suriname", "SR" },
{ "Svalbard And Jan Mayen", "SJ" },
{ "Swaziland", "SZ" },
{ "Sweden", "SE" },
{ "Switzerland", "CH" },
{ "Syrian Arab Republic", "SY" },
{ "Taiwan", "TW" },
{ "Tajikistan", "TJ" },
{ "Tanzania", "TZ" },
{ "Thailand", "TH" },
{ "Timor-Leste", "TL" },
{ "Togo", "TG" },
{ "Tokelau", "TK" },
{ "Tonga", "TO" },
{ "Trinidad And Tobago", "TT" },
{ "Tunisia", "TN" },
{ "Turkey", "TR" },
{ "Turkmenistan", "TM" },
{ "Turks And Caicos Islands", "TC" },
{ "Tuvalu", "TV" },
{ "Uganda", "UG" },
{ "Ukraine", "UA" },
{ "United Arab Emirates", "AE" },
{ "United Kingdom", "GB" },
{ "United States", "US" },
{ "United States Outlying Islands", "UM" },
{ "Uruguay", "UY" },
{ "Uzbekistan", "UZ" },
{ "Vanuatu", "VU" },
{ "Venezuela", "VE" },
{ "Viet Nam", "VN" },
{ "Virgin Islands, British", "VG" },
{ "Virgin Islands, U.S.", "VI" },
{ "Wallis And Futuna", "WF" },
{ "Western Sahara", "EH" },
{ "Yemen", "YE" },
{ "Zambia", "ZM" },
};
QString CountryNameToCode(const QString& countryName);
QString CountryCodeToFlag(const QString& countryCode);

View File

@ -12,7 +12,7 @@ namespace Configs
_add(new configItem("dl", &dl_speed, itemType::string)); _add(new configItem("dl", &dl_speed, itemType::string));
_add(new configItem("ul", &ul_speed, itemType::string)); _add(new configItem("ul", &ul_speed, itemType::string));
_add(new configItem("report", &full_test_report, itemType::string)); _add(new configItem("report", &full_test_report, itemType::string));
_add(new configItem("country_emoji", &test_country_emoji, itemType::string)); _add(new configItem("country", &test_country, itemType::string));
if (bean != nullptr) { if (bean != nullptr) {
this->bean = std::shared_ptr<Configs::AbstractBean>(bean); this->bean = std::shared_ptr<Configs::AbstractBean>(bean);
@ -26,7 +26,7 @@ namespace Configs
if (latency < 0) { if (latency < 0) {
result = "Unavailable"; result = "Unavailable";
} else if (latency > 0) { } else if (latency > 0) {
if (!test_country_emoji.isEmpty()) result += UNICODE_LRO + test_country_emoji + " "; if (!test_country.isEmpty()) result += UNICODE_LRO + CountryCodeToFlag(test_country) + " ";
result += QString("%1 ms").arg(latency); result += QString("%1 ms").arg(latency);
} }
if (!dl_speed.isEmpty() && dl_speed != "N/A") result += "" + dl_speed; if (!dl_speed.isEmpty() && dl_speed != "N/A") result += "" + dl_speed;

View File

@ -0,0 +1,13 @@
#include "include/global/CountryHelper.hpp"
QString CountryNameToCode(const QString& countryName) {
return CountryMap.value(countryName, "");
}
QString CountryCodeToFlag(const QString& countryCode) {
QVector<uint> ucs4 = countryCode.toUcs4();
for (uint& code : ucs4) {
code += 0x1F1A5;
}
return QString::fromUcs4(ucs4.data(), countryCode.length());
}

View File

@ -1158,7 +1158,7 @@ void MainWindow::UpdateDataView(bool force)
).arg(currentSptProfileName, ).arg(currentSptProfileName,
currentTestResult.dl_speed.value().c_str(), currentTestResult.dl_speed.value().c_str(),
currentTestResult.ul_speed.value().c_str(), currentTestResult.ul_speed.value().c_str(),
currentTestResult.server_country_emoji.value().c_str(), CountryCodeToFlag(CountryNameToCode(QString::fromStdString(currentTestResult.server_country.value()))),
currentTestResult.server_country.value().c_str(), currentTestResult.server_country.value().c_str(),
currentTestResult.server_name.value().c_str()); currentTestResult.server_name.value().c_str());
} }
@ -1325,7 +1325,9 @@ QList<std::shared_ptr<Configs::ProxyEntity>> MainWindow::filterProfilesList(cons
MW_show_log("Null profile, maybe data is corrupted"); MW_show_log("Null profile, maybe data is corrupted");
continue; continue;
} }
if (searchString.isEmpty() || profile->bean->name.contains(searchString, Qt::CaseInsensitive) || profile->bean->serverAddress.contains(searchString, Qt::CaseInsensitive)) res.append(profile); if (searchString.isEmpty() || profile->bean->name.contains(searchString, Qt::CaseInsensitive) || profile->bean->serverAddress.contains(searchString, Qt::CaseInsensitive)
|| (searchString.startsWith("CODE:") && searchString.mid(5) == profile->test_country))
res.append(profile);
} }
return res; return res;
} }

View File

@ -323,7 +323,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
if (!res.result.value().dl_speed.value().empty()) profile->dl_speed = QString::fromStdString(res.result.value().dl_speed.value()); if (!res.result.value().dl_speed.value().empty()) profile->dl_speed = QString::fromStdString(res.result.value().dl_speed.value());
if (!res.result.value().ul_speed.value().empty()) profile->ul_speed = QString::fromStdString(res.result.value().ul_speed.value()); if (!res.result.value().ul_speed.value().empty()) profile->ul_speed = QString::fromStdString(res.result.value().ul_speed.value());
if (profile->latency <= 0 && res.result.value().latency.value() > 0) profile->latency = res.result.value().latency.value(); if (profile->latency <= 0 && res.result.value().latency.value() > 0) profile->latency = res.result.value().latency.value();
if (!res.result->server_country_emoji.value().empty()) profile->test_country_emoji = QString::fromStdString(res.result.value().server_country_emoji.value()); if (!res.result->server_country.value().empty()) profile->test_country = CountryNameToCode(QString::fromStdString(res.result.value().server_country.value()));
refresh_proxy_list(profile->id); refresh_proxy_list(profile->id);
lastProxyListUpdate = QDateTime::currentDateTime(); lastProxyListUpdate = QDateTime::currentDateTime();
} }
@ -365,12 +365,12 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
ent->dl_speed = QString::fromStdString(res.dl_speed.value()); ent->dl_speed = QString::fromStdString(res.dl_speed.value());
ent->ul_speed = QString::fromStdString(res.ul_speed.value()); ent->ul_speed = QString::fromStdString(res.ul_speed.value());
if (ent->latency <= 0 && res.latency.value() > 0) ent->latency = res.latency.value(); if (ent->latency <= 0 && res.latency.value() > 0) ent->latency = res.latency.value();
if (!res.server_country_emoji.value().empty()) ent->test_country_emoji = QString::fromStdString(res.server_country_emoji.value()); if (!res.server_country.value().empty()) ent->test_country = CountryNameToCode(QString::fromStdString(res.server_country.value()));
} else { } else {
ent->dl_speed = "N/A"; ent->dl_speed = "N/A";
ent->ul_speed = "N/A"; ent->ul_speed = "N/A";
ent->latency = -1; ent->latency = -1;
ent->test_country_emoji = ""; ent->test_country = "";
MW_show_log(tr("[%1] speed test error: %2").arg(ent->bean->DisplayTypeAndName(), QString::fromStdString(res.error.value()))); MW_show_log(tr("[%1] speed test error: %2").arg(ent->bean->DisplayTypeAndName(), QString::fromStdString(res.error.value())));
} }
ent->Save(); ent->Save();