From 8f8503d4f7fc57c7daeba51fc53833c8cea8ce2c Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 14 Jan 2021 11:51:31 +0100 Subject: [PATCH] Add the gist of the application --- cloudflare-ddns/__main__.py | 39 +++++++++++++++++++++++++++++++++++ cloudflare-ddns/utils.py | 41 +++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 81 insertions(+) create mode 100644 cloudflare-ddns/utils.py diff --git a/cloudflare-ddns/__main__.py b/cloudflare-ddns/__main__.py index e69de29..5311fc8 100644 --- a/cloudflare-ddns/__main__.py +++ b/cloudflare-ddns/__main__.py @@ -0,0 +1,39 @@ +import logging + +import click +from email_validator import EmailNotValidError, validate_email + +from .utils import parse_duration, validate_bearer + +DEFAULT_DELAY = "5 minutes" + + +@click.command() +@click.option('--delay', '-d', default=DEFAULT_DELAY, show_default=True) +@click.option('--email', '-u', prompt="Enter your Cloudflare Email address") +@click.option('--key', '-k', prompt="Enter your Cloudflare Auth key", hide_input=True) +def start(delay: str, email: str, key: str) -> None: + """Main application entrypoint.""" + try: + duration = parse_duration(delay) + except ValueError as e: + logging.error(f"Failed to parse delay: {e}") + logging.error("Exiting with code 64.") + exit(64) + + try: + validate_email(email) + except EmailNotValidError: + logging.warning(f"The email address {email} don't seem valid. Do you have a typo?") + + try: + validate_bearer(key) + except ...: + ... + + +# Main entrypoint +if __name__ == "__main__": + logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s") + + start(auto_envvar_prefix="CF_DDNS") diff --git a/cloudflare-ddns/utils.py b/cloudflare-ddns/utils.py new file mode 100644 index 0000000..a34a46b --- /dev/null +++ b/cloudflare-ddns/utils.py @@ -0,0 +1,41 @@ +import re + +DURATION_REGEX = re.compile( + r"((?P\d+?) ?(days|day|D|d) ?)?" + r"((?P\d+?) ?(hours|hour|H|h) ?)?" + r"((?P\d+?) ?(minutes|minute|min|M|m) ?)?" + r"((?P\d+?) ?(seconds|second|sec|S|s))?" +) +UNIT_TO_SECONDS = { + "days": 86400, + "hours": 3600, + "minutes": 60, + "seconds": 1 +} + + +def parse_duration(duration: str) -> int: + """ + Parameter type for durations. + + The converter supports the following symbols for each unit of time: + - days: `d`, `D`, `day`, `days` + - hours: `H`, `h`, `hour`, `hours` + - minutes: `M`, `m`, `minute`, `minutes`, `min` + - seconds: `S`, `s`, `second`, `seconds`, `sec` + The units need to be provided in descending order of magnitude. + """ + match = DURATION_REGEX.fullmatch(duration) + if not match: + raise ValueError(f"{duration} isn't a valid duration.") + + duration = 0 + for unit, time_value in match.groupdict().items(): + if time_value: + duration += int(time_value) * UNIT_TO_SECONDS[unit] + + return duration + + +def validate_bearer(bearer: str) -> None: + ... diff --git a/requirements.txt b/requirements.txt index 0ef32ad..70a9287 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests~=2.25.1 click8~=8.0.1 +email-validator~=1.1.2