Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protonvpn wireguard config downloader #47

Merged
merged 5 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/protonvpn-wireguard-config-downloader-QA.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: ProtonVPN Wireguard Config Downloader QA

on:
pull_request:
push:
paths:
- 'protonvpn-wireguard-config-downloader/**'
branches:
- main

jobs:

check-qa:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version-file: protonvpn-wireguard-config-downloader/pyproject.toml
architecture: x64

- name: Install dependencies (and project)
working-directory: protonvpn-wireguard-config-downloader
run: |
pip install -U pip
pip install -e .[lint,scripts,test,check]

- name: Check black formatting
working-directory: protonvpn-wireguard-config-downloader
run: inv lint-black

- name: Check ruff
working-directory: protonvpn-wireguard-config-downloader
run: inv lint-ruff

- name: Check pyright
working-directory: protonvpn-wireguard-config-downloader
run: inv check-pyright
16 changes: 16 additions & 0 deletions protonvpn-wireguard-config-downloader/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.11-slim-bookworm
LABEL org.opencontainers.image.source=https://github.com/kiwix/mirrors-qa

# We need gnupg2 for python-gnupg used in Proton libraries to work properly.
RUN apt-get update && apt-get install -y gnupg2
elfkuzco marked this conversation as resolved.
Show resolved Hide resolved

COPY src /src/src

COPY pyproject.toml README.md /src/

RUN pip install --no-cache-dir /src \
&& rm -rf /src

RUN mkdir /data

CMD ["protonvpn-wireguard-configs", "--help"]
23 changes: 23 additions & 0 deletions protonvpn-wireguard-config-downloader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ProtonVPN Wireguard Configuration Downloader

A tool to automatically download wireguard configuration files for all available VPN servers from ProtonVPN.

**NOTE**
This script is intended to be used in Linux environments only.

## Environment Variables

- `USERNAME`: username for connnecting to ProtonVPN account
- `PASSWORD`
- `WORKDIR`: location to store configuration files. (default: /data)
- `WIREGUARD_PORT`: Port of the wireguard configuration files (default: 51820).This allows to choose the wireguard port for the configuration files rather than leaving it to the ProtonVPN library which often defaults to the first available port in the session object.

## Usage
- Build the image
```sh
docker build -t protonvpn-wireguard-config-downloader .
```
- Download the configuration files
```sh
docker run --rm -e USERNAME=abcd@efg -e PASSWORD=pa55word -v ./proton:/data protonvpn-wireguard-config-downloader protonvpn-wireguard-configs
```
238 changes: 238 additions & 0 deletions protonvpn-wireguard-config-downloader/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
[build-system]
requires = ["hatchling", "hatch-openzim"]
build-backend = "hatchling.build"

[project]
name = "protonvpn_wireguard_config_downloader"
requires-python = ">=3.11,<3.13"
description = "ProtonVPN Wireguard Configuration Files Downloader"
readme = "README.md"
authors = [
{ name = "Kiwix", email = "[email protected]" },
]
keywords = ["protonvpn", "wireguard"]
dependencies = [
"proton-core @ https://github.com/ProtonVPN/python-proton-core/archive/refs/tags/v0.2.0.zip",
"proton-vpn-logger @ https://github.com/ProtonVPN/python-proton-vpn-logger/archive/refs/tags/v0.2.1.zip",
"proton-vpn-api-core @ https://github.com/ProtonVPN/python-proton-vpn-api-core/archive/refs/tags/v0.32.2.zip",
"distro==1.9.0"
]
license = {text = "GPL-3.0-or-later"}
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
elfkuzco marked this conversation as resolved.
Show resolved Hide resolved
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
]

dynamic = ["version"]

[project.urls]
Homepage = "https://github.com/kiwix/mirrors-qa/protonvpn-wireguard-config-downloader"

[project.optional-dependencies]
scripts = [
"invoke==2.2.0",
]
lint = [
"black==24.4.2",
"ruff==0.5.1",
]
check = [
"pyright==1.1.370",
]
test = [
"pytest==8.2.2",
"coverage==7.5.4",
]
dev = [
"pre-commit==3.7.1",
"debugpy==1.8.2",
"protonvpn_wireguard_config_downloader[scripts]",
"protonvpn_wireguard_config_downloader[lint]",
"protonvpn_wireguard_config_downloader[test]",
"protonvpn_wireguard_config_downloader[check]",
]

[project.scripts]
protonvpn-wireguard-configs = "protonvpn_wireguard_config_downloader.entrypoint:main"

[tool.hatch.version]
path = "src/protonvpn_wireguard_config_downloader/__about__.py"

[tool.hatch.build]
exclude = [
"/.github",
]

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/protonvpn_wireguard_config_downloader"]

[tool.hatch.envs.default]
features = ["dev"]

[tool.hatch.envs.test]
features = ["scripts", "test"]


[tool.hatch.envs.test.scripts]
run = "inv test --args '{args}'"
run-cov = "inv test-cov --args '{args}'"
report-cov = "inv report-cov"
coverage = "inv coverage --args '{args}'"
html = "inv coverage --html --args '{args}'"

[tool.hatch.envs.lint]
template = "lint"
skip-install = false
features = ["scripts", "lint"]

[tool.hatch.envs.lint.scripts]
black = "inv lint-black --args '{args}'"
ruff = "inv lint-ruff --args '{args}'"
all = "inv lintall --args '{args}'"
fix-black = "inv fix-black --args '{args}'"
fix-ruff = "inv fix-ruff --args '{args}'"
fixall = "inv fixall --args '{args}'"

[tool.hatch.envs.check]
features = ["scripts", "check"]

[tool.hatch.envs.check.scripts]
pyright = "inv check-pyright --args '{args}'"
all = "inv checkall --args '{args}'"

[tool.black]
line-length = 88
target-version = ['py310']

[tool.ruff]
target-version = "py311"
line-length = 88
src = ["src"]

[tool.ruff.lint]
select = [
"A", # flake8-builtins
# "ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
# "ASYNC", # flake8-async
"B", # flake8-bugbear
# "BLE", # flake8-blind-except
"C4", # flake8-comprehensions
"C90", # mccabe
# "COM", # flake8-commas
# "D", # pydocstyle
# "DJ", # flake8-django
"DTZ", # flake8-datetimez
"E", # pycodestyle (default)
"EM", # flake8-errmsg
# "ERA", # eradicate
# "EXE", # flake8-executable
"F", # Pyflakes (default)
# "FA", # flake8-future-annotations
"FBT", # flake8-boolean-trap
# "FLY", # flynt
# "G", # flake8-logging-format
"I", # isort
"ICN", # flake8-import-conventions
# "INP", # flake8-no-pep420
# "INT", # flake8-gettext
"ISC", # flake8-implicit-str-concat
"N", # pep8-naming
# "NPY", # NumPy-specific rules
# "PD", # pandas-vet
# "PGH", # pygrep-hooks
# "PIE", # flake8-pie
# "PL", # Pylint
"PLC", # Pylint: Convention
"PLE", # Pylint: Error
"PLR", # Pylint: Refactor
"PLW", # Pylint: Warning
# "PT", # flake8-pytest-style
# "PTH", # flake8-use-pathlib
# "PYI", # flake8-pyi
"Q", # flake8-quotes
# "RET", # flake8-return
# "RSE", # flake8-raise
"RUF", # Ruff-specific rules
"S", # flake8-bandit
# "SIM", # flake8-simplify
# "SLF", # flake8-self
"T10", # flake8-debugger
"T20", # flake8-print
# "TCH", # flake8-type-checking
# "TD", # flake8-todos
"TID", # flake8-tidy-imports
# "TRY", # tryceratops
"UP", # pyupgrade
"W", # pycodestyle
"YTT", # flake8-2020
]
ignore = [
# Allow non-abstract empty methods in abstract base classes
"B027",
# Remove flake8-errmsg since we consider they bloat the code and provide limited value
"EM",
# Allow boolean positional values in function calls, like `dict.get(... True)`
"FBT003",
# Ignore checks for possible passwords
"S105", "S106", "S107",
# Ignore warnings on subprocess.run / popen
"S603",
# Ignore complexity
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
]
unfixable = [
# Don't touch unused imports
"F401",
]

[tool.ruff.lint.isort]
known-first-party = ["protonvpn_wireguard_config_downloader"]

[tool.ruff.lint.flake8-bugbear]
# add exceptions to B008 for fastapi.
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]

[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.ruff.lint.per-file-ignores]
# Tests can use magic values, assertions, and relative imports
"tests/**/*" = ["PLR2004", "S101", "TID252"]

[tool.pytest.ini_options]
minversion = "7.3"
testpaths = ["tests"]
pythonpath = [".", "src"]

[tool.coverage.paths]
protonvpn_wireguard_config_downloader = ["src/protonvpn_wireguard_config_downloader"]
tests = ["tests"]

[tool.coverage.run]
source_pkgs = ["protonvpn_wireguard_config_downloader"]
branch = true
parallel = true
omit = [
"src/protonvpn_wireguard_config_downloader/__about__.py",
]

[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

[tool.pyright]
include = ["src", "tests", "tasks.py"]
exclude = [".env/**", ".venv/**"]
extraPaths = ["src"]
pythonVersion = "3.11"
typeCheckingMode="strict"
disableBytesTypePromotions = true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "1.0.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import logging

from protonvpn_wireguard_config_downloader.settings import Settings

logger = logging.getLogger("task")

if not logger.hasHandlers():
logger.setLevel(logging.DEBUG if Settings.DEBUG else logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("[%(asctime)s: %(levelname)s] %(message)s"))
logger.addHandler(handler)
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import argparse
import asyncio
import logging
from pathlib import Path

from protonvpn_wireguard_config_downloader import logger
from protonvpn_wireguard_config_downloader.__about__ import __version__
from protonvpn_wireguard_config_downloader.protonvpn import (
login,
logout,
save_vpn_server_wireguard_config,
vpn_servers,
)
from protonvpn_wireguard_config_downloader.settings import Settings


async def download_vpn_wireguard_configs(
username: str, password: str, wireguard_port: int, work_dir: Path
) -> None:
"""Download Wireguard configuration files for all VPN servers."""
session = await login(username, password)
try:
logger.debug("Fetching available VPN servers for client...")
for vpn_server in vpn_servers(session, wireguard_port):
save_vpn_server_wireguard_config(session, vpn_server, work_dir)
finally:
logger.debug("Logging out...")
await logout(session)
logger.info("Successfully logged out client.")


def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"-v", "--verbose", help="Show verbose output", action="store_true"
)
parser.add_argument(
"--version",
help="Show version and exit.",
action="version",
version="%(prog)s " + __version__,
)
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)

asyncio.run(
download_vpn_wireguard_configs(
Settings.USERNAME,
Settings.PASSWORD,
Settings.WIREGUARD_PORT,
Settings.WORKDIR,
)
)
Loading
Loading