Update client.
This commit is contained in:
		
							parent
							
								
									153d1b2ca5
								
							
						
					
					
						commit
						246cdf51fa
					
				| @ -1,28 +1,124 @@ | |||||||
| from typing import Literal, Optional | from __future__ import annotations | ||||||
|  | from typing import Any, Dict, List, Literal, Optional, TypedDict, Union | ||||||
|  | 
 | ||||||
| import aiohttp | import aiohttp | ||||||
| from enum import Enum | from enum import Enum | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class VoteReason(Enum): | class VoteReason(Enum): | ||||||
|  |     """Enumeration for reasons when down-voting a rant or comment.""" | ||||||
|     NOT_FOR_ME = 0 |     NOT_FOR_ME = 0 | ||||||
|     REPOST = 1 |     REPOST = 1 | ||||||
|     OFFENSIVE_SPAM = 2 |     OFFENSIVE_SPAM = 2 | ||||||
| 
 | 
 | ||||||
|  | # --- TypedDicts for API Responses --- | ||||||
|  | 
 | ||||||
|  | class AuthToken(TypedDict): | ||||||
|  |     id: int | ||||||
|  |     key: str | ||||||
|  |     expire_time: int | ||||||
|  |     user_id: int | ||||||
|  | 
 | ||||||
|  | class LoginResponse(TypedDict): | ||||||
|  |     success: bool | ||||||
|  |     auth_token: AuthToken | ||||||
|  | 
 | ||||||
|  | class Image(TypedDict): | ||||||
|  |     url: str | ||||||
|  |     width: int | ||||||
|  |     height: int | ||||||
|  | 
 | ||||||
|  | class UserAvatar(TypedDict): | ||||||
|  |     b: str # background color | ||||||
|  |     i: NotRequired[str] # image identifier | ||||||
|  | 
 | ||||||
|  | class Rant(TypedDict): | ||||||
|  |     id: int | ||||||
|  |     text: str | ||||||
|  |     score: int | ||||||
|  |     created_time: int | ||||||
|  |     attached_image: Union[Image, str] | ||||||
|  |     num_comments: int | ||||||
|  |     tags: List[str] | ||||||
|  |     vote_state: int | ||||||
|  |     edited: bool | ||||||
|  |     link: str | ||||||
|  |     rt: int | ||||||
|  |     rc: int | ||||||
|  |     user_id: int | ||||||
|  |     user_username: str | ||||||
|  |     user_score: int | ||||||
|  |     user_avatar: UserAvatar | ||||||
|  |     editable: bool | ||||||
|  | 
 | ||||||
|  | class Comment(TypedDict): | ||||||
|  |     id: int | ||||||
|  |     rant_id: int | ||||||
|  |     body: str | ||||||
|  |     score: int | ||||||
|  |     created_time: int | ||||||
|  |     vote_state: int | ||||||
|  |     user_id: int | ||||||
|  |     user_username: str | ||||||
|  |     user_score: int | ||||||
|  |     user_avatar: UserAvatar | ||||||
|  | 
 | ||||||
|  | class UserProfile(TypedDict): | ||||||
|  |     username: str | ||||||
|  |     score: int | ||||||
|  |     about: str | ||||||
|  |     location: str | ||||||
|  |     created_time: int | ||||||
|  |     skills: str | ||||||
|  |     github: str | ||||||
|  |     website: str | ||||||
|  |     avatar: UserAvatar | ||||||
|  |     content: Dict[str, Dict[str, Union[List[Rant], List[Comment]]]] | ||||||
|  | 
 | ||||||
|  | class Notification(TypedDict): | ||||||
|  |     type: str | ||||||
|  |     rant_id: int | ||||||
|  |     comment_id: int | ||||||
|  |     created_time: int | ||||||
|  |     read: int | ||||||
|  |     uid: int # User ID of the notifier | ||||||
|  |     username: str | ||||||
|  | 
 | ||||||
|  | # --- API Class --- | ||||||
|  | 
 | ||||||
| class Api: | class Api: | ||||||
|  |     """An asynchronous wrapper for the devRant API.""" | ||||||
| 
 | 
 | ||||||
|     base_url = "https://www.devrant.io/api/" |     base_url: str = "https://www.devrant.io/api/" | ||||||
| 
 | 
 | ||||||
|     def __init__(self, username=None, password=None): |     def __init__(self, username: Optional[str] = None, password: Optional[str] = None): | ||||||
|         self.username = username |         """ | ||||||
|         self.password = password |         Initializes the API client. | ||||||
|         self.auth = None |  | ||||||
|         self.app_id = 3 |  | ||||||
|         self.user_id = None |  | ||||||
|         self.token_id = None |  | ||||||
|         self.token_Key = None |  | ||||||
|         self.session = None |  | ||||||
| 
 | 
 | ||||||
|     def patch_auth(self, request_dict=None): |         Args: | ||||||
|         auth_dict = {"app": self.app_id} |             username (Optional[str]): The username for authentication. | ||||||
|  |             password (Optional[str]): The password for authentication. | ||||||
|  |         """ | ||||||
|  |         self.username: Optional[str] = username | ||||||
|  |         self.password: Optional[str] = password | ||||||
|  |         self.auth: Optional[AuthToken] = None | ||||||
|  |         self.app_id: int = 3 | ||||||
|  |         self.user_id: Optional[int] = None | ||||||
|  |         self.token_id: Optional[int] = None | ||||||
|  |         self.token_key: Optional[str] = None | ||||||
|  |         self.session: Optional[aiohttp.ClientSession] = None | ||||||
|  | 
 | ||||||
|  |     def patch_auth(self, request_dict: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: | ||||||
|  |         """ | ||||||
|  |         Adds authentication details to a request dictionary. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             request_dict (Optional[Dict[str, Any]]): The dictionary to patch. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             Dict[str, Any]: The patched dictionary with auth details. | ||||||
|  |         """ | ||||||
|  |         auth_dict: Dict[str, Any] = {"app": self.app_id} | ||||||
|         if self.auth: |         if self.auth: | ||||||
|             auth_dict.update( |             auth_dict.update( | ||||||
|                 user_id=self.user_id, token_id=self.token_id, token_key=self.token_key |                 user_id=self.user_id, token_id=self.token_id, token_key=self.token_key | ||||||
| @ -32,12 +128,40 @@ class Api: | |||||||
|         request_dict.update(auth_dict) |         request_dict.update(auth_dict) | ||||||
|         return request_dict |         return request_dict | ||||||
| 
 | 
 | ||||||
|     def patch_url(self, url: str): |     def patch_url(self, url: str) -> str: | ||||||
|  |         """ | ||||||
|  |         Constructs the full API URL for an endpoint. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             url (str): The endpoint path. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             str: The full API URL. | ||||||
|  |         """ | ||||||
|         return self.base_url.rstrip("/") + "/" + url.lstrip("/") |         return self.base_url.rstrip("/") + "/" + url.lstrip("/") | ||||||
| 
 | 
 | ||||||
|     async def login(self): |     async def login(self) -> bool: | ||||||
|  |         """ | ||||||
|  |         Authenticates the user and stores the auth token. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if login is successful, False otherwise. | ||||||
|  |          | ||||||
|  |         Response Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |           "success": true, | ||||||
|  |           "auth_token": { | ||||||
|  |             "id": int,          // Token ID | ||||||
|  |             "key": "string",    // Token key | ||||||
|  |             "expire_time": int, // Unix timestamp of token expiration | ||||||
|  |             "user_id": int      // ID of the authenticated user | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         if not self.username or not self.password: |         if not self.username or not self.password: | ||||||
|             raise Exception("No authentication defails supplied.") |             raise Exception("No authentication details supplied.") | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.post( |             response = await session.post( | ||||||
|                 url=self.patch_url("users/auth-token"), |                 url=self.patch_url("users/auth-token"), | ||||||
| @ -47,7 +171,7 @@ class Api: | |||||||
|                     "app": self.app_id, |                     "app": self.app_id, | ||||||
|                 }, |                 }, | ||||||
|             ) |             ) | ||||||
|             obj = await response.json() |             obj: LoginResponse = await response.json() | ||||||
|             if not obj.get("success"): |             if not obj.get("success"): | ||||||
|                 return False |                 return False | ||||||
|             self.auth = obj.get("auth_token") |             self.auth = obj.get("auth_token") | ||||||
| @ -56,22 +180,46 @@ class Api: | |||||||
|             self.user_id = self.auth.get("user_id") |             self.user_id = self.auth.get("user_id") | ||||||
|             self.token_id = self.auth.get("id") |             self.token_id = self.auth.get("id") | ||||||
|             self.token_key = self.auth.get("key") |             self.token_key = self.auth.get("key") | ||||||
|         return self.auth and True or False |         return bool(self.auth) | ||||||
| 
 | 
 | ||||||
|     async def ensure_login(self): |     async def ensure_login(self) -> bool: | ||||||
|  |         """Ensures the user is logged in before making a request.""" | ||||||
|         if not self.auth: |         if not self.auth: | ||||||
|             return await self.login() |             return await self.login() | ||||||
|         return True |         return True | ||||||
| 
 | 
 | ||||||
|     async def __aenter__(self): |     async def __aenter__(self) -> aiohttp.ClientSession: | ||||||
|  |         """Asynchronous context manager entry.""" | ||||||
|         self.session = aiohttp.ClientSession() |         self.session = aiohttp.ClientSession() | ||||||
|         return self.session |         return self.session | ||||||
| 
 | 
 | ||||||
|     async def __aexit__(self, *args, **kwargs): |     async def __aexit__(self, *args: Any, **kwargs: Any) -> None: | ||||||
|         await self.session.close() |         """Asynchronous context manager exit.""" | ||||||
|  |         if self.session and not self.session.closed: | ||||||
|  |             await self.session.close() | ||||||
|         self.session = None |         self.session = None | ||||||
| 
 | 
 | ||||||
|     async def register_user(self, email, username, password): |     async def register_user(self, email: str, username: str, password: str) -> bool: | ||||||
|  |         """ | ||||||
|  |         Registers a new user. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             email (str): The user's email address. | ||||||
|  |             username (str): The desired username. | ||||||
|  |             password (str): The desired password. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True on successful registration, False otherwise. | ||||||
|  |          | ||||||
|  |         Failure Response Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |           "success": false, | ||||||
|  |           "error": "Error message string.", | ||||||
|  |           "error_field": "field_name" // e.g., "username" or "email" | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         response = None |         response = None | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.post( |             response = await session.post( | ||||||
| @ -88,13 +236,37 @@ class Api: | |||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         return obj.get('success', False) |         return obj.get('success', False) | ||||||
| 
 | 
 | ||||||
|     async def get_comments_from_user(self, username): |     async def get_comments_from_user(self, username: str) -> List[Comment]: | ||||||
|  |         """ | ||||||
|  |         Fetches all comments posted by a specific user by first fetching their profile. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             username (str): The username of the user. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             List[Comment]: A list of comment objects. The structure of each comment | ||||||
|  |                            is inferred from the general API design. | ||||||
|  |         """ | ||||||
|         user_id = await self.get_user_id(username) |         user_id = await self.get_user_id(username) | ||||||
|  |         if not user_id: | ||||||
|  |             return [] | ||||||
|         profile = await self.get_profile(user_id) |         profile = await self.get_profile(user_id) | ||||||
|  |         if not profile: | ||||||
|  |             return [] | ||||||
|  |         # The API nests content twice | ||||||
|         return profile.get("content", {}).get("content", {}).get("comments", []) |         return profile.get("content", {}).get("content", {}).get("comments", []) | ||||||
| 
 | 
 | ||||||
|     async def post_comment(self, rant_id, comment): |     async def post_comment(self, rant_id: int, comment: str) -> bool: | ||||||
|         response = None |         """ | ||||||
|  |         Posts a comment on a specific rant. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             rant_id (int): The ID of the rant to comment on. | ||||||
|  |             comment (str): The content of the comment. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if the comment was posted successfully, False otherwise. | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return False |             return False | ||||||
|         async with self as session: |         async with self as session: | ||||||
| @ -105,11 +277,19 @@ class Api: | |||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         return obj.get("success", False) |         return obj.get("success", False) | ||||||
| 
 | 
 | ||||||
|     async def get_comment(self, id_): |     async def get_comment(self, id_: int) -> Optional[Comment]: | ||||||
|         response = None |         """ | ||||||
|  |         Retrieves a single comment by its ID. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             id_ (int): The ID of the comment. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             Optional[Comment]: A dictionary representing the comment, or None if not found. | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url("comments/" + str(id_)), params=self.patch_auth() |                 url=self.patch_url(f"comments/{id_}"), params=self.patch_auth() | ||||||
|             ) |             ) | ||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
| 
 | 
 | ||||||
| @ -118,19 +298,58 @@ class Api: | |||||||
| 
 | 
 | ||||||
|         return obj.get("comment") |         return obj.get("comment") | ||||||
|      |      | ||||||
|     async def delete_comment(self, id_): |     async def delete_comment(self, id_: int) -> bool: | ||||||
|         response = None |         """ | ||||||
|  |         Deletes a comment by its ID. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             id_ (int): The ID of the comment to delete. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if deletion was successful, False otherwise. | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return False |             return False | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.delete( |             response = await session.delete( | ||||||
|                 url=self.patch_url("comments/" + str(id_)), params=self.patch_auth() |                 url=self.patch_url(f"comments/{id_}"), params=self.patch_auth() | ||||||
|             ) |             ) | ||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         return obj.get("success", False) |         return obj.get("success", False) | ||||||
| 
 | 
 | ||||||
|     async def get_profile(self, id_): |     async def get_profile(self, id_: int) -> Optional[UserProfile]: | ||||||
|         response = None |         """ | ||||||
|  |         Retrieves the profile of a user by their ID. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             id_ (int): The user's ID. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             Optional[UserProfile]: A dictionary with the user's profile data. | ||||||
|  |          | ||||||
|  |         Profile Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |           "username": "string", | ||||||
|  |           "score": int, | ||||||
|  |           "about": "string", | ||||||
|  |           "location": "string", | ||||||
|  |           "created_time": int, | ||||||
|  |           "skills": "string", | ||||||
|  |           "github": "string", | ||||||
|  |           "website": "string", | ||||||
|  |           "avatar": { "b": "hex_color", "i": "image_id" }, | ||||||
|  |           "content": { | ||||||
|  |             "content": { | ||||||
|  |               "rants": [ RantObject, ... ], | ||||||
|  |               "upvoted": [ RantObject, ... ], | ||||||
|  |               "comments": [ CommentObject, ... ], | ||||||
|  |               "favorites": [ RantObject, ... ] | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url(f"users/{id_}"), params=self.patch_auth() |                 url=self.patch_url(f"users/{id_}"), params=self.patch_auth() | ||||||
| @ -140,7 +359,16 @@ class Api: | |||||||
|             return None |             return None | ||||||
|         return obj.get("profile") |         return obj.get("profile") | ||||||
| 
 | 
 | ||||||
|     async def search(self, term): |     async def search(self, term: str) -> List[Rant]: | ||||||
|  |         """ | ||||||
|  |         Searches for rants based on a search term. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             term (str): The term to search for. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             List[Rant]: A list of rant objects from the search results. | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url("devrant/search"), |                 url=self.patch_url("devrant/search"), | ||||||
| @ -148,11 +376,29 @@ class Api: | |||||||
|             ) |             ) | ||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         if not obj.get("success"): |         if not obj.get("success"): | ||||||
|             return |             return [] | ||||||
|         return obj.get("results", []) |         return obj.get("results", []) | ||||||
| 
 | 
 | ||||||
|     async def get_rant(self, id): |     async def get_rant(self, id: int) -> Dict[str, Any]: | ||||||
|         response = None |         """ | ||||||
|  |         Retrieves a single rant and its comments by ID. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             id (int): The ID of the rant. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             Dict[str, Any]: The full API response object. | ||||||
|  | 
 | ||||||
|  |         Response Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |           "rant": { RantObject }, | ||||||
|  |           "comments": [ CommentObject, ... ], | ||||||
|  |           "success": true, | ||||||
|  |           "subscribed": 0 or 1 | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 self.patch_url(f"devrant/rants/{id}"), |                 self.patch_url(f"devrant/rants/{id}"), | ||||||
| @ -160,8 +406,18 @@ class Api: | |||||||
|             ) |             ) | ||||||
|         return await response.json() |         return await response.json() | ||||||
| 
 | 
 | ||||||
|     async def get_rants(self, sort="recent", limit=20, skip=0): |     async def get_rants(self, sort: str = "recent", limit: int = 20, skip: int = 0) -> List[Rant]: | ||||||
|         response = None |         """ | ||||||
|  |         Fetches a list of rants. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             sort (str): The sorting method ('recent', 'top', 'algo'). | ||||||
|  |             limit (int): The number of rants to return. | ||||||
|  |             skip (int): The number of rants to skip for pagination. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             List[Rant]: A list of rant objects. | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url("devrant/rants"), |                 url=self.patch_url("devrant/rants"), | ||||||
| @ -169,11 +425,27 @@ class Api: | |||||||
|             ) |             ) | ||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         if not obj.get("success"): |         if not obj.get("success"): | ||||||
|             return |             return [] | ||||||
|         return obj.get("rants", []) |         return obj.get("rants", []) | ||||||
| 
 | 
 | ||||||
|     async def get_user_id(self, username): |     async def get_user_id(self, username: str) -> Optional[int]: | ||||||
|         response = None |         """ | ||||||
|  |         Retrieves a user's ID from their username. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             username (str): The username to look up. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             Optional[int]: The user's ID, or None if not found. | ||||||
|  |          | ||||||
|  |         Response Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |             "success": true, | ||||||
|  |             "user_id": int | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url("get-user-id"), |                 url=self.patch_url("get-user-id"), | ||||||
| @ -185,15 +457,31 @@ class Api: | |||||||
|         return obj.get("user_id") |         return obj.get("user_id") | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     async def mentions(self): |     async def mentions(self) -> List[Notification]: | ||||||
|  |         """ | ||||||
|  |         Fetches notifications where the user was mentioned. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             List[Notification]: A list of mention notification objects. | ||||||
|  |         """ | ||||||
|  |         notifications = await self.notifs | ||||||
|         return [ |         return [ | ||||||
|             notif for notif in (await self.notifs) if notif["type"] == "comment_mention" |             notif for notif in notifications if notif.get("type") == "comment_mention" | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|     async def update_comment(self, comment_id, comment): |     async def update_comment(self, comment_id: int, comment: str) -> bool: | ||||||
|         response = None |         """ | ||||||
|  |         Updates an existing comment. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             comment_id (int): The ID of the comment to update. | ||||||
|  |             comment (str): The new content of the comment. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if the update was successful, False otherwise. | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return None |             return False | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.post( |             response = await session.post( | ||||||
|                 url=self.patch_url(f"comments/{comment_id}"), |                 url=self.patch_url(f"comments/{comment_id}"), | ||||||
| @ -202,9 +490,20 @@ class Api: | |||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         return obj.get("success", False) |         return obj.get("success", False) | ||||||
| 
 | 
 | ||||||
|     async def vote_rant(self, rant_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None): |     async def vote_rant(self, rant_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None) -> bool: | ||||||
|  |         """ | ||||||
|  |         Casts a vote on a rant. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             rant_id (int): The ID of the rant to vote on. | ||||||
|  |             vote (Literal[-1, 0, 1]): -1 for downvote, 0 to unvote, 1 for upvote. | ||||||
|  |             reason (Optional[VoteReason]): The reason for a downvote. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if the vote was successful, False otherwise. | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return None |             return False | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.post( |             response = await session.post( | ||||||
|                 url=self.patch_url(f"devrant/rants/{rant_id}/vote"), |                 url=self.patch_url(f"devrant/rants/{rant_id}/vote"), | ||||||
| @ -213,9 +512,20 @@ class Api: | |||||||
|         obj = await response.json() |         obj = await response.json() | ||||||
|         return obj.get("success", False) |         return obj.get("success", False) | ||||||
| 
 | 
 | ||||||
|     async def vote_comment(self, comment_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None): |     async def vote_comment(self, comment_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None) -> bool: | ||||||
|  |         """ | ||||||
|  |         Casts a vote on a comment. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             comment_id (int): The ID of the comment to vote on. | ||||||
|  |             vote (Literal[-1, 0, 1]): -1 for downvote, 0 to unvote, 1 for upvote. | ||||||
|  |             reason (Optional[VoteReason]): The reason for a downvote. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             bool: True if the vote was successful, False otherwise. | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return None |             return False | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.post( |             response = await session.post( | ||||||
|                 url=self.patch_url(f"comments/{comment_id}/vote"), |                 url=self.patch_url(f"comments/{comment_id}/vote"), | ||||||
| @ -225,12 +535,35 @@ class Api: | |||||||
|         return obj.get("success", False) |         return obj.get("success", False) | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     async def notifs(self): |     async def notifs(self) -> List[Notification]: | ||||||
|         response = None |         """ | ||||||
|  |         Fetches the user's notification feed. | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             List[Notification]: A list of notification items. | ||||||
|  | 
 | ||||||
|  |         Response Structure: | ||||||
|  |         ```json | ||||||
|  |         { | ||||||
|  |           "success": true, | ||||||
|  |           "data": { | ||||||
|  |             "items": [ NotificationObject, ... ], | ||||||
|  |             "check_time": int, // Timestamp of the check | ||||||
|  |             "username_map": [], // Deprecated or unused | ||||||
|  |             "unread": { | ||||||
|  |               "all": int, "upvotes": int, "mentions": int, | ||||||
|  |               "comments": int, "subs": int, "total": int | ||||||
|  |             }, | ||||||
|  |             "num_unread": int | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         ``` | ||||||
|  |         """ | ||||||
|         if not await self.ensure_login(): |         if not await self.ensure_login(): | ||||||
|             return |             return [] | ||||||
|         async with self as session: |         async with self as session: | ||||||
|             response = await session.get( |             response = await session.get( | ||||||
|                 url=self.patch_url("users/me/notif-feed"), params=self.patch_auth() |                 url=self.patch_url("users/me/notif-feed"), params=self.patch_auth() | ||||||
|             ) |             ) | ||||||
|         return (await response.json()).get("data", {}).get("items", []) |         obj = await response.json() | ||||||
|  |         return obj.get("data", {}).get("items", []) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user