Skip to content

Instantly share code, notes, and snippets.

@st1vms
Last active November 6, 2023 14:09
Show Gist options
  • Save st1vms/52f3a974c7a60c06ea0b4a726974dd04 to your computer and use it in GitHub Desktop.
Save st1vms/52f3a974c7a60c06ea0b4a726974dd04 to your computer and use it in GitHub Desktop.
Python script to rate ProtonVPN FreeTier domains by load factor.
"""Auto retrieve ProtonVPN free-tiers load ratings"""
from requests import get as http_get
# Taken from https://account.protonvpn.com/downloads
# Open dev-tools and look for GET requests to /api/vpn/logicals endpoint
# Copy the entire cookie header value into this string
__COOKIE_STRING = ""
# Also copy the x-pm-uid header value from that same GET request into this string
__X_PM_UID_STRING = ""
# Also copy your browser User Agent into this string
__USER_AGENT = ""
def __get_json_stats() -> dict | None:
url = "https://account.protonvpn.com/api/vpn/logicals"
headers = {
"Host": "account.protonvpn.com",
"User-Agent": __USER_AGENT,
"Accept": "application/vnd.protonmail.v1+json",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://account.protonvpn.com/downloads",
"x-pm-appversion": "web-vpn-settings@5.0.58.2",
"DNT": "1",
"Connection": "keep-alive",
"x-pm-uid": __X_PM_UID_STRING,
"Cookie": __COOKIE_STRING,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"TE": "trailers",
}
res = http_get(url, headers=headers, timeout=5)
if res.status_code == 200:
return res.json()
return None
def get_free_tier_ratings() -> list[tuple[str, float]] | None:
"""Retrieves list of tuples, containing ordered domain names with their load number"""
stats = __get_json_stats()
if not stats or not "LogicalServers" in stats:
print("Error retrieving stats...")
return None
tiers = __free_tiers()
ret = []
for entry in stats["LogicalServers"]:
if not "Domain" in entry or not "Load" in entry:
continue
domain: str = entry["Domain"]
load = entry["Load"]
if domain.split(".")[0] in tiers:
ret.append((domain, load))
return sorted(ret, key=lambda x: x[1])
def __main() -> None:
"""Main"""
i = 0
while not i:
try:
i = int(input("\nHow many results you want to get?\n>>").strip())
if i <= 0:
i = 0
print("\nMust be a positive value!")
continue
except ValueError:
i = 0
continue
ratings = get_free_tier_ratings()
for rate in ratings[:i]:
print(f"{rate[0]} -> {rate[1]}%")
def __free_tiers() -> set:
"""Embedded ProtonVPN free tiers domain names."""
return {
"node-jp-26",
"node-nl-141",
"node-nl-93",
"node-nl-182",
"node-us-198",
"node-nl-161",
"node-us-91",
"node-us-61",
"node-nl-139",
"node-us-145",
"node-nl-172",
"node-us-175",
"node-us-162",
"node-nl-185",
"node-nl-06",
"node-nl-113",
"node-nl-83",
"node-ro-07",
"node-nl-02",
"node-nl-191",
"node-nl-132",
"node-nl-187",
"node-nl-162",
"node-nl-81",
"node-jp-28",
"node-nl-134",
"node-us-128",
"node-nl-199",
"node-nl-124",
"node-nl-123",
"node-nl-163",
"node-nl-189",
"node-nl-98",
"node-us-151",
"node-nl-110",
"node-nl-128",
"node-jp-13",
"node-nl-203",
"node-nl-97",
"node-us-159",
"node-us-141",
"node-us-72",
"node-nl-180",
"node-us-155",
"node-nl-158",
"node-nl-200",
"node-nl-156",
"node-us-191",
"node-jp-19",
"node-nl-72",
"node-jp-24",
"node-nl-196",
"node-us-147",
"node-nl-149",
"node-nl-140",
"node-pl-12",
"node-nl-71",
"node-nl-148",
"node-nl-05",
"node-us-137",
"node-ro-04",
"node-nl-94",
"node-nl-95",
"node-jp-15",
"node-nl-130",
"node-nl-178",
"node-jp-18",
"node-nl-68",
"node-nl-105",
"node-jp-16",
"node-ro-03",
"node-nl-74",
"node-us-54",
"node-nl-57",
"node-us-146",
"node-nl-76",
"node-us-184",
"node-ro-06",
"node-nl-159",
"node-nl-181",
"node-nl-193",
"node-nl-111",
"node-jp-27",
"node-nl-129",
"node-us-154",
"node-nl-117",
"node-nl-150",
"node-nl-109",
"node-nl-108",
"node-nl-142",
"node-nl-133",
"node-us-136",
"node-us-193",
"node-nl-170",
"node-us-148",
"node-nl-173",
"node-nl-92",
"node-jp-22",
"node-us-70",
"node-nl-79",
"node-nl-186",
"node-us-163",
"node-nl-101",
"node-us-73",
"node-us-131",
"node-us-55",
"node-nl-197",
"node-nl-96",
"node-us-133",
"node-nl-126",
"node-jp-21",
"node-us-47",
"node-nl-184",
"node-us-62",
"node-nl-152",
"node-nl-147",
"node-nl-188",
"node-us-45",
"node-us-156",
"node-nl-106",
"node-us-196",
"node-us-76",
"node-us-60",
"node-us-71",
"node-nl-70",
"node-nl-179",
"node-us-152",
"node-nl-201",
"node-nl-102",
"node-nl-127",
"node-us-149",
"node-jp-25",
"node-nl-164",
"node-nl-198",
"node-nl-175",
"node-us-142",
"node-nl-137",
"node-us-170",
"node-nl-69",
"node-us-132",
"node-nl-114",
"node-us-180",
"node-us-150",
"node-nl-90",
"node-nl-125",
"node-ro-05",
"node-nl-154",
"node-jp-20",
"node-nl-75",
"node-us-56",
"node-pl-13",
"node-nl-77",
"node-nl-115",
"node-nl-82",
"node-nl-169",
"node-nl-195",
"node-nl-176",
"node-pl-11",
"node-nl-78",
"node-nl-177",
"node-nl-116",
"node-nl-91",
"node-nl-103",
"node-us-78",
"node-pl-10",
"node-nl-153",
"node-nl-138",
"node-us-81",
"node-nl-118",
"node-nl-183",
"node-us-138",
"node-us-140",
"node-jp-23",
"node-us-139",
"node-jp-11",
"node-nl-190",
"node-nl-160",
"node-nl-04",
"node-us-153",
"node-nl-155",
"node-nl-104",
"node-nl-157",
"node-nl-171",
"node-pl-09",
"node-us-157",
"node-nl-73",
"node-nl-192",
"node-nl-03",
"node-nl-131",
}
if __name__ == "__main__":
__main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment