helldivepy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- helldivepy/__init__.py +3 -0
- helldivepy/api/__init__.py +15 -0
- helldivepy/api/assignments.py +34 -0
- helldivepy/api/base.py +41 -0
- helldivepy/api/campaigns.py +42 -0
- helldivepy/api/dispatches.py +41 -0
- helldivepy/api/planets.py +59 -0
- helldivepy/api/steam.py +34 -0
- helldivepy/api/war.py +25 -0
- helldivepy/api_client.py +153 -0
- helldivepy/constants.py +3 -0
- helldivepy/enums.py +44 -0
- helldivepy/models.py +208 -0
- helldivepy/utils.py +60 -0
- helldivepy-0.1.0.dist-info/LICENSE +21 -0
- helldivepy-0.1.0.dist-info/METADATA +84 -0
- helldivepy-0.1.0.dist-info/RECORD +18 -0
- helldivepy-0.1.0.dist-info/WHEEL +4 -0
helldivepy/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .dispatches import DispatchModule
|
|
2
|
+
from .war import WarModule
|
|
3
|
+
from .steam import SteamModule
|
|
4
|
+
from .assignments import AssignmentsModule
|
|
5
|
+
from .planets import PlanetsModule
|
|
6
|
+
from .campaigns import CampaignModule
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"DispatchModule",
|
|
10
|
+
"WarModule",
|
|
11
|
+
"SteamModule",
|
|
12
|
+
"AssignmentsModule",
|
|
13
|
+
"PlanetsModule",
|
|
14
|
+
"CampaignModule",
|
|
15
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AssignmentsModule(BaseApiModule):
|
|
10
|
+
"""
|
|
11
|
+
The Assignments API module (Major Order). This module is used to interact with the current major order(s).
|
|
12
|
+
|
|
13
|
+
Methods:
|
|
14
|
+
get_all_assignments(): Gets all current assignments
|
|
15
|
+
get_assignment(assignment_id: int): Gets one assignment using the assignment ID
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
20
|
+
super().__init__(api_client)
|
|
21
|
+
|
|
22
|
+
def get_all_assignments(self) -> typing.List[models.Assignment]:
|
|
23
|
+
"""
|
|
24
|
+
Gets all current assignments
|
|
25
|
+
"""
|
|
26
|
+
data = self.get("community", "api", "v1", "assignments")
|
|
27
|
+
return [models.Assignment(**assignment) for assignment in data]
|
|
28
|
+
|
|
29
|
+
def get_assignment(self, assignment_id: int) -> models.Assignment:
|
|
30
|
+
"""
|
|
31
|
+
Gets one assignment using the assignment ID
|
|
32
|
+
"""
|
|
33
|
+
data = self.get("community", "api", "v1", "assignments", str(assignment_id))
|
|
34
|
+
return models.Assignment(**data)
|
helldivepy/api/base.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import requests
|
|
3
|
+
from helldivepy.utils import (
|
|
4
|
+
DiveHarderAPIConnectionError,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from helldivepy.api_client import ApiClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseApiModule:
|
|
12
|
+
"""
|
|
13
|
+
The base class for all API modules.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
17
|
+
self.api_client = api_client
|
|
18
|
+
self.logger = api_client.logger
|
|
19
|
+
self.session = api_client.session
|
|
20
|
+
self.diveharder_url = api_client.api_config.diveharder
|
|
21
|
+
self.community_url = api_client.api_config.community
|
|
22
|
+
|
|
23
|
+
def get(self, url: typing.Literal["diveharder", "community"], *path) -> typing.Dict:
|
|
24
|
+
"""
|
|
25
|
+
Sends a GET request to the specified URL.
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
response = self.session.get(
|
|
29
|
+
url=f"{self.diveharder_url if url == 'diveharder' else self.community_url}/{'/'.join(path)}",
|
|
30
|
+
timeout=4,
|
|
31
|
+
)
|
|
32
|
+
response.raise_for_status()
|
|
33
|
+
return response.json()
|
|
34
|
+
except requests.exceptions.HTTPError as e:
|
|
35
|
+
self.logger.error(f"HTTPError: {e}")
|
|
36
|
+
return {}
|
|
37
|
+
except requests.exceptions.ConnectionError as e:
|
|
38
|
+
self.logger.error(f"ConnectionError: {e}")
|
|
39
|
+
raise DiveHarderAPIConnectionError(
|
|
40
|
+
f"Failed to connect to {self.diveharder_url if url == "diveharder" else self.community_url}. (Offline?)"
|
|
41
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CampaignModule(BaseApiModule):
|
|
10
|
+
"""
|
|
11
|
+
The Campaign API module. This module is used to interact with all campaigns (Battles/Planets that are accessible).
|
|
12
|
+
|
|
13
|
+
Methods:
|
|
14
|
+
get_campaigns(): Gets all current active campaigns.
|
|
15
|
+
get_campaign(campaign_id: int): Gets a campaign by its ID.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
20
|
+
super().__init__(api_client)
|
|
21
|
+
|
|
22
|
+
def get_campaigns(self) -> typing.List[models.Campaign]:
|
|
23
|
+
"""Gets all current active campaigns.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
typing.List[models.Campaign]: The campaigns.
|
|
27
|
+
"""
|
|
28
|
+
data = self.get("community", "api", "v1", "campaigns")
|
|
29
|
+
return [models.Campaign(**campaign) for campaign in data]
|
|
30
|
+
|
|
31
|
+
def get_campaign(self, campaign_id: int) -> models.Campaign:
|
|
32
|
+
"""Gets a campaign by its ID.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
campaign_id (int): The ID of the campaign.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
models.Campaign: The campaign.
|
|
39
|
+
"""
|
|
40
|
+
data = self.get("community", "api", "v1", "campaigns", str(campaign_id))
|
|
41
|
+
|
|
42
|
+
return models.Campaign(**data)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DispatchModule(BaseApiModule):
|
|
10
|
+
"""The Dispatch API module. This module is used to interact with all dispatches (Ingame News)
|
|
11
|
+
|
|
12
|
+
Methods:
|
|
13
|
+
get_dispatches(old_to_new: bool): Gets all dispatches and orders them based on the `old_to_new` argument.
|
|
14
|
+
get_dispatch(dispatch_id: int): Gets a dispatch by its ID.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
19
|
+
super().__init__(api_client)
|
|
20
|
+
|
|
21
|
+
def get_dispatches(self, old_to_new: bool = True) -> typing.List[models.Dispatch]:
|
|
22
|
+
"""
|
|
23
|
+
Gets the information about the current war.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Sort the dispatches by published date
|
|
27
|
+
data = sorted(
|
|
28
|
+
self.get("community", "api", "v1", "dispatches"),
|
|
29
|
+
key=lambda x: x["published"],
|
|
30
|
+
reverse=not old_to_new,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return [models.Dispatch(**dispatch) for dispatch in data]
|
|
34
|
+
|
|
35
|
+
def get_dispatch(self, dispatch_id: int) -> models.Dispatch:
|
|
36
|
+
"""
|
|
37
|
+
Gets the information about the current war.
|
|
38
|
+
"""
|
|
39
|
+
# Get the dispatch by the ID/Index
|
|
40
|
+
data = self.get("community", "api", "v1", "dispatches", str(dispatch_id))
|
|
41
|
+
return models.Dispatch(**data)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PlanetsModule(BaseApiModule):
|
|
10
|
+
"""
|
|
11
|
+
The Planets API module. This module is used to interact with the planets.
|
|
12
|
+
|
|
13
|
+
Methods:
|
|
14
|
+
get_planets(): Gets all planets.
|
|
15
|
+
get_planet(index: int): Gets a planet using the index.
|
|
16
|
+
get_planets_with_events(): Gets all planets with events.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
20
|
+
super().__init__(api_client)
|
|
21
|
+
self._planets: list[models.Planet] = []
|
|
22
|
+
|
|
23
|
+
def get_planets(self) -> typing.List[models.Planet]:
|
|
24
|
+
"""Get all planets
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
typing.List[models.Planet]: The list of planet objects
|
|
28
|
+
"""
|
|
29
|
+
data = self.get("community", "api", "v1", "planets")
|
|
30
|
+
|
|
31
|
+
self._planets = [models.Planet(**planet) for planet in data]
|
|
32
|
+
return self._planets
|
|
33
|
+
|
|
34
|
+
def get_planet(self, index: int, cached: bool = False) -> models.Planet:
|
|
35
|
+
"""Gets a planet using the index
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
index (int): The index of the planet
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
models.Planet: The planet object
|
|
42
|
+
"""
|
|
43
|
+
if cached:
|
|
44
|
+
if len(self._planets) < index:
|
|
45
|
+
self.logger.warning(f"Planet {index} not cached. Fetching from API.")
|
|
46
|
+
self.get_planets()
|
|
47
|
+
return self._planets[index]
|
|
48
|
+
return self._planets[index]
|
|
49
|
+
data = self.get("community", "api", "v1", "planets", str(index))
|
|
50
|
+
return models.Planet(**data)
|
|
51
|
+
|
|
52
|
+
def get_planets_with_events(self) -> typing.List[models.Planet]:
|
|
53
|
+
"""Gets all planets with an active event
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
typing.List[models.Planet]: The list of planet objects
|
|
57
|
+
"""
|
|
58
|
+
data = self.get("community", "api", "v1", "planet-events")
|
|
59
|
+
return [models.Planet(**planet) for planet in data]
|
helldivepy/api/steam.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SteamModule(BaseApiModule):
|
|
10
|
+
"""
|
|
11
|
+
The Steam API module. Used to get all the Game updates and News.
|
|
12
|
+
|
|
13
|
+
Methods:
|
|
14
|
+
get_all_steam_news(): Gets all the steam news.
|
|
15
|
+
get_steam_news(gid: str): Gets a steam news item by its ID.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
19
|
+
super().__init__(api_client)
|
|
20
|
+
|
|
21
|
+
def get_all_steam_news(self) -> typing.List[models.SteamNews]:
|
|
22
|
+
"""
|
|
23
|
+
Gets the information about the current war.
|
|
24
|
+
"""
|
|
25
|
+
data = self.get("community", "api", "v1", "steam")
|
|
26
|
+
|
|
27
|
+
return [models.SteamNews(**news) for news in data]
|
|
28
|
+
|
|
29
|
+
def get_steam_news(self, gid: str) -> models.SteamNews:
|
|
30
|
+
"""
|
|
31
|
+
Gets the information about the current war.
|
|
32
|
+
"""
|
|
33
|
+
data = self.get("community", "api", "v1", "steam", gid)
|
|
34
|
+
return models.SteamNews(**data)
|
helldivepy/api/war.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from helldivepy.api.base import BaseApiModule
|
|
2
|
+
import typing
|
|
3
|
+
import helldivepy.models as models
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from helldivepy.api_client import ApiClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WarModule(BaseApiModule):
|
|
10
|
+
"""The War module of the API. This module is used to get all info about the current war.
|
|
11
|
+
|
|
12
|
+
Methods:
|
|
13
|
+
get_war_info(): Gets the information about the current war.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, api_client: "ApiClient") -> None:
|
|
17
|
+
super().__init__(api_client)
|
|
18
|
+
|
|
19
|
+
def get_war_info(self) -> models.WarInfo:
|
|
20
|
+
"""
|
|
21
|
+
Gets the information about the current war.
|
|
22
|
+
"""
|
|
23
|
+
data = self.get("community", "api", "v1", "war")
|
|
24
|
+
|
|
25
|
+
return models.WarInfo(**data)
|
helldivepy/api_client.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import requests
|
|
3
|
+
from requests.adapters import HTTPAdapter, Retry
|
|
4
|
+
from helldivepy.constants import OFFICIAL_DIVEHARDER_URL, OFFICIAL_COMMUNITY_URL
|
|
5
|
+
import helldivepy.models as models
|
|
6
|
+
import typing
|
|
7
|
+
import helldivepy.api as modules
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def retry_adapter(
|
|
11
|
+
backoff_factor: float, retries: int, extra_retry_codes: list | None = None
|
|
12
|
+
) -> HTTPAdapter:
|
|
13
|
+
"""Configures an HTTP adapter with retries and backoff."""
|
|
14
|
+
if extra_retry_codes is None:
|
|
15
|
+
extra_retry_codes = []
|
|
16
|
+
retry_codes = [429] + extra_retry_codes
|
|
17
|
+
retry_strategy = Retry(
|
|
18
|
+
total=retries,
|
|
19
|
+
status_forcelist=retry_codes,
|
|
20
|
+
backoff_factor=backoff_factor,
|
|
21
|
+
allowed_methods=["GET"],
|
|
22
|
+
)
|
|
23
|
+
return HTTPAdapter(max_retries=retry_strategy)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_logger(debug: bool) -> logging.Logger:
|
|
27
|
+
"""Configures debug logging if requested."""
|
|
28
|
+
from rich.logging import RichHandler
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
logger.level = logging.DEBUG if debug else 5000
|
|
32
|
+
if debug:
|
|
33
|
+
logger.addHandler(
|
|
34
|
+
RichHandler(
|
|
35
|
+
level=logging.DEBUG,
|
|
36
|
+
omit_repeated_times=False,
|
|
37
|
+
markup=True,
|
|
38
|
+
rich_tracebacks=True,
|
|
39
|
+
log_time_format="%X %p",
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
return logger
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ModuleDict(typing.TypedDict):
|
|
46
|
+
"""A dictionary of modules."""
|
|
47
|
+
|
|
48
|
+
war: modules.WarModule
|
|
49
|
+
dispatch: modules.DispatchModule
|
|
50
|
+
steam: modules.SteamModule
|
|
51
|
+
assignments: modules.AssignmentsModule
|
|
52
|
+
planets: modules.PlanetsModule
|
|
53
|
+
campaigns: modules.CampaignModule
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ApiClient:
|
|
57
|
+
"""
|
|
58
|
+
The client used to interact with the Helldivers 2 APIs
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
_instance = None
|
|
62
|
+
|
|
63
|
+
def __new__(cls, *args, **kwargs) -> typing.Self:
|
|
64
|
+
if cls._instance is None:
|
|
65
|
+
cls._instance = super(ApiClient, cls).__new__(cls)
|
|
66
|
+
cls._instance.__init__(*args, **kwargs)
|
|
67
|
+
return cls._instance
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def get_client(cls):
|
|
71
|
+
return cls._instance
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
user_agent: str,
|
|
76
|
+
user_contact: str,
|
|
77
|
+
debug: bool = False,
|
|
78
|
+
diveharder_url: str = OFFICIAL_DIVEHARDER_URL,
|
|
79
|
+
community_url: str = OFFICIAL_COMMUNITY_URL,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""The client used to interact with the Helldivers 2 APIs
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
user_agent (str): The user agent to use when making requests.
|
|
85
|
+
user_contact (str): The user contact to use when making requests.
|
|
86
|
+
debug (bool, optional): Enables debug logging for development.
|
|
87
|
+
diveharder_url (str, optional): The diveharder API url to use. Defaults to `constants.OFFICIAL_DIVEHARDER_URL`.
|
|
88
|
+
community_url (str, optional): The community API url to use. Defaults to `constants.OFFICIAL_COMMUNITY_URL`.
|
|
89
|
+
"""
|
|
90
|
+
self.debug = debug
|
|
91
|
+
self.logger = set_logger(debug)
|
|
92
|
+
self.api_config = models.APIURLConfiguration(
|
|
93
|
+
diveharder=diveharder_url, community=community_url
|
|
94
|
+
)
|
|
95
|
+
self._user_contact = user_contact
|
|
96
|
+
self._user_agent = user_agent
|
|
97
|
+
self._setup_session()
|
|
98
|
+
self._modules = ModuleDict(
|
|
99
|
+
war=modules.WarModule(self),
|
|
100
|
+
dispatch=modules.DispatchModule(self),
|
|
101
|
+
steam=modules.SteamModule(self),
|
|
102
|
+
assignments=modules.AssignmentsModule(self),
|
|
103
|
+
planets=modules.PlanetsModule(self),
|
|
104
|
+
campaigns=modules.CampaignModule(self),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def _setup_session(self):
|
|
108
|
+
self.session = requests.Session()
|
|
109
|
+
self.session.headers.update(
|
|
110
|
+
{
|
|
111
|
+
"User-Agent": self._user_agent,
|
|
112
|
+
"X-Super-Client": self._user_agent,
|
|
113
|
+
"X-Super-Contact": self._user_contact,
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
self.session.mount("https://", retry_adapter(0.2, 2))
|
|
117
|
+
self.session.mount("http://", retry_adapter(0.2, 2))
|
|
118
|
+
|
|
119
|
+
def get_war_info(self) -> models.WarInfo:
|
|
120
|
+
return self._modules["war"].get_war_info()
|
|
121
|
+
|
|
122
|
+
# ==============[Modules]==============
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def war(self) -> modules.WarModule:
|
|
126
|
+
return self._modules["war"]
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def dispatch(self) -> modules.DispatchModule:
|
|
130
|
+
return self._modules["dispatch"]
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def steam(self) -> modules.SteamModule:
|
|
134
|
+
return self._modules["steam"]
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def assignments(self) -> modules.AssignmentsModule:
|
|
138
|
+
return self._modules["assignments"]
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def major_orders(self) -> modules.AssignmentsModule:
|
|
142
|
+
return self.assignments
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def planets(self) -> modules.PlanetsModule:
|
|
146
|
+
return self._modules["planets"]
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def campaigns(self) -> modules.CampaignModule:
|
|
150
|
+
return self._modules["campaigns"]
|
|
151
|
+
|
|
152
|
+
def __repr__(self) -> str:
|
|
153
|
+
return f"{self.__class__.__name__}({self.api_config})"
|
helldivepy/constants.py
ADDED
helldivepy/enums.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
# {"1": "race", "2": "unknown", "3": "goal", "11": "liberate", "12": "planet_index"}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BetterEnum(Enum):
|
|
8
|
+
@classmethod
|
|
9
|
+
def parse(cls, value):
|
|
10
|
+
try:
|
|
11
|
+
return cls(value)
|
|
12
|
+
except ValueError:
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CampaignTypes(BetterEnum):
|
|
17
|
+
|
|
18
|
+
LIBERATION_DEFENSE = 0
|
|
19
|
+
RECON = 1
|
|
20
|
+
STORY = 2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ValueTypes(BetterEnum):
|
|
24
|
+
RACE = 1
|
|
25
|
+
UNKNOWN = 2
|
|
26
|
+
TARGET_COUNT = 3
|
|
27
|
+
UNIT_ID = 4
|
|
28
|
+
ITEM_ID = 5
|
|
29
|
+
LIBERATE = 11
|
|
30
|
+
PLANET = 12
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RewardTypes(BetterEnum):
|
|
34
|
+
MEDALS = 1
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AssignmentTypes(BetterEnum):
|
|
38
|
+
ERADICATE = 3
|
|
39
|
+
LIBERATION = 11
|
|
40
|
+
DEFENSE = 12
|
|
41
|
+
CONTROL = 13
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
FactionType = typing.Literal["Humans", "Terminids", "Automaton", "Illuminate"]
|
helldivepy/models.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import typing
|
|
4
|
+
import helldivepy.enums as enums
|
|
5
|
+
import helldivepy.utils as utils
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class APIURLConfiguration(BaseModel):
|
|
9
|
+
diveharder: str
|
|
10
|
+
community: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Statistics(BaseModel):
|
|
14
|
+
"""The statistics of a player."""
|
|
15
|
+
|
|
16
|
+
missions_won: int = Field(alias="missionsWon")
|
|
17
|
+
missions_lost: int = Field(alias="missionsLost")
|
|
18
|
+
mission_time: int = Field(alias="missionTime")
|
|
19
|
+
terminid_kills: int = Field(alias="terminidKills")
|
|
20
|
+
automaton_kills: int = Field(alias="automatonKills")
|
|
21
|
+
illuminate_kills: int = Field(alias="illuminateKills")
|
|
22
|
+
bullets_fired: int = Field(alias="bulletsFired")
|
|
23
|
+
bullets_hit: int = Field(alias="bulletsHit")
|
|
24
|
+
time_played: int = Field(alias="timePlayed")
|
|
25
|
+
deaths: int
|
|
26
|
+
revives: int
|
|
27
|
+
friendlies: int
|
|
28
|
+
mission_success_rate: float = Field(alias="missionSuccessRate")
|
|
29
|
+
accuracy: int
|
|
30
|
+
player_count: int = Field(alias="playerCount")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WarInfo(BaseModel):
|
|
34
|
+
"""The information about the current war."""
|
|
35
|
+
|
|
36
|
+
started: datetime
|
|
37
|
+
ended: datetime
|
|
38
|
+
now: datetime
|
|
39
|
+
client_version: str = Field(alias="clientVersion")
|
|
40
|
+
factions: list[typing.Literal["Humans", "Terminids", "Automaton", "Illuminate"]]
|
|
41
|
+
impact_multiplier: float = Field(alias="impactMultiplier")
|
|
42
|
+
statistics: Statistics
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Dispatch(BaseModel):
|
|
46
|
+
"""The information about the current war."""
|
|
47
|
+
|
|
48
|
+
id: int
|
|
49
|
+
published: datetime
|
|
50
|
+
type: typing.Literal[0]
|
|
51
|
+
message: str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SteamNews(BaseModel):
|
|
55
|
+
"""Steam patchh notes."""
|
|
56
|
+
|
|
57
|
+
id: str
|
|
58
|
+
title: str
|
|
59
|
+
url: str
|
|
60
|
+
author: str
|
|
61
|
+
content: str
|
|
62
|
+
published_at: datetime = Field(alias="publishedAt")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class PlanetEvent(BaseModel):
|
|
66
|
+
"""An event on a planet."""
|
|
67
|
+
|
|
68
|
+
id: int
|
|
69
|
+
event_type: typing.Literal[1] = Field(alias="eventType")
|
|
70
|
+
faction: enums.FactionType
|
|
71
|
+
health: int
|
|
72
|
+
max_health: int = Field(alias="maxHealth")
|
|
73
|
+
start_time: datetime = Field(alias="startTime")
|
|
74
|
+
end_time: datetime = Field(alias="endTime")
|
|
75
|
+
campaign_id: int = Field(alias="campaignId")
|
|
76
|
+
joint_operation_ids: list[int] = Field(alias="jointOperationIds")
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def planet(self) -> "Planet | None":
|
|
80
|
+
from helldivepy import ApiClient
|
|
81
|
+
|
|
82
|
+
if ApiClient._instance:
|
|
83
|
+
return ApiClient._instance.planets.get_planet(self.campaign_id)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Position(BaseModel):
|
|
88
|
+
"""A position in the game."""
|
|
89
|
+
|
|
90
|
+
x: float
|
|
91
|
+
y: float
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class PlanetaryHazard(BaseModel):
|
|
95
|
+
"""A hazard on a planet."""
|
|
96
|
+
|
|
97
|
+
name: str
|
|
98
|
+
description: str
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Biome(BaseModel):
|
|
102
|
+
"""A biome on a planet."""
|
|
103
|
+
|
|
104
|
+
name: str
|
|
105
|
+
description: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Planet(BaseModel):
|
|
109
|
+
"""A planet in the game."""
|
|
110
|
+
|
|
111
|
+
index: int
|
|
112
|
+
name: str
|
|
113
|
+
sector: str
|
|
114
|
+
biome: "Biome"
|
|
115
|
+
hazards: list["PlanetaryHazard"]
|
|
116
|
+
hash: int
|
|
117
|
+
position: "Position"
|
|
118
|
+
waypoints: list[int]
|
|
119
|
+
max_health: int = Field(alias="maxHealth")
|
|
120
|
+
health: int
|
|
121
|
+
disabled: bool
|
|
122
|
+
initial_owner: enums.FactionType = Field(
|
|
123
|
+
alias="initialOwner",
|
|
124
|
+
)
|
|
125
|
+
current_owner: enums.FactionType = Field(alias="currentOwner")
|
|
126
|
+
regen_per_second: float = Field(alias="regenPerSecond")
|
|
127
|
+
event: PlanetEvent | None
|
|
128
|
+
statistics: Statistics
|
|
129
|
+
attacking: list[int]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# TODO: make better docstrings
|
|
133
|
+
class AssignmentTaskData(BaseModel):
|
|
134
|
+
liberate: bool | None
|
|
135
|
+
planet: Planet | None
|
|
136
|
+
target_count: int | None
|
|
137
|
+
race: enums.FactionType | None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class AssignmentTask(BaseModel):
|
|
141
|
+
"""A task in an assignment"""
|
|
142
|
+
|
|
143
|
+
type: enums.AssignmentTypes
|
|
144
|
+
values: list[int]
|
|
145
|
+
value_types: list[enums.ValueTypes] = Field(alias="valueTypes")
|
|
146
|
+
data: AssignmentTaskData = AssignmentTaskData(
|
|
147
|
+
liberate=None, planet=None, target_count=None, race=None
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def model_post_init(self, __context: typing.Any) -> None:
|
|
151
|
+
from helldivepy.api_client import ApiClient
|
|
152
|
+
|
|
153
|
+
client = ApiClient._instance
|
|
154
|
+
if client is None:
|
|
155
|
+
raise ValueError("ApiClient is not initialized")
|
|
156
|
+
|
|
157
|
+
for k, v in zip(self.value_types, self.values):
|
|
158
|
+
match k:
|
|
159
|
+
case enums.ValueTypes.PLANET:
|
|
160
|
+
self.data.planet = client.planets.get_planet(v, cached=True)
|
|
161
|
+
case enums.ValueTypes.RACE:
|
|
162
|
+
self.data.race = utils.parse_faction(v)
|
|
163
|
+
case enums.ValueTypes.TARGET_COUNT:
|
|
164
|
+
self.data.target_count = v
|
|
165
|
+
case enums.ValueTypes.LIBERATE:
|
|
166
|
+
self.data.liberate = bool(v)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class AssignmentReward(BaseModel):
|
|
170
|
+
"""
|
|
171
|
+
A reward in an assignment\n
|
|
172
|
+
Medals: 1
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
type: typing.Literal[1]
|
|
176
|
+
amount: int
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class Assignment(BaseModel):
|
|
180
|
+
"""An assignment (Major Order)"""
|
|
181
|
+
|
|
182
|
+
id: int
|
|
183
|
+
progress: list[int]
|
|
184
|
+
title: str | None = None
|
|
185
|
+
briefing: str | None = None
|
|
186
|
+
description: str | None = None
|
|
187
|
+
tasks: list[AssignmentTask]
|
|
188
|
+
reward: AssignmentReward
|
|
189
|
+
expiration: datetime
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def is_complete(self) -> bool:
|
|
193
|
+
return all(
|
|
194
|
+
task.data.target_count == self.progress[index]
|
|
195
|
+
for index, task in enumerate(self.tasks)
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def __str__(self) -> str:
|
|
199
|
+
return f"{self.title} - {self.briefing}"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class Campaign(BaseModel):
|
|
203
|
+
"""A campaign or battle in the game."""
|
|
204
|
+
|
|
205
|
+
id: int
|
|
206
|
+
planet: Planet
|
|
207
|
+
type: int
|
|
208
|
+
count: int
|
helldivepy/utils.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from helldivepy.constants import FACTIONS
|
|
3
|
+
import helldivepy.enums as enums
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def hdml_to_md(text: str) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Converts a string in HDML (DiveHarder Markup Language) format to Markdown format.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
text (str): The input string in HDML format.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
str: The input string converted to Markdown format.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> hdml_to_md("<i=3>Hello</i=3> <i=1>World</i=1>")
|
|
18
|
+
"[b]Hello[/b] [yellow]World[/yellow]"
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
pattern = r"<i=(\d+)>(.*?)<\/i(?:=\1)?>"
|
|
22
|
+
matches = re.findall(pattern, text)
|
|
23
|
+
|
|
24
|
+
modified_text = text
|
|
25
|
+
for match in matches:
|
|
26
|
+
if match[0] == "3":
|
|
27
|
+
modified_text = modified_text.replace(match[1], f"[b]{match[1]}[/b]")
|
|
28
|
+
elif match[0] == "1":
|
|
29
|
+
modified_text = modified_text.replace(
|
|
30
|
+
match[1], f"[yellow]{match[1]}[/yellow]"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
modified_text = re.sub(r"<\/?i(?:=\d+)?>", "", modified_text)
|
|
34
|
+
|
|
35
|
+
return modified_text
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def url_join(*args):
|
|
39
|
+
"""Join combine URL parts to get the full endpoint address."""
|
|
40
|
+
return "/".join(arg.strip("/") for arg in args)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_faction(faction: int) -> enums.FactionType | None:
|
|
44
|
+
"""Parse faction ID to string
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
faction (int): The faction ID
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: The faction name
|
|
51
|
+
"""
|
|
52
|
+
return FACTIONS.get(faction, None) # type: ignore
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DiveHarderException(Exception):
|
|
56
|
+
"""Base exception for all DiveHarder exceptions."""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DiveHarderAPIConnectionError(DiveHarderException):
|
|
60
|
+
"""Exception raised when the DiveHarder API connection fails."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 AJXD2
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: helldivepy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Helldivers 2 API wrapper using the Diveharder API and the Helldivers Community API
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: helldivers 2,api,wrapper,diveharder,community
|
|
7
|
+
Author: AJXD2
|
|
8
|
+
Author-email: aj@ajxd2.dev
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Games/Entertainment
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: fire (>=0.7.0,<0.8.0)
|
|
20
|
+
Requires-Dist: pydantic (>=2.9.2,<3.0.0)
|
|
21
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
22
|
+
Requires-Dist: rich (>=13.9.3,<14.0.0)
|
|
23
|
+
Project-URL: documentation, https://github.com/ajxd2/helldive.py#readme
|
|
24
|
+
Project-URL: homepage, https://github.com/ajxd2/helldive.py
|
|
25
|
+
Project-URL: repository, https://github.com/ajxd2/helldive.py
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Helldive.py
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/helldivepy/)
|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
[](https://pypi.org/project/helldivepy/)
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
> A simple Python library for diving deep into the [Helldivers Community API](https://github.com/helldivers-2/api) and [Diveharder API](https://github.com/helldivers-2/diveharder_api.py).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## ⚙️ Installation
|
|
41
|
+
|
|
42
|
+
To install **Helldive.py**, just use pip:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install helldive.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 🚀 Quickstart
|
|
49
|
+
|
|
50
|
+
Here's a super-quick example to get you diving right in:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import helldivepy
|
|
54
|
+
|
|
55
|
+
client = helldivepy.ApiClient(user_agent="my-app", user_contact="email@example.com")
|
|
56
|
+
|
|
57
|
+
# Get the latest dispatches
|
|
58
|
+
dispatches = client.dispatch.get_dispatches()
|
|
59
|
+
|
|
60
|
+
print(dispatches)
|
|
61
|
+
# Output example
|
|
62
|
+
[
|
|
63
|
+
Dispatch(id=0, published=datetime.datetime, type=0, message='Hello, World 1!'),
|
|
64
|
+
Dispatch(id=1, published=datetime.datetime, type=0, message='Hello, World 2!'),
|
|
65
|
+
Dispatch(id=2, published=datetime.datetime, type=0, message='Hello, World 3!')
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 🌟 Features
|
|
70
|
+
|
|
71
|
+
- **Easy API Access**: Communicate with the Helldivers Community API and Diveharder API without breaking a sweat.
|
|
72
|
+
- **Typed Data**: Get structured, easily readable data like dispatches, planets, and more!
|
|
73
|
+
- **Perfect for Projects**: Ideal for projects, bots, or just exploring Helldivers data.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🛠️ Contributing
|
|
78
|
+
|
|
79
|
+
Contributions are always welcome! If you’d like to make changes, feel free to submit a pull request. For major updates, open an issue first to discuss your ideas.
|
|
80
|
+
|
|
81
|
+
## 📄 License
|
|
82
|
+
|
|
83
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
84
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
helldivepy/__init__.py,sha256=z24Nm7mo0d5KRZfgPnoD-CZL-2SICDfYENZgmNseD5o,59
|
|
2
|
+
helldivepy/api/__init__.py,sha256=uVpko11fzttXInMcfHfgyDVMGF6r-nlOB1isIkl-YZY,354
|
|
3
|
+
helldivepy/api/assignments.py,sha256=-hpvQ2zwc1VAoLHuNPtyRUndTbeMKqqrvjPfEgBxLps,1138
|
|
4
|
+
helldivepy/api/base.py,sha256=Rl4CCemDtYlB-P8p6soUprbAvJxSuCYUR5Wdm1AtWkI,1412
|
|
5
|
+
helldivepy/api/campaigns.py,sha256=w49_uLm13US9rv1Dc0Oz5vweGfLLo9Oiwzv7Rsq5PiA,1272
|
|
6
|
+
helldivepy/api/dispatches.py,sha256=pJLPgqSELSLSkIgpgI09RqQB3wP8cqUZMKYNiz3YkqM,1364
|
|
7
|
+
helldivepy/api/planets.py,sha256=tnComjOJAjSnAgkpT8335IYagbJ0Fi8f093YsNonc1Y,1947
|
|
8
|
+
helldivepy/api/steam.py,sha256=TVHb9kMnya0HTggKdfiR9nUJU_AHUUJSPm4yh_5An50,1026
|
|
9
|
+
helldivepy/api/war.py,sha256=m2Ql6o1hlHDObff-AuzfLSx8jvg38tEWzA-p-pl6le0,700
|
|
10
|
+
helldivepy/api_client.py,sha256=kUn85aOXTNtUeMqU3zNdOrocSxgTANWN6Ac89woAw6E,4821
|
|
11
|
+
helldivepy/constants.py,sha256=Ua5L5SfxoEn5SQNn5j-Mk0xZbJQr96TdiV89vVoCJvs,184
|
|
12
|
+
helldivepy/enums.py,sha256=si8sY-El8pFeJsRdNf3ATwpmF9dzRweWqRhEn5xmNOE,763
|
|
13
|
+
helldivepy/models.py,sha256=BUgC6HfRLPo0XKd-kF_Vtd3e9aT8sdHyWIrL7tkD3Cg,5328
|
|
14
|
+
helldivepy/utils.py,sha256=J8UEBHCD_09LjDXmmcdSNZUbbGkdjw_zEzKxHNQvFq0,1593
|
|
15
|
+
helldivepy-0.1.0.dist-info/LICENSE,sha256=lk4otsNbLY8RPt0j7CvbelwWXeOZsXRIkaublucGr-w,1062
|
|
16
|
+
helldivepy-0.1.0.dist-info/METADATA,sha256=kO3LrG-8SFnnol9NuigRd4n89uxSq_7tTk9nOclk1vg,3093
|
|
17
|
+
helldivepy-0.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
18
|
+
helldivepy-0.1.0.dist-info/RECORD,,
|