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 4 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
15 changes: 15 additions & 0 deletions protonvpn-wireguard-config-downloader/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.11-slim-bookworm
LABEL org.opencontainers.image.source=https://github.com/kiwix/mirrors-qa

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)

## 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 ./:/data protonvpn-wireguard-config-downloader protonvpn-wireguard-configs
elfkuzco marked this conversation as resolved.
Show resolved Hide resolved
```
237 changes: 237 additions & 0 deletions protonvpn-wireguard-config-downloader/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
[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
"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.1.1",
elfkuzco marked this conversation as resolved.
Show resolved Hide resolved
"ruff==0.2.0",
]
check = [
"pyright==1.1.349",
]
test = [
"pytest==8.0.0",
"coverage==7.4.1",
]
dev = [
"pre-commit==3.6.0",
"debugpy==1.8.0",
"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__ = "0.0.1-dev0"
elfkuzco marked this conversation as resolved.
Show resolved Hide resolved
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