Habiticalib 0.1.0a2__py3-none-any.whl → 0.2.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.
- habiticalib/__init__.py +34 -2
- habiticalib/const.py +3 -1
- habiticalib/exceptions.py +13 -4
- habiticalib/helpers.py +25 -1
- habiticalib/lib.py +508 -25
- habiticalib/types.py +488 -66
- {habiticalib-0.1.0a2.dist-info → habiticalib-0.2.0.dist-info}/METADATA +3 -3
- habiticalib-0.2.0.dist-info/RECORD +11 -0
- {habiticalib-0.1.0a2.dist-info → habiticalib-0.2.0.dist-info}/WHEEL +1 -1
- habiticalib-0.1.0a2.dist-info/RECORD +0 -11
- {habiticalib-0.1.0a2.dist-info → habiticalib-0.2.0.dist-info}/licenses/LICENSE +0 -0
    
        habiticalib/lib.py
    CHANGED
    
    | @@ -6,29 +6,40 @@ import asyncio | |
| 6 6 | 
             
            from http import HTTPStatus
         | 
| 7 7 | 
             
            from io import BytesIO
         | 
| 8 8 | 
             
            import logging
         | 
| 9 | 
            -
            from typing import IO, TYPE_CHECKING, Self
         | 
| 9 | 
            +
            from typing import IO, TYPE_CHECKING, Any, Self
         | 
| 10 10 |  | 
| 11 11 | 
             
            from aiohttp import ClientError, ClientResponseError, ClientSession
         | 
| 12 | 
            +
            from habitipy.aio import HabitipyAsync  # type: ignore[import-untyped]
         | 
| 12 13 | 
             
            from PIL import Image
         | 
| 13 14 | 
             
            from yarl import URL
         | 
| 14 15 |  | 
| 15 | 
            -
            from .const import ASSETS_URL, BACKER_ONLY_GEAR, DEFAULT_URL
         | 
| 16 | 
            +
            from .const import ASSETS_URL, BACKER_ONLY_GEAR, DEFAULT_URL, PAGE_LIMIT
         | 
| 16 17 | 
             
            from .exceptions import (
         | 
| 17 18 | 
             
                BadRequestError,
         | 
| 18 19 | 
             
                NotAuthorizedError,
         | 
| 19 20 | 
             
                NotFoundError,
         | 
| 20 21 | 
             
                TooManyRequestsError,
         | 
| 21 22 | 
             
            )
         | 
| 22 | 
            -
            from .helpers import  | 
| 23 | 
            +
            from .helpers import (
         | 
| 24 | 
            +
                deserialize_task,
         | 
| 25 | 
            +
                extract_user_styles,
         | 
| 26 | 
            +
                get_user_agent,
         | 
| 27 | 
            +
                get_x_client,
         | 
| 28 | 
            +
                join_fields,
         | 
| 29 | 
            +
            )
         | 
| 23 30 | 
             
            from .types import (
         | 
| 24 31 | 
             
                Attributes,
         | 
| 25 | 
            -
                Class,
         | 
| 26 32 | 
             
                Direction,
         | 
| 33 | 
            +
                HabiticaClass,
         | 
| 27 34 | 
             
                HabiticaClassSystemResponse,
         | 
| 35 | 
            +
                HabiticaContentResponse,
         | 
| 28 36 | 
             
                HabiticaErrorResponse,
         | 
| 37 | 
            +
                HabiticaGroupMembersResponse,
         | 
| 29 38 | 
             
                HabiticaLoginResponse,
         | 
| 39 | 
            +
                HabiticaQuestResponse,
         | 
| 30 40 | 
             
                HabiticaResponse,
         | 
| 31 41 | 
             
                HabiticaScoreResponse,
         | 
| 42 | 
            +
                HabiticaSleepResponse,
         | 
| 32 43 | 
             
                HabiticaStatsResponse,
         | 
| 33 44 | 
             
                HabiticaTagResponse,
         | 
| 34 45 | 
             
                HabiticaTagsResponse,
         | 
| @@ -254,14 +265,15 @@ class Habitica: | |
| 254 265 | 
             
                    self,
         | 
| 255 266 | 
             
                    task_type: TaskFilter | None = None,
         | 
| 256 267 | 
             
                    due_date: datetime | None = None,
         | 
| 257 | 
            -
                ) ->  | 
| 268 | 
            +
                ) -> HabiticaTasksResponse:
         | 
| 258 269 | 
             
                    """Get the authenticated user's tasks.
         | 
| 259 270 |  | 
| 260 271 | 
             
                    Parameters
         | 
| 261 272 | 
             
                    ----------
         | 
| 262 273 | 
             
                    task_type : TaskFilter | None
         | 
| 263 274 | 
             
                        The type of task to retrieve, defined in TaskFilter enum.
         | 
| 264 | 
            -
                        If `None`, all task types will be retrieved  | 
| 275 | 
            +
                        If `None`, all task types will be retrieved except completed to-dos
         | 
| 276 | 
            +
                        (default is None).
         | 
| 265 277 |  | 
| 266 278 | 
             
                    due_date : datetime | None
         | 
| 267 279 | 
             
                        Optional date to use for computing the nextDue field for each returned task.
         | 
| @@ -378,14 +390,16 @@ class Habitica: | |
| 378 390 |  | 
| 379 391 | 
             
                    Examples
         | 
| 380 392 | 
             
                    --------
         | 
| 381 | 
            -
                    >>> new_task = Task( | 
| 393 | 
            +
                    >>> new_task = Task(text="New Task", type=TaskType.TODO ...)
         | 
| 382 394 | 
             
                    >>> create_response = await habitica.create_task(new_task)
         | 
| 383 395 | 
             
                    >>> print(create_response.data)  # Displays the created task information
         | 
| 384 396 | 
             
                    """
         | 
| 385 397 | 
             
                    url = self.url / "api/v3/tasks/user"
         | 
| 386 398 |  | 
| 399 | 
            +
                    json = deserialize_task(task)
         | 
| 400 | 
            +
             | 
| 387 401 | 
             
                    return HabiticaTaskResponse.from_json(
         | 
| 388 | 
            -
                        await self._request("post", url=url, json= | 
| 402 | 
            +
                        await self._request("post", url=url, json=json),
         | 
| 389 403 | 
             
                    )
         | 
| 390 404 |  | 
| 391 405 | 
             
                async def update_task(self, task_id: UUID, task: Task) -> HabiticaTaskResponse:
         | 
| @@ -421,14 +435,16 @@ class Habitica: | |
| 421 435 | 
             
                    Examples
         | 
| 422 436 | 
             
                    --------
         | 
| 423 437 | 
             
                    >>> task_id = UUID("12345678-1234-5678-1234-567812345678")
         | 
| 424 | 
            -
                    >>> updated_task = Task( | 
| 438 | 
            +
                    >>> updated_task = Task(text="Updated Task", ...)
         | 
| 425 439 | 
             
                    >>> update_response = await habitica.update_task(task_id, updated_task)
         | 
| 426 440 | 
             
                    >>> print(update_response.data)  # Displays the updated task information
         | 
| 427 441 | 
             
                    """
         | 
| 428 442 | 
             
                    url = self.url / "api/v3/tasks" / str(task_id)
         | 
| 429 443 |  | 
| 444 | 
            +
                    json = deserialize_task(task)
         | 
| 445 | 
            +
             | 
| 430 446 | 
             
                    return HabiticaTaskResponse.from_json(
         | 
| 431 | 
            -
                        await self._request("put", url=url, json= | 
| 447 | 
            +
                        await self._request("put", url=url, json=json),
         | 
| 432 448 | 
             
                    )
         | 
| 433 449 |  | 
| 434 450 | 
             
                async def delete_task(self, task_id: UUID) -> HabiticaResponse:
         | 
| @@ -445,7 +461,7 @@ class Habitica: | |
| 445 461 | 
             
                    Returns
         | 
| 446 462 | 
             
                    -------
         | 
| 447 463 | 
             
                    HabiticaTaskResponse
         | 
| 448 | 
            -
                        A response  | 
| 464 | 
            +
                        A response containing an empty data object.
         | 
| 449 465 |  | 
| 450 466 | 
             
                    Raises
         | 
| 451 467 | 
             
                    ------
         | 
| @@ -549,7 +565,7 @@ class Habitica: | |
| 549 565 | 
             
                async def get_content(
         | 
| 550 566 | 
             
                    self,
         | 
| 551 567 | 
             
                    language: Language | None = None,
         | 
| 552 | 
            -
                ) ->  | 
| 568 | 
            +
                ) -> HabiticaContentResponse:
         | 
| 553 569 | 
             
                    """
         | 
| 554 570 | 
             
                    Fetch game content from the Habitica API.
         | 
| 555 571 |  | 
| @@ -591,7 +607,7 @@ class Habitica: | |
| 591 607 | 
             
                    if language:
         | 
| 592 608 | 
             
                        params.update({"language": language.value})
         | 
| 593 609 |  | 
| 594 | 
            -
                    return  | 
| 610 | 
            +
                    return HabiticaContentResponse.from_json(
         | 
| 595 611 | 
             
                        await self._request("get", url=url, params=params),
         | 
| 596 612 | 
             
                    )
         | 
| 597 613 |  | 
| @@ -799,14 +815,14 @@ class Habitica: | |
| 799 815 |  | 
| 800 816 | 
             
                async def cast_skill(
         | 
| 801 817 | 
             
                    self,
         | 
| 802 | 
            -
                     | 
| 818 | 
            +
                    skill: Skill,
         | 
| 803 819 | 
             
                    target_id: UUID | None = None,
         | 
| 804 820 | 
             
                ) -> HabiticaUserResponse:
         | 
| 805 821 | 
             
                    """Cast a skill (spell) in Habitica, optionally targeting a specific user, task or party.
         | 
| 806 822 |  | 
| 807 823 | 
             
                    Parameters
         | 
| 808 824 | 
             
                    ----------
         | 
| 809 | 
            -
                     | 
| 825 | 
            +
                    skill : Skill
         | 
| 810 826 | 
             
                        The skill (or spell) to be cast. This should be a valid `Skill` enum value.
         | 
| 811 827 | 
             
                    target_id : UUID, optional
         | 
| 812 828 | 
             
                        The unique identifier of the target for the skill. If the skill does not require a target,
         | 
| @@ -832,23 +848,23 @@ class Habitica: | |
| 832 848 | 
             
                    TimeoutError
         | 
| 833 849 | 
             
                        If the connection times out.
         | 
| 834 850 | 
             
                    """
         | 
| 835 | 
            -
                    url = self.url / "api/v3/class/cast" /  | 
| 851 | 
            +
                    url = self.url / "api/v3/user/class/cast" / skill
         | 
| 836 852 | 
             
                    params = {}
         | 
| 837 853 |  | 
| 838 854 | 
             
                    if target_id:
         | 
| 839 855 | 
             
                        params.update({"targetId": str(target_id)})
         | 
| 840 856 | 
             
                    return HabiticaUserResponse.from_json(
         | 
| 841 | 
            -
                        await self._request("post", url=url,  | 
| 857 | 
            +
                        await self._request("post", url=url, params=params),
         | 
| 842 858 | 
             
                    )
         | 
| 843 859 |  | 
| 844 860 | 
             
                async def toggle_sleep(
         | 
| 845 861 | 
             
                    self,
         | 
| 846 | 
            -
                ) ->  | 
| 862 | 
            +
                ) -> HabiticaSleepResponse:
         | 
| 847 863 | 
             
                    """Toggles the user's sleep mode in Habitica.
         | 
| 848 864 |  | 
| 849 865 | 
             
                    Returns
         | 
| 850 866 | 
             
                    -------
         | 
| 851 | 
            -
                     | 
| 867 | 
            +
                    HabiticaSleepResponse
         | 
| 852 868 | 
             
                        A response object containing the result of the sleep mode toggle,
         | 
| 853 869 | 
             
                        and the new sleep state (True if sleeping, False if not).
         | 
| 854 870 |  | 
| @@ -865,7 +881,7 @@ class Habitica: | |
| 865 881 | 
             
                    """
         | 
| 866 882 | 
             
                    url = self.url / "api/v3/user/sleep"
         | 
| 867 883 |  | 
| 868 | 
            -
                    return  | 
| 884 | 
            +
                    return HabiticaSleepResponse.from_json(await self._request("post", url=url))
         | 
| 869 885 |  | 
| 870 886 | 
             
                async def revive(
         | 
| 871 887 | 
             
                    self,
         | 
| @@ -889,7 +905,7 @@ class Habitica: | |
| 889 905 |  | 
| 890 906 | 
             
                    return HabiticaResponse.from_json(await self._request("post", url=url))
         | 
| 891 907 |  | 
| 892 | 
            -
                async def change_class(self, Class:  | 
| 908 | 
            +
                async def change_class(self, Class: HabiticaClass) -> HabiticaClassSystemResponse:  # noqa: N803
         | 
| 893 909 | 
             
                    """Change the user's class in Habitica.
         | 
| 894 910 |  | 
| 895 911 | 
             
                    This method sends a request to the Habitica API to change the user's class
         | 
| @@ -920,7 +936,7 @@ class Habitica: | |
| 920 936 |  | 
| 921 937 | 
             
                    Examples
         | 
| 922 938 | 
             
                    --------
         | 
| 923 | 
            -
                    >>> new_class =  | 
| 939 | 
            +
                    >>> new_class = HabiticaClass.WARRIOR
         | 
| 924 940 | 
             
                    >>> change_response = await habitica.change_class(new_class)
         | 
| 925 941 | 
             
                    >>> print(change_response.data.stats)  # Displays the user's stats after class change
         | 
| 926 942 | 
             
                    """
         | 
| @@ -1068,7 +1084,7 @@ class Habitica: | |
| 1068 1084 | 
             
                    url = self.url / "api/v3/tags"
         | 
| 1069 1085 |  | 
| 1070 1086 | 
             
                    return HabiticaTagsResponse.from_json(
         | 
| 1071 | 
            -
                        await self._request(" | 
| 1087 | 
            +
                        await self._request("get", url=url),
         | 
| 1072 1088 | 
             
                    )
         | 
| 1073 1089 |  | 
| 1074 1090 | 
             
                async def get_tag(self, tag_id: UUID) -> HabiticaTagResponse:
         | 
| @@ -1106,7 +1122,7 @@ class Habitica: | |
| 1106 1122 | 
             
                    url = self.url / "api/v3/tags" / str(tag_id)
         | 
| 1107 1123 |  | 
| 1108 1124 | 
             
                    return HabiticaTagResponse.from_json(
         | 
| 1109 | 
            -
                        await self._request(" | 
| 1125 | 
            +
                        await self._request("get", url=url),
         | 
| 1110 1126 | 
             
                    )
         | 
| 1111 1127 |  | 
| 1112 1128 | 
             
                async def delete_tag(self, tag_id: UUID) -> HabiticaResponse:
         | 
| @@ -1266,10 +1282,447 @@ class Habitica: | |
| 1266 1282 | 
             
                    url = self.url / "api/v3/reorder-tags"
         | 
| 1267 1283 | 
             
                    json = {"tagId": str(tag_id), "to": to}
         | 
| 1268 1284 |  | 
| 1269 | 
            -
                    return  | 
| 1285 | 
            +
                    return HabiticaResponse.from_json(
         | 
| 1270 1286 | 
             
                        await self._request("post", url=url, json=json),
         | 
| 1271 1287 | 
             
                    )
         | 
| 1272 1288 |  | 
| 1289 | 
            +
                async def get_group_members(
         | 
| 1290 | 
            +
                    self,
         | 
| 1291 | 
            +
                    group_id: UUID | None = None,
         | 
| 1292 | 
            +
                    *,
         | 
| 1293 | 
            +
                    limit: int | None = None,
         | 
| 1294 | 
            +
                    tasks: bool = False,
         | 
| 1295 | 
            +
                    public_fields: bool = False,
         | 
| 1296 | 
            +
                    last_id: UUID | None = None,
         | 
| 1297 | 
            +
                ) -> HabiticaGroupMembersResponse:
         | 
| 1298 | 
            +
                    """Get members of the party or a specific group.
         | 
| 1299 | 
            +
             | 
| 1300 | 
            +
                    This method retrieves a list of members for a party or a specified group
         | 
| 1301 | 
            +
                    from the Habitica API. Additional options allow including tasks or public
         | 
| 1302 | 
            +
                    through results if necessary to collect all members. If the API rate limit is
         | 
| 1303 | 
            +
                    exceeded, the method will pause for the duration specified in the `retry-after`
         | 
| 1304 | 
            +
                    header and retry the request.
         | 
| 1305 | 
            +
             | 
| 1306 | 
            +
             | 
| 1307 | 
            +
                    Parameters
         | 
| 1308 | 
            +
                    ----------
         | 
| 1309 | 
            +
                    group_id : UUID, optional
         | 
| 1310 | 
            +
                        The UUID of the group. Defaults to the user's party if not specified.
         | 
| 1311 | 
            +
                    limit : int, optional
         | 
| 1312 | 
            +
                        Maximum number of members per request (default: 30, max: 60).
         | 
| 1313 | 
            +
                    tasks : bool, optional
         | 
| 1314 | 
            +
                        Whether to include tasks associated with the group.
         | 
| 1315 | 
            +
                    public_fields : bool, optional
         | 
| 1316 | 
            +
                        Whether to include all public fields for group members.
         | 
| 1317 | 
            +
                    last_id : UUID, optional
         | 
| 1318 | 
            +
                        For paginated requests, the UUID of the last member retrieved.
         | 
| 1319 | 
            +
             | 
| 1320 | 
            +
                    Returns
         | 
| 1321 | 
            +
                    -------
         | 
| 1322 | 
            +
                    HabiticaGroupMembersResponse
         | 
| 1323 | 
            +
                        A response object containing the group member data.
         | 
| 1324 | 
            +
             | 
| 1325 | 
            +
                    Raises
         | 
| 1326 | 
            +
                    ------
         | 
| 1327 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1328 | 
            +
                        For HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1329 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1330 | 
            +
                        If the connection to the API fails.
         | 
| 1331 | 
            +
                    aiohttp.ClientError
         | 
| 1332 | 
            +
                        For any other exceptions raised by aiohttp during the request.
         | 
| 1333 | 
            +
                    TimeoutError
         | 
| 1334 | 
            +
                        If the connection times out.
         | 
| 1335 | 
            +
             | 
| 1336 | 
            +
                    Examples
         | 
| 1337 | 
            +
                    --------
         | 
| 1338 | 
            +
                    >>> members_response = await habitica.get_group_members()
         | 
| 1339 | 
            +
                    >>> for member in members_response.data:
         | 
| 1340 | 
            +
                    ...     print(member.profile.name)
         | 
| 1341 | 
            +
                    """
         | 
| 1342 | 
            +
             | 
| 1343 | 
            +
                    if limit is not None and (limit < 1 or limit > PAGE_LIMIT):
         | 
| 1344 | 
            +
                        msg = f"The 'limit' parameter must be between 1 and {PAGE_LIMIT}."
         | 
| 1345 | 
            +
                        raise ValueError(msg)
         | 
| 1346 | 
            +
             | 
| 1347 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1348 | 
            +
                    url = self.url / "api/v3/groups" / group / "members"
         | 
| 1349 | 
            +
             | 
| 1350 | 
            +
                    params: dict[str, str | int] = {}
         | 
| 1351 | 
            +
             | 
| 1352 | 
            +
                    if tasks:
         | 
| 1353 | 
            +
                        params["includeTasks"] = "true"
         | 
| 1354 | 
            +
                    if public_fields:
         | 
| 1355 | 
            +
                        params["includeAllPublicFields"] = "true"
         | 
| 1356 | 
            +
                    if last_id:
         | 
| 1357 | 
            +
                        params["lastId"] = str(last_id)
         | 
| 1358 | 
            +
                    if limit:
         | 
| 1359 | 
            +
                        params["limit"] = limit
         | 
| 1360 | 
            +
             | 
| 1361 | 
            +
                    while True:
         | 
| 1362 | 
            +
                        try:
         | 
| 1363 | 
            +
                            response = HabiticaGroupMembersResponse.from_json(
         | 
| 1364 | 
            +
                                await self._request("get", url=url, params=params),
         | 
| 1365 | 
            +
                            )
         | 
| 1366 | 
            +
                            break
         | 
| 1367 | 
            +
                        except TooManyRequestsError as e:
         | 
| 1368 | 
            +
                            await asyncio.sleep(e.retry_after)
         | 
| 1369 | 
            +
             | 
| 1370 | 
            +
                    if len(response.data) == limit:
         | 
| 1371 | 
            +
                        next_page = await self.get_group_members(
         | 
| 1372 | 
            +
                            group_id=group_id,
         | 
| 1373 | 
            +
                            limit=limit,
         | 
| 1374 | 
            +
                            tasks=tasks,
         | 
| 1375 | 
            +
                            public_fields=public_fields,
         | 
| 1376 | 
            +
                            last_id=response.data[-1].id,
         | 
| 1377 | 
            +
                        )
         | 
| 1378 | 
            +
                        response.data.extend(next_page.data)
         | 
| 1379 | 
            +
             | 
| 1380 | 
            +
                    return response
         | 
| 1381 | 
            +
             | 
| 1382 | 
            +
                async def abort_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1383 | 
            +
                    """Abort an active quest for the party or a specific group.
         | 
| 1384 | 
            +
             | 
| 1385 | 
            +
                    Prematurely terminates an ongoing quest, causing all progress to be lost.
         | 
| 1386 | 
            +
                    The quest scroll will be returned to the owner's inventory.
         | 
| 1387 | 
            +
                    Only the quest leader or group leader is allowed to perform this action.
         | 
| 1388 | 
            +
             | 
| 1389 | 
            +
                    Parameters
         | 
| 1390 | 
            +
                    ----------
         | 
| 1391 | 
            +
                    group_id : UUID, optional
         | 
| 1392 | 
            +
                        The UUID of the specific group whose quest should be aborted.
         | 
| 1393 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1394 | 
            +
             | 
| 1395 | 
            +
                    Returns
         | 
| 1396 | 
            +
                    -------
         | 
| 1397 | 
            +
                    HabiticaQuestResponse
         | 
| 1398 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1399 | 
            +
             | 
| 1400 | 
            +
                    Raises
         | 
| 1401 | 
            +
                    ------
         | 
| 1402 | 
            +
                    NotFoundError
         | 
| 1403 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1404 | 
            +
                    NotAuthorizedError
         | 
| 1405 | 
            +
                        If the user does not have permission to abort the quest.
         | 
| 1406 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1407 | 
            +
                        For HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1408 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1409 | 
            +
                        If the connection to the API fails.
         | 
| 1410 | 
            +
                    aiohttp.ClientError
         | 
| 1411 | 
            +
                        For any other exceptions raised by aiohttp during the request.
         | 
| 1412 | 
            +
                    TimeoutError
         | 
| 1413 | 
            +
                        If the connection times out.
         | 
| 1414 | 
            +
             | 
| 1415 | 
            +
                    Examples
         | 
| 1416 | 
            +
                    --------
         | 
| 1417 | 
            +
                    Abort the party's current quest:
         | 
| 1418 | 
            +
                    >>> response = await habitica.abort_quest()
         | 
| 1419 | 
            +
                    >>> print(response.success)  # True if the quest was successfully aborted.
         | 
| 1420 | 
            +
             | 
| 1421 | 
            +
                    Abort a quest for a specific group:
         | 
| 1422 | 
            +
                    >>> group_id = UUID("12345678-1234-5678-1234-567812345678")
         | 
| 1423 | 
            +
                    >>> response = await habitica.abort_quest(group_id)
         | 
| 1424 | 
            +
                    >>> print(response.success)  # True if the quest was successfully aborted.
         | 
| 1425 | 
            +
                    """
         | 
| 1426 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1427 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/abort"
         | 
| 1428 | 
            +
             | 
| 1429 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1430 | 
            +
                        await self._request("post", url=url),
         | 
| 1431 | 
            +
                    )
         | 
| 1432 | 
            +
             | 
| 1433 | 
            +
                async def accept_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1434 | 
            +
                    """Accept a pending invitation to a quest from the party or a specific group.
         | 
| 1435 | 
            +
             | 
| 1436 | 
            +
                    Allows a user to accept an invitation to participate in a quest within a
         | 
| 1437 | 
            +
                    specified group.
         | 
| 1438 | 
            +
             | 
| 1439 | 
            +
                    Parameters
         | 
| 1440 | 
            +
                    ----------
         | 
| 1441 | 
            +
                    group_id : UUID, optional
         | 
| 1442 | 
            +
                        The UUID of the group for which the quest invitation is being accepted.
         | 
| 1443 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1444 | 
            +
             | 
| 1445 | 
            +
             | 
| 1446 | 
            +
                    Returns
         | 
| 1447 | 
            +
                    -------
         | 
| 1448 | 
            +
                    HabiticaQuestResponse
         | 
| 1449 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1450 | 
            +
             | 
| 1451 | 
            +
                    Raises
         | 
| 1452 | 
            +
                    ------
         | 
| 1453 | 
            +
                    NotFoundError
         | 
| 1454 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1455 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1456 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1457 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1458 | 
            +
                        If the connection to the API fails.
         | 
| 1459 | 
            +
                    aiohttp.ClientError
         | 
| 1460 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1461 | 
            +
                    TimeoutError
         | 
| 1462 | 
            +
                        If the connection to the API times out.
         | 
| 1463 | 
            +
             | 
| 1464 | 
            +
                    Examples
         | 
| 1465 | 
            +
                    --------
         | 
| 1466 | 
            +
                    Accept a pending quest invitation from the party:
         | 
| 1467 | 
            +
                    >>> response = await habitica.accept_quest()
         | 
| 1468 | 
            +
                    >>> print(response.success)  # True if the quest invitation was successfully accepted.
         | 
| 1469 | 
            +
                    """
         | 
| 1470 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1471 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/accept"
         | 
| 1472 | 
            +
             | 
| 1473 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1474 | 
            +
                        await self._request("post", url=url),
         | 
| 1475 | 
            +
                    )
         | 
| 1476 | 
            +
             | 
| 1477 | 
            +
                async def reject_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1478 | 
            +
                    """Reject a pending quest invitation from the party or a specific group.
         | 
| 1479 | 
            +
             | 
| 1480 | 
            +
                    Allows a user to reject an invitation to participate in a quest within a
         | 
| 1481 | 
            +
                    specified group. The user will not join the quest and will be excluded from
         | 
| 1482 | 
            +
                    its progress and rewards.
         | 
| 1483 | 
            +
             | 
| 1484 | 
            +
                    Parameters
         | 
| 1485 | 
            +
                    ----------
         | 
| 1486 | 
            +
                    group_id : UUID, optional
         | 
| 1487 | 
            +
                        The UUID of the group for which the quest invitation is being rejected.
         | 
| 1488 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1489 | 
            +
             | 
| 1490 | 
            +
             | 
| 1491 | 
            +
                    Returns
         | 
| 1492 | 
            +
                    -------
         | 
| 1493 | 
            +
                    HabiticaQuestResponse
         | 
| 1494 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1495 | 
            +
             | 
| 1496 | 
            +
                    Raises
         | 
| 1497 | 
            +
                    ------
         | 
| 1498 | 
            +
                    NotFoundError
         | 
| 1499 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1500 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1501 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1502 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1503 | 
            +
                        If the connection to the API fails.
         | 
| 1504 | 
            +
                    aiohttp.ClientError
         | 
| 1505 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1506 | 
            +
                    TimeoutError
         | 
| 1507 | 
            +
                        If the connection to the API times out.
         | 
| 1508 | 
            +
             | 
| 1509 | 
            +
                    Examples
         | 
| 1510 | 
            +
                    --------
         | 
| 1511 | 
            +
                    Reject a pending quest invitation from the party:
         | 
| 1512 | 
            +
                    >>> response = await habitica.reject_quest()
         | 
| 1513 | 
            +
                    >>> print(response.success)  # True if the quest invitation was successfully rejected.
         | 
| 1514 | 
            +
             | 
| 1515 | 
            +
                    Reject a pending quest invitation from a specific group:
         | 
| 1516 | 
            +
                    >>> group_id = UUID("12345678-1234-5678-1234-567812345678")
         | 
| 1517 | 
            +
                    >>> response = await habitica.reject_quest(group_id)
         | 
| 1518 | 
            +
                    >>> print(response.success)  # True if the quest invitation was successfully rejected.
         | 
| 1519 | 
            +
                    """
         | 
| 1520 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1521 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/reject"
         | 
| 1522 | 
            +
             | 
| 1523 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1524 | 
            +
                        await self._request("post", url=url),
         | 
| 1525 | 
            +
                    )
         | 
| 1526 | 
            +
             | 
| 1527 | 
            +
                async def cancel_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1528 | 
            +
                    """Cancel a pending quest for the party or a specific group.
         | 
| 1529 | 
            +
             | 
| 1530 | 
            +
                    Cancel a quest that has not yet startet. All accepted and pending invitations
         | 
| 1531 | 
            +
                    will be canceled and the quest roll returned to the owner's inventory.
         | 
| 1532 | 
            +
                    Only quest leader or group leader can perform this action.
         | 
| 1533 | 
            +
             | 
| 1534 | 
            +
                    Parameters
         | 
| 1535 | 
            +
                    ----------
         | 
| 1536 | 
            +
                    group_id : UUID, optional
         | 
| 1537 | 
            +
                        The UUID of the group for which the quest is being canceled.
         | 
| 1538 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1539 | 
            +
             | 
| 1540 | 
            +
                    Returns
         | 
| 1541 | 
            +
                    -------
         | 
| 1542 | 
            +
                    HabiticaQuestResponse
         | 
| 1543 | 
            +
                        A response object containing details about the canceled quest.
         | 
| 1544 | 
            +
             | 
| 1545 | 
            +
                    Raises
         | 
| 1546 | 
            +
                    ------
         | 
| 1547 | 
            +
                    NotFoundError
         | 
| 1548 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1549 | 
            +
                    NotAuthorizedError
         | 
| 1550 | 
            +
                        If the user does not have permission to cancel the quest.
         | 
| 1551 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1552 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1553 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1554 | 
            +
                        If the connection to the API fails.
         | 
| 1555 | 
            +
                    aiohttp.ClientError
         | 
| 1556 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1557 | 
            +
                    TimeoutError
         | 
| 1558 | 
            +
                        If the connection to the API times out.
         | 
| 1559 | 
            +
             | 
| 1560 | 
            +
                    Examples
         | 
| 1561 | 
            +
                    --------
         | 
| 1562 | 
            +
                    Cancel a pending quest for the party:
         | 
| 1563 | 
            +
                    >>> response = await habitica.cancel_quest()
         | 
| 1564 | 
            +
                    >>> print(response.success)  # True if the quest was successfully canceled.
         | 
| 1565 | 
            +
                    """
         | 
| 1566 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1567 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/cancel"
         | 
| 1568 | 
            +
             | 
| 1569 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1570 | 
            +
                        await self._request("post", url=url),
         | 
| 1571 | 
            +
                    )
         | 
| 1572 | 
            +
             | 
| 1573 | 
            +
                async def start_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1574 | 
            +
                    """Force-start a quest for the party or a specific group.
         | 
| 1575 | 
            +
             | 
| 1576 | 
            +
                    Begins a quest immediately, bypassing any pending invitations that haven't been
         | 
| 1577 | 
            +
                    accepted or rejected.
         | 
| 1578 | 
            +
                    Only quest leader or group leader can perform this action.
         | 
| 1579 | 
            +
             | 
| 1580 | 
            +
                    Parameters
         | 
| 1581 | 
            +
                    ----------
         | 
| 1582 | 
            +
                    group_id : UUID, optional
         | 
| 1583 | 
            +
                        The UUID of the group for which the quest should be started.
         | 
| 1584 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1585 | 
            +
             | 
| 1586 | 
            +
             | 
| 1587 | 
            +
                    Returns
         | 
| 1588 | 
            +
                    -------
         | 
| 1589 | 
            +
                    HabiticaQuestResponse
         | 
| 1590 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1591 | 
            +
             | 
| 1592 | 
            +
                    Raises
         | 
| 1593 | 
            +
                    ------
         | 
| 1594 | 
            +
                    NotFoundError
         | 
| 1595 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1596 | 
            +
                    NotAuthorizedError
         | 
| 1597 | 
            +
                        If the user does not have permission to start the quest.
         | 
| 1598 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1599 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1600 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1601 | 
            +
                        If the connection to the API fails.
         | 
| 1602 | 
            +
                    aiohttp.ClientError
         | 
| 1603 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1604 | 
            +
                    TimeoutError
         | 
| 1605 | 
            +
                        If the connection to the API times out.
         | 
| 1606 | 
            +
             | 
| 1607 | 
            +
                    Examples
         | 
| 1608 | 
            +
                    --------
         | 
| 1609 | 
            +
                    Cancel a pending quest for the party:
         | 
| 1610 | 
            +
                    >>> response = await habitica.cancel_quest()
         | 
| 1611 | 
            +
                    >>> print(response.success)  # True if the quest was successfully canceled.
         | 
| 1612 | 
            +
                    """
         | 
| 1613 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1614 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/force-start"
         | 
| 1615 | 
            +
             | 
| 1616 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1617 | 
            +
                        await self._request("post", url=url),
         | 
| 1618 | 
            +
                    )
         | 
| 1619 | 
            +
             | 
| 1620 | 
            +
                async def invite_quest(
         | 
| 1621 | 
            +
                    self,
         | 
| 1622 | 
            +
                    group_id: UUID | None = None,
         | 
| 1623 | 
            +
                    *,
         | 
| 1624 | 
            +
                    quest_key: str,
         | 
| 1625 | 
            +
                ) -> HabiticaQuestResponse:
         | 
| 1626 | 
            +
                    """Invite members of the party or a specific group to participate in a quest.
         | 
| 1627 | 
            +
             | 
| 1628 | 
            +
                    Sends invitations for a quest to all eligible members of the specified group.
         | 
| 1629 | 
            +
                    The quest is started when all members accept or reject the invitation.
         | 
| 1630 | 
            +
             | 
| 1631 | 
            +
                    Parameters
         | 
| 1632 | 
            +
                    ----------
         | 
| 1633 | 
            +
                    group_id : UUID, optional
         | 
| 1634 | 
            +
                        The UUID of the group for which the quest invitations should be sent.
         | 
| 1635 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1636 | 
            +
                    quest_key : str
         | 
| 1637 | 
            +
                        The unique key identifying the quest to invite members to.
         | 
| 1638 | 
            +
             | 
| 1639 | 
            +
                    Returns
         | 
| 1640 | 
            +
                    -------
         | 
| 1641 | 
            +
                    HabiticaQuestResponse
         | 
| 1642 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1643 | 
            +
             | 
| 1644 | 
            +
                    Raises
         | 
| 1645 | 
            +
                    ------
         | 
| 1646 | 
            +
                    NotFoundError
         | 
| 1647 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1648 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1649 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1650 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1651 | 
            +
                        If the connection to the API fails.
         | 
| 1652 | 
            +
                    aiohttp.ClientError
         | 
| 1653 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1654 | 
            +
                    TimeoutError
         | 
| 1655 | 
            +
                        If the connection to the API times out.
         | 
| 1656 | 
            +
             | 
| 1657 | 
            +
                    Examples
         | 
| 1658 | 
            +
                    --------
         | 
| 1659 | 
            +
                    Send a quest invitation to the party:
         | 
| 1660 | 
            +
                    >>> response = await habitica.invite_quest(quest_key="dilatory_derby")
         | 
| 1661 | 
            +
                    >>> print(response.success)  # True if invitations were successfully sent.
         | 
| 1662 | 
            +
             | 
| 1663 | 
            +
                    Send a quest invitation to a specific group:
         | 
| 1664 | 
            +
                    >>> group_id = UUID("12345678-1234-5678-1234-567812345678")
         | 
| 1665 | 
            +
                    >>> response = await habitica.invite_quest(group_id, quest_key="golden_knight")
         | 
| 1666 | 
            +
                    >>> print(response.success)  # True if invitations were successfully sent.
         | 
| 1667 | 
            +
                    """
         | 
| 1668 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1669 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/invite" / quest_key
         | 
| 1670 | 
            +
             | 
| 1671 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1672 | 
            +
                        await self._request("post", url=url),
         | 
| 1673 | 
            +
                    )
         | 
| 1674 | 
            +
             | 
| 1675 | 
            +
                async def leave_quest(self, group_id: UUID | None = None) -> HabiticaQuestResponse:
         | 
| 1676 | 
            +
                    """Leave the current quest from the party or a specific group.
         | 
| 1677 | 
            +
             | 
| 1678 | 
            +
                    Allows a user to exit an ongoing quest they are part of. This action removes
         | 
| 1679 | 
            +
                    them from the quest but does not affect its progress for other participants.
         | 
| 1680 | 
            +
                    Users who leave a quest will not contribute to its completion or receive rewards.
         | 
| 1681 | 
            +
             | 
| 1682 | 
            +
                    Parameters
         | 
| 1683 | 
            +
                    ----------
         | 
| 1684 | 
            +
                    group_id : UUID, optional
         | 
| 1685 | 
            +
                        The UUID of the group associated with the quest the user is leaving.
         | 
| 1686 | 
            +
                        Defaults to the user's party if not specified.
         | 
| 1687 | 
            +
                    quest_key : str
         | 
| 1688 | 
            +
                        The unique key identifying the quest to invite members to.
         | 
| 1689 | 
            +
             | 
| 1690 | 
            +
                    Returns
         | 
| 1691 | 
            +
                    -------
         | 
| 1692 | 
            +
                    HabiticaQuestResponse
         | 
| 1693 | 
            +
                        A response object containing updated quest data of the group or party.
         | 
| 1694 | 
            +
             | 
| 1695 | 
            +
                    Raises
         | 
| 1696 | 
            +
                    ------
         | 
| 1697 | 
            +
                    NotFoundError
         | 
| 1698 | 
            +
                        If the specified group or quest could not be found.
         | 
| 1699 | 
            +
                    aiohttp.ClientResponseError
         | 
| 1700 | 
            +
                        Raised for HTTP-related errors, such as HTTP 400 or 500 response status.
         | 
| 1701 | 
            +
                    aiohttp.ClientConnectionError
         | 
| 1702 | 
            +
                        If the connection to the API fails.
         | 
| 1703 | 
            +
                    aiohttp.ClientError
         | 
| 1704 | 
            +
                        Raised for any other exceptions encountered by `aiohttp` during the request.
         | 
| 1705 | 
            +
                    TimeoutError
         | 
| 1706 | 
            +
                        If the connection to the API times out.
         | 
| 1707 | 
            +
             | 
| 1708 | 
            +
                    Examples
         | 
| 1709 | 
            +
                    --------
         | 
| 1710 | 
            +
                    Leave the current quest in the user's party:
         | 
| 1711 | 
            +
                    >>> response = await habitica.leave_quest()
         | 
| 1712 | 
            +
                    >>> print(response.success)  # True if the user successfully left the quest.
         | 
| 1713 | 
            +
             | 
| 1714 | 
            +
                    Leave the current quest in a specific group:
         | 
| 1715 | 
            +
                    >>> group_id = UUID("12345678-1234-5678-1234-567812345678")
         | 
| 1716 | 
            +
                    >>> response = await habitica.leave_quest(group_id)
         | 
| 1717 | 
            +
                    >>> print(response.success)  # True if the user successfully left the quest.
         | 
| 1718 | 
            +
                    """
         | 
| 1719 | 
            +
                    group = "party" if not group_id else str(group_id)
         | 
| 1720 | 
            +
                    url = self.url / "api/v3/groups" / group / "quests/leave"
         | 
| 1721 | 
            +
             | 
| 1722 | 
            +
                    return HabiticaQuestResponse.from_json(
         | 
| 1723 | 
            +
                        await self._request("post", url=url),
         | 
| 1724 | 
            +
                    )
         | 
| 1725 | 
            +
             | 
| 1273 1726 | 
             
                def _cache_asset(self, asset: str, asset_data: IO[bytes]) -> None:
         | 
| 1274 1727 | 
             
                    """Cache an asset and maintain the cache size limit by removing older entries.
         | 
| 1275 1728 |  | 
| @@ -1546,3 +1999,33 @@ class Habitica: | |
| 1546 1999 | 
             
                        image.save(fp, fmt)
         | 
| 1547 2000 |  | 
| 1548 2001 | 
             
                    return user_styles
         | 
| 2002 | 
            +
             | 
| 2003 | 
            +
                async def habitipy(self) -> HabitipyAsync:
         | 
| 2004 | 
            +
                    """Create a Habitipy instance."""
         | 
| 2005 | 
            +
             | 
| 2006 | 
            +
                    _session = self._session
         | 
| 2007 | 
            +
                    _headers = self._headers
         | 
| 2008 | 
            +
                    loop = asyncio.get_running_loop()
         | 
| 2009 | 
            +
             | 
| 2010 | 
            +
                    class HAHabitipyAsync(HabitipyAsync):
         | 
| 2011 | 
            +
                        """Closure API class to hold session."""
         | 
| 2012 | 
            +
             | 
| 2013 | 
            +
                        def __call__(self, **kwargs) -> Any:
         | 
| 2014 | 
            +
                            """Pass session to habitipy."""
         | 
| 2015 | 
            +
                            return super().__call__(_session, **kwargs)
         | 
| 2016 | 
            +
             | 
| 2017 | 
            +
                        def _make_headers(self) -> dict[str, str]:
         | 
| 2018 | 
            +
                            """Inject headers."""
         | 
| 2019 | 
            +
                            headers = super()._make_headers()
         | 
| 2020 | 
            +
                            headers.update(_headers)
         | 
| 2021 | 
            +
                            return headers
         | 
| 2022 | 
            +
             | 
| 2023 | 
            +
                    return await loop.run_in_executor(
         | 
| 2024 | 
            +
                        None,
         | 
| 2025 | 
            +
                        HAHabitipyAsync,
         | 
| 2026 | 
            +
                        {
         | 
| 2027 | 
            +
                            "url": str(self.url),
         | 
| 2028 | 
            +
                            "login": self._headers.get("X-API-USER"),
         | 
| 2029 | 
            +
                            "password": self._headers.get("X-API-KEY"),
         | 
| 2030 | 
            +
                        },  # type: ignore[var-annotated]
         | 
| 2031 | 
            +
                    )
         |