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 ADDED
@@ -0,0 +1,3 @@
1
+ from .api_client import ApiClient
2
+
3
+ __all__ = ["ApiClient"]
@@ -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]
@@ -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)
@@ -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})"
@@ -0,0 +1,3 @@
1
+ OFFICIAL_DIVEHARDER_URL = "https://api.diveharder.com"
2
+ OFFICIAL_COMMUNITY_URL = "https://api.helldivers2.dev"
3
+ FACTIONS = {1: "Humans", 2: "Terminids", 3: "Automaton", 4: "Illuminate"}
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
+ [![PyPI](https://img.shields.io/pypi/v/helldivepy.svg?label=PyPI&color=blue)](https://pypi.org/project/helldivepy/)
31
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/helldivepy?color=brightgreen)
32
+ ![GitHub License](https://img.shields.io/github/license/ajxd2/helldive.py?color=yellow)
33
+ [![Python Versions](https://img.shields.io/pypi/pyversions/helldivepy.svg?color=orange)](https://pypi.org/project/helldivepy/)
34
+ ![GitHub contributors](https://img.shields.io/github/contributors/ajxd2/helldive.py?color=ff69b4)
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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any