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/HTTPRequestHelper.cpp
src/global/DeviceDetailsHelper.cpp
src/global/CountryHelper.cpp
3rdparty/base64.cpp
3rdparty/qrcodegen.cpp

View File

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

View File

@ -4,7 +4,6 @@ go 1.23.6
require (
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/dustin/go-humanize v1.0.1
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/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
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/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
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()
}
res = append(res, &gen.SpeedTestResult{
DlSpeed: To(data.DlSpeed),
UlSpeed: To(data.UlSpeed),
Latency: To(data.Latency),
OutboundTag: To(data.Tag),
Error: To(errStr),
ServerName: To(data.ServerName),
ServerCountry: To(data.ServerCountry),
ServerCountryEmoji: To(data.ServerCountryEmoji),
Cancelled: To(data.Cancelled),
DlSpeed: To(data.DlSpeed),
UlSpeed: To(data.UlSpeed),
Latency: To(data.Latency),
OutboundTag: To(data.Tag),
Error: To(errStr),
ServerName: To(data.ServerName),
ServerCountry: To(data.ServerCountry),
Cancelled: To(data.Cancelled),
})
}
@ -371,15 +370,14 @@ func (s *server) QuerySpeedTest(in *gen.EmptyReq, out *gen.QuerySpeedTestRespons
errStr = res.Error.Error()
}
out.Result = &gen.SpeedTestResult{
DlSpeed: To(res.DlSpeed),
UlSpeed: To(res.UlSpeed),
Latency: To(res.Latency),
OutboundTag: To(res.Tag),
Error: To(errStr),
ServerName: To(res.ServerName),
ServerCountry: To(res.ServerCountry),
ServerCountryEmoji: To(res.ServerCountryEmoji),
Cancelled: To(res.Cancelled),
DlSpeed: To(res.DlSpeed),
UlSpeed: To(res.UlSpeed),
Latency: To(res.Latency),
OutboundTag: To(res.Tag),
Error: To(errStr),
ServerName: To(res.ServerName),
ServerCountry: To(res.ServerCountry),
Cancelled: To(res.Cancelled),
}
out.IsRunning = To(isRunning)
return nil

View File

@ -53,15 +53,14 @@ func (u *URLTestReporter) Results() []*URLTestResult {
}
type SpeedTestResult struct {
Tag string
DlSpeed string
UlSpeed string
Latency int32
ServerName string
ServerCountry string
ServerCountryEmoji string
Error error
Cancelled bool
Tag string
DlSpeed string
UlSpeed string
Latency int32
ServerName string
ServerCountry string
Error error
Cancelled bool
}
type SpeedTestResultQuerier struct {
@ -296,12 +295,6 @@ func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, n
}
res.ServerName = srv[0].Name
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{})

View File

@ -4,6 +4,9 @@
#include "include/stats/traffic/TrafficData.hpp"
#include "include/configs/proxy/AbstractBean.hpp"
#include "include/configs/proxy/ExtraCore.h"
#ifndef Q_MOC_RUN
#include "include/global/CountryHelper.hpp"
#endif
namespace Configs {
class SocksHttpBean;
@ -41,7 +44,7 @@ namespace Configs {
int latency = 0;
QString dl_speed;
QString ul_speed;
QString test_country_emoji;
QString test_country;
std::shared_ptr<Configs::AbstractBean> bean;
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("ul", &ul_speed, 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) {
this->bean = std::shared_ptr<Configs::AbstractBean>(bean);
@ -26,7 +26,7 @@ namespace Configs
if (latency < 0) {
result = "Unavailable";
} 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);
}
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,
currentTestResult.dl_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_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");
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;
}

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().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 (!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);
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->ul_speed = QString::fromStdString(res.ul_speed.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 {
ent->dl_speed = "N/A";
ent->ul_speed = "N/A";
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())));
}
ent->Save();