Last active
June 7, 2022 16:02
-
-
Save dadatuputi/ae961b3573da664c0bfcfa8d3fe35d68 to your computer and use it in GitHub Desktop.
Uses Selenium to get (or refresh) your Windscribe ephemeral port, absent a proper API to do it. It does not require a gui, or even x to be installed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# Step 1: install firefox: $ sudo apt install firefox | |
# Step 2: install selenium: $ pip3 install selenium | |
# Step 3: install geckodriver: https://github.com/mozilla/geckodriver/releases | |
from selenium.webdriver.firefox.options import Options as FirefoxOptions | |
from selenium.common.exceptions import NoSuchElementException | |
from selenium import webdriver | |
from selenium.webdriver.support.ui import WebDriverWait | |
from selenium.webdriver.support import expected_conditions as EC | |
from selenium.webdriver.common.by import By | |
import time | |
import logging | |
import argparse | |
from getpass import getpass | |
options = FirefoxOptions() | |
options.add_argument("--headless") | |
driver = webdriver.Firefox(options=options) | |
url_login = "https://windscribe.com/login" | |
url_account = "https://windscribe.com/myaccount" | |
url_logout = "https://windscribe.com/myaccount/logout" | |
def login(username, password): | |
# Logs in and returns webdriver used in get_port | |
driver.get(url_login) | |
logging.debug('Navigated to {}'.format(url_login)) | |
# Wait for CSRF | |
WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//input[@id="csrf_time"][not(@value="")]'))) | |
WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//input[@id="csrf_token"][not(@value="")]'))) | |
driver.find_element(By.ID, "username").send_keys(username) | |
driver.find_element(By.ID, "pass").send_keys(password) | |
driver.find_element(By.ID, "login_button").click() | |
logging.debug('Entered credentials and clicked login button') | |
# Check that login succeeded: | |
# If we are still at the login url, check for error message | |
if driver.current_url == url_login: | |
_error = "Login failure: " | |
try: | |
_error += driver.find_element(By.XPATH, "//div[@class='content_message error'][not(@hidden)]").text | |
except NoSuchElementException: | |
_error += "No Error, but login failed. Source: \n\n" | |
_error += driver.page_source | |
raise Exception(_error) | |
WebDriverWait(driver, 5).until(EC.url_matches(url_account)) | |
logging.info('Successfully logged in as user {}'.format(username)) | |
return driver | |
def get_port(driver, refresh=False): | |
driver.get(url_account) | |
logging.debug('Navigated to {}'.format(url_account)) | |
# Navigate to port forwarding tab | |
_port_forwarding_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, '//a[@id="menu-ports"][@onclick][string-length(@onclick) > 0]'))) | |
_port_forwarding_button.click() | |
logging.debug('Clicked Port Forwarding tab') | |
# Navigate to ephemeral port forwarding tab | |
_ephemeral_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, '//li[@id="pf-eph-btn"][@onclick][string-length(@onclick) > 0]'))) | |
time.sleep(1) # This is a dirty hack, but there is some js going on in the background I need to wait for | |
_ephemeral_button.click() | |
logging.debug('Clicked Ephemeral tab') | |
# If refresh, delete existing port | |
if refresh: | |
try: | |
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, '//button[normalize-space()="Delete Port"]'))).click() | |
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, '//button[normalize-space()="Request Matching Port"]'))) | |
logging.info("Deleted existing port") | |
except NoSuchElementException: | |
# No port to delete | |
pass | |
# Generate matching port if possible | |
try: | |
# Wait until request matching port button appears to continue: | |
driver.find_element(By.XPATH, '//button[normalize-space()="Request Matching Port"]').click() | |
logging.debug('Requesting a matching port') | |
except NoSuchElementException: | |
# We already have a port | |
pass | |
# Get the port and return | |
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH, '//button[normalize-space()="Delete Port"]'))) | |
_port = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//div[@id="epf-port-info"]/span[string-length(text()) > 0]'))).text | |
_port = int(_port) | |
assert 1024 < _port < 65535 | |
logging.info('Received matching port of {}'.format(_port)) | |
_time = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//span[@id="epf-countdown"][string-length(text()) > 0]'))).text | |
logging.info('Port will expire in {}'.format(_time)) | |
return _port, _time | |
def cleanup(driver): | |
driver.get(url_logout) | |
driver.quit() | |
def main(username, password, refresh): | |
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) | |
try: | |
driver = login(username, password) | |
port, time = get_port(driver, refresh) | |
print(port) | |
except Exception as e: | |
logging.error(e) | |
finally: | |
if driver: | |
cleanup(driver) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Get or refresh matching ephemeral port from Windscribe") | |
parser.add_argument('-u', '--user', required=True, type=str) | |
parser.add_argument('-p', '--password', required=False, type=str) | |
parser.add_argument('-r', '--refresh', required=False, action="store_true", help="Force a refresh of the port if possible") | |
args = parser.parse_args() | |
if not args.password: | |
args.password = getpass() | |
main(args.user, args.password, args.refresh) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment