Habiticalib 0.3.3__py3-none-any.whl → 0.3.4__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.
habiticalib/__init__.py CHANGED
@@ -33,7 +33,7 @@ from .types import (
33
33
  HabiticaTaskOrderResponse,
34
34
  HabiticaTaskResponse,
35
35
  HabiticaTasksResponse,
36
- HabiticaUserAnonymizedrResponse,
36
+ HabiticaUserAnonymizedResponse,
37
37
  HabiticaUserExport,
38
38
  HabiticaUserResponse,
39
39
  Language,
@@ -82,7 +82,7 @@ __all__ = [
82
82
  "HabiticaTaskOrderResponse",
83
83
  "HabiticaTaskResponse",
84
84
  "HabiticaTasksResponse",
85
- "HabiticaUserAnonymizedrResponse",
85
+ "HabiticaUserAnonymizedResponse",
86
86
  "HabiticaUserExport",
87
87
  "HabiticaUserResponse",
88
88
  "Language",
habiticalib/const.py CHANGED
@@ -1,18 +1,48 @@
1
1
  """Constants for Habiticalib."""
2
2
 
3
- __version__ = "0.3.3"
3
+ __version__ = "0.3.4"
4
4
 
5
5
  DEFAULT_URL = "https://habitica.com/"
6
6
  ASSETS_URL = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/"
7
7
 
8
8
  DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf"
9
9
 
10
- BACKER_ONLY_GEAR = {
10
+ # Assets that doesn't follow name conventions like 2019 Kickstarter gear
11
+ # https://github.com/HabitRPG/habitica/blob/develop/website/client/src/assets/css/sprites.css
12
+ SPECIAL_ASSETS = {
11
13
  "armor_special_ks2019": "BackerOnly-Equip-MythicGryphonArmor.gif",
14
+ "back_special_heroicAureole": "back_special_heroicAureole.gif",
15
+ "background_airship": "background_airship.gif",
16
+ "background_clocktower": "background_clocktower.gif",
17
+ "background_steamworks": "background_steamworks.gif",
18
+ "broad_armor_special_0": "BackerOnly-Equip-ShadeArmor.gif",
19
+ "broad_armor_special_1": "ContributorOnly-Equip-CrystalArmor.gif",
20
+ "broad_armor_special_ks2019": "BackerOnly-Equip-MythicGryphonArmor.gif",
12
21
  "eyewear_special_ks2019": "BackerOnly-Equip-MythicGryphonVisor.gif",
22
+ "head_special_0": "BackerOnly-Equip-ShadeHelmet.gif",
23
+ "head_special_1": "ContributorOnly-Equip-CrystalHelmet.gif",
13
24
  "head_special_ks2019": "BackerOnly-Equip-MythicGryphonHelm.gif",
25
+ "Mount_Body_Gryphon-Gryphatrice": "BackerOnly-Mount-Body-Gryphatrice.gif",
26
+ "Mount_Head_Gryphon-Gryphatrice": "BackerOnly-Mount-Head-Gryphatrice.gif",
27
+ "Pet-Gryphatrice-Jubilant": "Pet-Gryphatrice-Jubilant.gif",
28
+ "Pet-Gryphon-Gryphatrice": "BackerOnly-Pet-Gryphatrice.gif",
29
+ "Pet-Wolf-Cerberus": "BackerOnly-Pet-CerberusPup.gif",
30
+ "shield_special_0": "BackerOnly-Shield-TormentedSkull.gif",
14
31
  "shield_special_ks2019": "BackerOnly-Equip-MythicGryphonShield.gif",
32
+ "slim_armor_special_0": "BackerOnly-Equip-ShadeArmor.gif",
33
+ "slim_armor_special_1": "ContributorOnly-Equip-CrystalArmor.gif",
34
+ "slim_armor_special_ks2019": "BackerOnly-Equip-MythicGryphonArmor.gif",
35
+ "weapon_special_0": "BackerOnly-Weapon-DarkSoulsBlade.gif",
36
+ "weapon_special_critical": "weapon_special_critical.gif",
15
37
  "weapon_special_ks2019": "BackerOnly-Equip-MythicGryphonGlaive.gif",
16
38
  }
17
39
 
40
+ SPECIAL_ASSETS_OFFSET = {
41
+ "head_special_0": (-3, -18),
42
+ "weapon_special_0": (-3, -18),
43
+ "weapon_special_critical": (-12, 12),
44
+ "weapon_special_1": (-12, 0),
45
+ "head_special_1": (0, 3),
46
+ }
47
+
18
48
  PAGE_LIMIT = 60
habiticalib/lib.py CHANGED
@@ -6,6 +6,7 @@ import asyncio
6
6
  from http import HTTPStatus
7
7
  from io import BytesIO
8
8
  import logging
9
+ from operator import add
9
10
  from typing import IO, TYPE_CHECKING, Any, Self
10
11
 
11
12
  from aiohttp import ClientError, ClientResponseError, ClientSession
@@ -13,7 +14,13 @@ from habitipy.aio import HabitipyAsync # type: ignore[import-untyped]
13
14
  from PIL import Image
14
15
  from yarl import URL
15
16
 
16
- from .const import ASSETS_URL, BACKER_ONLY_GEAR, DEFAULT_URL, PAGE_LIMIT
17
+ from .const import (
18
+ ASSETS_URL,
19
+ DEFAULT_URL,
20
+ PAGE_LIMIT,
21
+ SPECIAL_ASSETS,
22
+ SPECIAL_ASSETS_OFFSET,
23
+ )
17
24
  from .exceptions import (
18
25
  BadRequestError,
19
26
  NotAuthorizedError,
@@ -47,7 +54,7 @@ from .types import (
47
54
  HabiticaTaskOrderResponse,
48
55
  HabiticaTaskResponse,
49
56
  HabiticaTasksResponse,
50
- HabiticaUserAnonymizedrResponse,
57
+ HabiticaUserAnonymizedResponse,
51
58
  HabiticaUserExport,
52
59
  HabiticaUserResponse,
53
60
  Language,
@@ -113,7 +120,7 @@ class Habitica:
113
120
  async def _request(self, method: str, url: URL, **kwargs) -> str:
114
121
  """Handle API request."""
115
122
  async with self._session.request(
116
- method,
123
+ method.upper(),
117
124
  url,
118
125
  headers=self._headers,
119
126
  **kwargs,
@@ -255,7 +262,7 @@ class Habitica:
255
262
 
256
263
  async def get_user_anonymized(
257
264
  self,
258
- ) -> HabiticaUserAnonymizedrResponse:
265
+ ) -> HabiticaUserAnonymizedResponse:
259
266
  """Get the authenticated user's anonymized profile.
260
267
 
261
268
  This method retrieves the user's profile data while excluding sensitive
@@ -292,7 +299,7 @@ class Habitica:
292
299
 
293
300
  url = url / "anonymized"
294
301
 
295
- return HabiticaUserAnonymizedrResponse.from_json(
302
+ return HabiticaUserAnonymizedResponse.from_json(
296
303
  await self._request("get", url=url),
297
304
  )
298
305
 
@@ -1808,9 +1815,9 @@ class Habitica:
1808
1815
  -------
1809
1816
  None
1810
1817
  """
1811
- url = URL(ASSETS_URL) / f"{asset}"
1812
- if not url.suffix:
1813
- url = url.with_suffix(".png")
1818
+
1819
+ url = URL(ASSETS_URL) / SPECIAL_ASSETS.get(asset, f"{asset}.png")
1820
+
1814
1821
  try:
1815
1822
  if not (asset_data := self._assets_cache.get(asset)):
1816
1823
  async with self._session.get(url) as r:
@@ -1819,18 +1826,20 @@ class Habitica:
1819
1826
  self._cache_asset(asset, asset_data)
1820
1827
  except ClientResponseError as e:
1821
1828
  _LOGGER.exception(
1822
- "Failed to load %s.png due to error [%s]: %s",
1829
+ "Failed to load %s due to error [%s]: %s",
1823
1830
  asset,
1824
1831
  e.status,
1825
1832
  e.message,
1826
1833
  )
1827
1834
  except ClientError:
1828
1835
  _LOGGER.exception(
1829
- "Failed to load %s.png due to a request error",
1836
+ "Failed to load %s due to a request error",
1830
1837
  asset,
1831
1838
  )
1832
1839
  else:
1833
1840
  fetched_image = Image.open(asset_data).convert("RGBA")
1841
+ if offset := SPECIAL_ASSETS_OFFSET.get(asset):
1842
+ position = tuple(map(add, position, offset))
1834
1843
  image.paste(fetched_image, position, fetched_image)
1835
1844
 
1836
1845
  async def generate_avatar( # noqa: PLR0912, PLR0915
@@ -1895,11 +1904,8 @@ class Habitica:
1895
1904
  )
1896
1905
  gear = getattr(gear_set, gear_type)
1897
1906
  if gear and gear != f"{gear_type}_base_0":
1898
- # 2019 Kickstarter gear doesn't follow name conventions
1899
- if special_ks2019 := BACKER_ONLY_GEAR.get(gear):
1900
- gear = special_ks2019
1901
1907
  # armor has slim and broad size options
1902
- elif gear_type == "armor":
1908
+ if gear_type == "armor":
1903
1909
  gear = f"{preferences.size}_{gear}"
1904
1910
  await self.paste_image(image, gear, (24, mount_offset_y))
1905
1911
 
@@ -1928,7 +1934,7 @@ class Habitica:
1928
1934
  ):
1929
1935
  if stats.buffs.spookySparkles:
1930
1936
  await self.paste_image(image, "ghost", (24, mount_offset_y))
1931
- if stats.buffs.shinySeed:
1937
+ if stats.buffs.snowball:
1932
1938
  await self.paste_image(
1933
1939
  image,
1934
1940
  f"avatar_snowball_{stats.Class}",
@@ -1961,7 +1967,7 @@ class Habitica:
1961
1967
  await self.paste_image(
1962
1968
  image,
1963
1969
  f"chair_{preferences.chair}",
1964
- (24, 0),
1970
+ (24, mount_offset_y),
1965
1971
  )
1966
1972
 
1967
1973
  # Fetch and paste the back accessory
@@ -1970,7 +1976,7 @@ class Habitica:
1970
1976
  # Fetch and paste the skin
1971
1977
  await self.paste_image(
1972
1978
  image,
1973
- f"skin_{preferences.skin}{"_sleep" if preferences.sleep else ""}",
1979
+ f"skin_{preferences.skin}{'_sleep' if preferences.sleep else ''}",
1974
1980
  (24, mount_offset_y),
1975
1981
  )
1976
1982
 
habiticalib/types.py CHANGED
@@ -903,6 +903,7 @@ class Task(TypedDict("Task", {"type": NotRequired[TaskType]}), total=True):
903
903
  weeksOfMonth: NotRequired[list[int]]
904
904
  completed: NotRequired[bool]
905
905
  streak: NotRequired[int]
906
+ value: NotRequired[float]
906
907
 
907
908
 
908
909
  @dataclass(kw_only=True)
@@ -914,7 +915,7 @@ class TaskData:
914
915
  Type: TaskType | None = field(default=None, metadata=field_options(alias="type"))
915
916
  text: str | None = None
916
917
  notes: str | None = None
917
- tags: list[UUID] | None = None
918
+ tags: list[UUID] = field(default_factory=list)
918
919
  value: float | None = None
919
920
  priority: TaskPriority | None = None
920
921
  attribute: Attributes | None = None
@@ -929,7 +930,7 @@ class TaskData:
929
930
  counterUp: int | None = None
930
931
  counterDown: int | None = None
931
932
  frequency: Frequency | None = None
932
- history: list[EntryHistory] | None = None
933
+ history: list[EntryHistory] = field(default_factory=list)
933
934
  alias: str | None = None
934
935
  everyX: int | None = None
935
936
  startDate: datetime | None = None
@@ -1059,7 +1060,7 @@ class UserAnonymizedData:
1059
1060
 
1060
1061
 
1061
1062
  @dataclass(kw_only=True)
1062
- class HabiticaUserAnonymizedrResponse(DataClassORJSONMixin):
1063
+ class HabiticaUserAnonymizedResponse(DataClassORJSONMixin):
1063
1064
  """Representation of a anonymized user data export."""
1064
1065
 
1065
1066
  data: UserAnonymizedData
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Habiticalib
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Asynchronous Python client library for the Habitica API
5
5
  Project-URL: Documentation, https://tr4nt0r.github.io/habiticalib/
6
6
  Project-URL: Source, https://github.com/tr4nt0r/habiticalib
@@ -0,0 +1,12 @@
1
+ habiticalib/__init__.py,sha256=YDYsEGMIxZftYU9YkNC0tyvJrkxn2lXV8WcVQoOXyvA,2415
2
+ habiticalib/const.py,sha256=HP5xKY09-GES72btrXbI0XUFSrCBCqXdn2JPwfOR0vs,2308
3
+ habiticalib/exceptions.py,sha256=oVFCGbHkVn0UpIKIPZPzXfvzs9US4R05ebdEn6cOpqM,1350
4
+ habiticalib/ha.py,sha256=rSzrs7ixLJH3ZtOFlcuKV2t1ZndT0VpuPhDsGqorkOs,20757
5
+ habiticalib/helpers.py,sha256=IRZLYWkDVLI0iVBgBMmvZ6L83KCUl-CnzGhUR_tP6Fg,4576
6
+ habiticalib/lib.py,sha256=YxnlRwCsSlmrdvVu06TKRa6SgnQj2fh2LY8JM7HRqq8,73610
7
+ habiticalib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ habiticalib/types.py,sha256=GS3GOlSY31SrJKcuci__drYewhJpi4jfPmAHapPijrg,43359
9
+ habiticalib-0.3.4.dist-info/METADATA,sha256=9DSGPmVo5IqO1hR1Z47oPP1xMiEsiCZpdMT3AGvs448,4207
10
+ habiticalib-0.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ habiticalib-0.3.4.dist-info/licenses/LICENSE,sha256=oIinIOSJ49l1iVIRI3XGXFWt6SF7a83kEFBAY8ORwNI,1084
12
+ habiticalib-0.3.4.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- habiticalib/__init__.py,sha256=_kmQ0MsCxEqMz-gqguxljTQZTOcy5HA1FSHJQ0rGbS8,2417
2
- habiticalib/const.py,sha256=OirK5B2U4uTdxJ92W8yW2TL0A06NDLOhfGI7yG6mmb4,624
3
- habiticalib/exceptions.py,sha256=oVFCGbHkVn0UpIKIPZPzXfvzs9US4R05ebdEn6cOpqM,1350
4
- habiticalib/ha.py,sha256=rSzrs7ixLJH3ZtOFlcuKV2t1ZndT0VpuPhDsGqorkOs,20757
5
- habiticalib/helpers.py,sha256=IRZLYWkDVLI0iVBgBMmvZ6L83KCUl-CnzGhUR_tP6Fg,4576
6
- habiticalib/lib.py,sha256=K473mN43nN8Fb27Qxw3_yVNLec-aeYws1_StsjXCmfo,73627
7
- habiticalib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- habiticalib/types.py,sha256=BzkVbZ2ewQlvs5jSSc1pXkkW_Qc2is2dQ2VLQVloRyI,43298
9
- habiticalib-0.3.3.dist-info/METADATA,sha256=mVY3CwicMSDHar-3MWQZdusoHrWxXsxTTqTdgSaBh6o,4207
10
- habiticalib-0.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- habiticalib-0.3.3.dist-info/licenses/LICENSE,sha256=oIinIOSJ49l1iVIRI3XGXFWt6SF7a83kEFBAY8ORwNI,1084
12
- habiticalib-0.3.3.dist-info/RECORD,,