mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 12:05:07 +00:00
fix(proton): update query parsing and server selection logic for improved accuracy
This commit is contained in:
@@ -25,13 +25,14 @@ class ProtonVPN(Proxy):
|
||||
|
||||
Authenticates through TV login (a self-sustaining login session cached at <cache>/vpn/protonvpn.json, the
|
||||
only refreshable kind) or an exported account.proton.me cookie session (AUTH-<UID>, access only,
|
||||
re-export on expiry). Resolves a server by country/city, mints short-lived proxy credentials, and
|
||||
re-export on expiry). Resolves a server by country/city/node, mints short-lived proxy credentials, and
|
||||
returns an authenticated HTTPS proxy URL (https://user:pass@server:4443).
|
||||
|
||||
Query format (after the proxy provider prefix, for example "protonvpn:us"):
|
||||
us random server in the country (paid preferred over free)
|
||||
us12 Proton server #12 (the number shown in the connection log)
|
||||
us:ny city, NordVPN-style
|
||||
us random server in the country (paid preferred over free)
|
||||
us12 / us-12 / us#12 Proton server #12 (the number shown in the connection log)
|
||||
us:ny city, NordVPN-style
|
||||
node-pl-24 specific physical server node by domain
|
||||
|
||||
Free Proton accounts work, limited to the free-tier exit countries.
|
||||
"""
|
||||
@@ -122,19 +123,25 @@ class ProtonVPN(Proxy):
|
||||
query = self.server_map.get(query, query)
|
||||
country, city, server_num = self.parse_query(query)
|
||||
|
||||
max_tier = self.get_max_tier()
|
||||
eligible = [
|
||||
s
|
||||
for s in self.get_logicals()
|
||||
if s.get("Status") == 1
|
||||
and int(s.get("Tier") or 0) <= max_tier
|
||||
and not int(s.get("Features") or 0) & self.EXCLUDE_FEATURES
|
||||
]
|
||||
|
||||
pool = []
|
||||
if country:
|
||||
max_tier = self.get_max_tier() # only servers the account can actually connect to
|
||||
pool = [
|
||||
s
|
||||
for s in self.get_logicals()
|
||||
if str(s.get("ExitCountry") or "").lower() == country
|
||||
and s.get("Status") == 1
|
||||
and int(s.get("Tier") or 0) <= max_tier
|
||||
and not int(s.get("Features") or 0) & self.EXCLUDE_FEATURES
|
||||
]
|
||||
pool = [s for s in eligible if str(s.get("ExitCountry") or "").lower() == country]
|
||||
if city:
|
||||
pool = self.match_city(pool, city)
|
||||
else:
|
||||
domain_query = query.removesuffix(".protonvpn.net")
|
||||
pool = [
|
||||
s for s in eligible if str(s.get("Domain") or "").lower().removesuffix(".protonvpn.net") == domain_query
|
||||
]
|
||||
|
||||
if not pool:
|
||||
log.warning("Proton: no server matched query '%s'", query)
|
||||
@@ -142,13 +149,13 @@ class ProtonVPN(Proxy):
|
||||
return self.select(pool, server_num)
|
||||
|
||||
def parse_query(self, query: str) -> tuple[Optional[str], Optional[str], Optional[int]]:
|
||||
"""Parse "us", "us12"/"us-12" (server #12), and "us:ny" (city)."""
|
||||
"""Parse "us", "us12"/"us-12"/"us#12" (server #12), and "us:ny" (city)."""
|
||||
city = None
|
||||
if ":" in query:
|
||||
query, city = query.split(":", 1)
|
||||
city = city.strip() or None
|
||||
|
||||
match = re.match(r"^([a-z]{2})-?(\d+)?$", query.strip())
|
||||
match = re.match(r"^([a-z]{2})[-#]?(\d+)?$", query.strip())
|
||||
if not match:
|
||||
return None, None, None
|
||||
country = self.COUNTRY_ALIASES.get(match.group(1), match.group(1))
|
||||
|
||||
Reference in New Issue
Block a user