From fa64fe7f729e452f71173e0f6b0884d85394b773 Mon Sep 17 00:00:00 2001 From: GoProSlowYo <68455785+goproslowyo@users.noreply.github.com> Date: Mon, 2 Oct 2023 14:04:55 -0700 Subject: [PATCH] Refactor a bit --- box-to-docs.py | 150 +++++++++++++++++-------------------- models.py | 104 ++++++++++++++------------ poetry.lock | 195 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 5 ++ 4 files changed, 320 insertions(+), 134 deletions(-) diff --git a/box-to-docs.py b/box-to-docs.py index b611d34..cc656f3 100755 --- a/box-to-docs.py +++ b/box-to-docs.py @@ -1,26 +1,24 @@ #!/usr/bin/env python3 -from typing import List, Optional import argparse import json import logging from time import sleep - -from utils import WRITEUP_TEMPLATE -from models import Machine +from typing import List, Optional import requests +from models import Machine +from utils import WRITEUP_TEMPLATE -def fetch_htb_machines(htb_token: str) -> Optional[List[Machine]]: - """Fetch machines from HTB. - Args: - htb_token (str): The HTB API token. +def fetch_htb_machines() -> Optional[List[Machine]]: + """Fetch machines from HTB. Returns: Optional[List[Machine]]: A list of Machine objects or None if fetching fails. """ + # Initialize variables for pagination per_page = 25 current_page = 1 @@ -29,7 +27,7 @@ def fetch_htb_machines(htb_token: str) -> Optional[List[Machine]]: while True: htb_url = f"https://www.hackthebox.com/api/v4/machine/list/retired/paginated?per_page={per_page}&page={current_page}" headers = { - "Authorization": f"Bearer {htb_token}", + "Authorization": f"Bearer {args.htb_token}", "user-agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0", } response = requests.get(htb_url, headers=headers) @@ -40,21 +38,21 @@ def fetch_htb_machines(htb_token: str) -> Optional[List[Machine]]: # Convert the JSON machine data to Machine objects for machine_data in page_data.get("data", []): machine = Machine( - id=machine_data["id"], + machine_id=machine_data["id"], name=machine_data["name"], os=machine_data["os"], - release=machine_data["release"], - isTodo=machine_data["isTodo"], - difficultyText=machine_data["difficultyText"], - star=machine_data["star"], - playInfo=machine_data["playInfo"], - authUserInUserOwns=machine_data["authUserInUserOwns"], - authUserInRootOwns=machine_data["authUserInRootOwns"], - avatar=machine_data["avatar"], + release_date=machine_data["release"], + todo=machine_data["isTodo"], + difficulty=machine_data["difficultyText"], + rating=machine_data["star"], + machine_state=machine_data["playInfo"], + userOwned=machine_data["authUserInUserOwns"], + rootOwned=machine_data["authUserInRootOwns"], + image=machine_data["avatar"], ) machines.append(machine) - # Check if there is a "Next »" link in the meta section + # Check if there is a "Next » (»)" link in the meta section next_link = next( ( link["url"] @@ -83,95 +81,84 @@ def fetch_htb_machines(htb_token: str) -> Optional[List[Machine]]: return machines -def check_existing_item(notion_token: str, database_id: str, box_id: int) -> bool: +def check_existing_item(machine_id: int) -> bool: """Checks if an item with a specific Box ID exists in the Notion database. Args: - notion_token (str): The Notion API token. - database_id (str): The Notion database ID. - box_id (int): The Box ID to check for. + machine_id (int): The Box ID to check for. Returns: bool: True if the item exists, False otherwise. """ - notion_api_url = f"https://api.notion.com/v1/databases/{database_id}/query" + notion_api_url = f"https://api.notion.com/v1/databases/{args.database_id}/query" headers = { - "Authorization": f"Bearer {notion_token}", + "Authorization": f"Bearer {args.notion_token}", "Content-Type": "application/json", "Notion-Version": "2022-06-28", } - payload = {"filter": {"property": "Box ID", "number": {"equals": box_id}}} + payload = {"filter": {"property": "Box ID", "number": {"equals": machine_id}}} response = requests.post(notion_api_url, headers=headers, json=payload) if response.status_code != requests.codes.ok: logging.error(response.text) - logging.error(f"Failed to check for existing item with Box ID {box_id}") + logging.error(f"Failed to check for existing item with Box ID {machine_id}") return False data = response.json().get("results", []) return len(data) > 0 # If there are results, an item with the Box ID already exists -def update_notion_database(notion_token: str, database_id: str, retired_machines: List[Machine]) -> None: - """Updates a Notion database with retired HTB machines. +def update_notion_database(machines: List[Machine]) -> None: + """Updates a Notion database with the list of HTB machines skipping any existing machines found by machine_id. Args: - notion_token (str): The Notion API token. - database_id (str): The Notion database ID. - retired_machines (List[Machine]): A list of retired Machine objects. + machines (List[Machine]): A list of Machine objects. """ notion_api_url = f"https://api.notion.com/v1/pages" headers = { - "Authorization": f"Bearer {notion_token}", + "Authorization": f"Bearer {args.notion_token}", "Content-Type": "application/json", "Notion-Version": "2022-06-28", } - for machine in retired_machines: - box_id = int(machine.id) - box_name = machine.name - logging.debug(f"Box Name: {box_name}\nBox ID: {box_id}") + + for machine in machines: + machine_id = int(machine["id"]) + machine_name = machine["name"] + logging.debug(f"machine: {machine}") + logging.debug(f"Box Name: {machine_name}\nBox ID: {machine_id}") # Check if an item with the same Box ID already exists in the database - if check_existing_item(notion_token, database_id, box_id): + if check_existing_item(machine_id): logging.warn( - f"Item with Box ID {box_id} for {box_name} already exists. Skipping." + f"Item with Box ID {machine_id} for {machine_name} already exists. Skipping." ) + sleep(0.5) continue - sleep(0.6) - img_block = { - "object": "block", - "type": "image", - "image": { - "type": "external", - "external": {"url": f"https://www.hackthebox.com{machine.avatar}"}, - }, - } child_blocks = [block for block in WRITEUP_TEMPLATE] - child_blocks.insert(0, img_block) logging.debug(f"child_blocks: {child_blocks}") payload = { - "parent": {"database_id": database_id, "type": "database_id"}, + "parent": {"database_id": args.database_id, "type": "database_id"}, "icon": { "type": "external", - "external": {"url": f"https://www.hackthebox.com{machine.avatar}"}, + "external": {"url": f"https://www.hackthebox.com{machine['avatar']}"}, }, "cover": { "type": "external", - "external": {"url": f"https://www.hackthebox.com{machine.avatar}"}, + "external": {"url": f"https://www.hackthebox.com{machine['avatar']}"}, }, "properties": { "Name": {"title": [{"text": {"content": machine.name}}]}, - "Box ID": {"number": int(machine.id)}, - "OS": {"select": {"name": machine.os}}, - "Release Date": {"date": {"start": machine.release}}, - "To Do?": {"checkbox": machine.isTodo}, - "Difficulty": {"select": {"name": machine.difficultyText}}, - "Rating": {"number": float(machine.star)}, - "Active?": {"checkbox": machine.playInfo["isActive"] or False}, - "$": {"checkbox": machine.authUserInUserOwns or False}, - "#": {"checkbox": machine.authUserInRootOwns or False}, + "Box ID": {"number": int(machine_id)}, + "OS": {"select": {"name": machine["os"]}}, + "Release Date": {"date": {"start": machine["release"]}}, + "To Do?": {"checkbox": machine["isTodo"]}, + "Difficulty": {"select": {"name": machine["difficultyText"]}}, + "Rating": {"number": float(machine["star"])}, + "Active?": {"checkbox": machine["playInfo"]["isActive"] or False}, + "$": {"checkbox": machine["authUserInUserOwns"] or False}, + "#": {"checkbox": machine["authUserInRootOwns"] or False}, }, "children": child_blocks, } @@ -188,17 +175,17 @@ def update_notion_database(notion_token: str, database_id: str, retired_machines retry = 0 logging.error(response.text) logging.error( - f"Failed to update Notion database for machine {machine.name}" + f"Failed to update Notion database for machine {machine_name}" ) while retry < 3: sleep(1) - logging.info(f"Retrying update for machine {machine.name}") + logging.info(f"Retrying update for machine {machine_name}") response = requests.post(notion_api_url, headers=headers, json=payload) if response.status_code == requests.codes.ok: break retry += 1 break - sleep(0.6) + sleep(0.5) if __name__ == "__main__": @@ -207,24 +194,19 @@ def update_notion_database(notion_token: str, database_id: str, retired_machines # Argument parsing # TODO(gpsy): Make these read env vars instead of passing secrets as args - parser = argparse.ArgumentParser( - description="docsthebox: HTB Machines to Notion DB for Writeups" - ) + parser = argparse.ArgumentParser(description="docsthebox: HTB Machines to Notion DB for Writeups") parser.add_argument("--htb-token", required=False, help="Your HTB Bearer Token") parser.add_argument("--notion-token", required=True, help="Your Notion API Token") - parser.add_argument( - "--database-id", - required=True, - help="Notion Database ID where new rows will be created", - ) + parser.add_argument("--database-id", required=True, help="Notion Database where machines will be created") # Flag to skip HTB api calls and use local json file - parser.add_argument( - "--local", - action="store_true", - help="Skip HTB API calls and use local JSON file instead", - ) + parser.add_argument("--local", action="store_true", help="Skip HTB API calls and use local JSON file instead") + parser.add_argument("--debug", action="store_true", help="Enable debug logging") args = parser.parse_args() + # Set debug logging if debug flag is set + if args.debug: + logging.getLogger().setLevel(logging.DEBUG) + # Return an error is HTB token not set but local flag is not set if not args.htb_token and not args.local: logging.error("HTB Token not set. Exiting.") @@ -238,14 +220,14 @@ def update_notion_database(notion_token: str, database_id: str, retired_machines logging.info( "Skipping HTB API calls and using local JSON file instead." ) - retired_machines = json.load(f) + machines = json.load(f) # print length - logging.info(f"Loaded {len(retired_machines)} Retired machines") + logging.info(f"Loaded {len(machines)} Retired machines") # if not exists warn and use fetch_htb_machines except FileNotFoundError: logging.warning("Local JSON file not found. Fetching from HTB...") - retired_machines = fetch_htb_machines(args.htb_token) - if retired_machines is None: + machines = fetch_htb_machines() + if machines is None: logging.error("Could not fetch retired machines. Exiting.") exit(1) else: @@ -259,12 +241,12 @@ def update_notion_database(notion_token: str, database_id: str, retired_machines pass # Fetch retired machines from HTB logging.info("Fetching retired machines from HTB...") - retired_machines = fetch_htb_machines(args.htb_token) - if retired_machines is None: + machines = fetch_htb_machines() + if machines is None: logging.error("Could not fetch retired machines. Exiting.") exit(1) # Update Notion database logging.info("Updating Notion database...") - update_notion_database(args.notion_token, args.database_id, retired_machines) + update_notion_database(machines) logging.info("Finished updating the Notion database with retired HTB machines.") diff --git a/models.py b/models.py index db92109..3144403 100644 --- a/models.py +++ b/models.py @@ -1,8 +1,9 @@ -from typing import Dict, TypedDict, Union, Any +from typing import Any, Dict, TypedDict, Union -class PlayInfo(TypedDict): - """Type definition for playInfo attribute.""" +class MachineState(TypedDict): + """Information about the state of the machine.""" + isActive: bool isCompleted: bool @@ -12,63 +13,74 @@ class Machine: Represents an HTB (Hack The Box) machine. Attributes: - id (int): The ID of the machine. + machine_id (int): The ID of the machine. name (str): The name of the machine. os (str): The operating system of the machine. - release (str): The release version of the machine. - isTodo (bool): Whether the machine is marked as "To Do". - difficultyText (str): The difficulty level of the machine as a text. - star (float): The star rating of the machine. - playInfo (PlayInfo): Information about the play state of the machine. - authUserInUserOwns (bool): Whether the authenticated user owns the user part. - authUserInRootOwns (bool): Whether the authenticated user owns the root part. - avatar (str): The avatar image URL or path for the machine. + release_date (str): The release date of the machine. + todo (bool): Whether the machine is marked as "To Do". + difficulty (str): The difficulty level of the machine. + rating (float): The rating of the machine. + machine_state (MachineState): Information about the state of the machine. + authUserInUserOwns (bool): Whether user owns the user flag. + authUserInRootOwns (bool): Whether user owns the root flag. + image (str): The avatar image URL or path for the machine. """ - def __init__(self, id: int, name: str, os: str, release: str, isTodo: bool, - difficultyText: str, star: float, playInfo: PlayInfo, - authUserInUserOwns: bool, authUserInRootOwns: bool, avatar: str): + def __init__( + self, + machine_id: int, + name: str, + os: str, + release_date: str, + todo: bool, + difficulty: str, + rating: float, + machine_state: MachineState, + userOwned: bool, + rootOwned: bool, + image: str, + ): """ Initializes a new instance of the Machine class. Parameters: - id (int): The ID of the machine. + machine_id (int): The ID of the machine. name (str): The name of the machine. os (str): The operating system of the machine. - release (str): The release version of the machine. - isTodo (bool): Whether the machine is marked as "To Do". - difficultyText (str): The difficulty level of the machine as a text. - star (float): The star rating of the machine. - playInfo (PlayInfo): Information about the play state of the machine. - authUserInUserOwns (bool): Whether the authenticated user owns the user part. - authUserInRootOwns (bool): Whether the authenticated user owns the root part. - avatar (str): The avatar image URL or path for the machine. + release_date (str): The release date of the machine. + todo (bool): Whether the machine is marked as "To Do". + difficulty (str): The difficulty level of the machine. + rating (float): The rating of the machine. + machine_state (MachineState): Information about the state of the machine. + authUserInUserOwns (bool): Whether user owns the user flag. + authUserInRootOwns (bool): Whether user owns the root flag. + image (str): The avatar image URL or path for the machine. """ - self.id = id + self.id = machine_id self.name = name self.os = os - self.release = release - self.isTodo = isTodo - self.difficultyText = difficultyText - self.star = star - self.playInfo = playInfo - self.authUserInUserOwns = authUserInUserOwns - self.authUserInRootOwns = authUserInRootOwns - self.avatar = avatar + self.release_date = release_date + self.todo = todo + self.difficulty = difficulty + self.rating = rating + self.machine_state = machine_state + self.userOwned = userOwned + self.rootOwned = rootOwned + self.image = image - def to_dict(self) -> Dict[str, Union[Any, PlayInfo]]: + def to_dict(self) -> Dict[str, Union[Any, MachineState]]: """Convert the Machine object to a dictionary.""" return { - 'id': self.id, - 'name': self.name, - 'os': self.os, - 'release': self.release, - 'isTodo': self.isTodo, - 'difficultyText': self.difficultyText, - 'star': self.star, - 'playInfo': self.playInfo, - 'authUserInUserOwns': self.authUserInUserOwns, - 'authUserInRootOwns': self.authUserInRootOwns, - 'avatar': self.avatar - } \ No newline at end of file + "id": self.id, + "name": self.name, + "os": self.os, + "release": self.release_date, + "isTodo": self.todo, + "difficultyText": self.difficulty, + "star": self.rating, + "playInfo": self.machine_state, + "authUserInUserOwns": self.userOwned, + "authUserInRootOwns": self.rootOwned, + "avatar": self.image, + } diff --git a/poetry.lock b/poetry.lock index 016d425..848551b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,49 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +[[package]] +name = "black" +version = "23.9.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, + {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, + {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" version = "2023.7.22" @@ -110,6 +154,31 @@ files = [ {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + [[package]] name = "idna" version = "3.4" @@ -121,6 +190,99 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "mypy" +version = "1.5.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, + {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, + {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, + {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, + {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, + {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, + {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, + {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, + {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, + {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, + {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, + {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, + {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, + {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, + {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, + {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, + {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, + {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, + {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, + {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, + {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, + {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, + {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, + {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, + {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, + {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, + {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "3.11.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + [[package]] name = "requests" version = "2.31.0" @@ -142,15 +304,40 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "types-requests" +version = "2.31.0.7" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "types-requests-2.31.0.7.tar.gz", hash = "sha256:4d930dcabbc2452e3d70728e581ac4ac8c2d13f62509ad9114673f542af8cb4e"}, + {file = "types_requests-2.31.0.7-py3-none-any.whl", hash = "sha256:39844effefca88f4f824dcdc4127b813d3b86a56b2248d3d1afa58832040d979"}, +] + +[package.dependencies] +urllib3 = ">=2" + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + [[package]] name = "urllib3" -version = "2.0.5" +version = "2.0.6" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, - {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, + {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, + {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, ] [package.extras] @@ -162,4 +349,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "a5780ef8e06df616beb6eb67292099db49b8fe658fcbf22940e5e1af96a7c14e" +content-hash = "ed1059d3aa7e59c8f7f351abd9208e3be5011c65b9582d04354dfee0331d2bee" diff --git a/pyproject.toml b/pyproject.toml index e8fb1f1..46cadff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,11 @@ python = "^3.11" requests = "^2.31.0" +[tool.poetry.group.dev.dependencies] +black = "^23.9.1" +mypy = "^1.5.1" +types-requests = "^2.31.0.7" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api"