chore: add devrant crawler example project and update README formatting
- Add new examples/crawler directory with Makefile, README, crawler.py, database.py, and main.py - Include devranta-1.1.0 distribution packages and api_test_results.json - Remove trailing whitespace and blank lines from README.md for consistent formatting
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
Metadata-Version: 2.1
|
||||
Metadata-Version: 2.4
|
||||
Name: devranta
|
||||
Version: 1.0.0
|
||||
Version: 1.1.0
|
||||
Summary: Async devRant API client made with aiohttp.
|
||||
Author: retoor
|
||||
Author-email: retoor@molodetz.nl
|
||||
@@ -13,10 +13,7 @@ Requires-Dist: dataset
|
||||
|
||||
# devRanta
|
||||
|
||||
devRanta is an async devrant client written in and for Python.
|
||||
Authentication is only needed for half of the functionality and thus username and password are optional parameters by constructing the main class of this package (Api).
|
||||
|
||||
You can find last packages in tar and wheel format [here](https://retoor.molodetz.nl/retoor/devranta/packages).
|
||||
devRanta is the best async devRant client written in Python. Authentication is only needed for half of the functionality; thus, the username and password are optional parameters when constructing the main class of this package (Api). You can find the latest packages in tar and wheel format [here](https://retoor.molodetz.nl/retoor/devranta/packages).
|
||||
|
||||
## Running
|
||||
```
|
||||
@@ -44,3 +41,274 @@ async def list_rants():
|
||||
See [tests](src/devranta/tests.py) for [examples](src/devranta/tests.py) on how to use.
|
||||
|
||||
|
||||
# devRant API Documentation
|
||||
|
||||
For people wanting to build their own client.
|
||||
|
||||
TODO: document responses.
|
||||
|
||||
## Base URL
|
||||
`https://devrant.com/api`
|
||||
|
||||
## Authentication
|
||||
- Uses `dr_token` cookie with `token_id`, `token_key`, and `user_id`.
|
||||
- Required for endpoints needing user authentication.
|
||||
- `guid`, `plat`, `sid`, `seid` included in requests for session tracking.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### User Management
|
||||
1. **Register User**
|
||||
- **URL**: `/api/users`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3 (constant)
|
||||
- `type`: 1 (constant)
|
||||
- `email`: User email
|
||||
- `username`: User username
|
||||
- `password`: User password
|
||||
- `guid`: Unique identifier (from `getMyGuid`)
|
||||
- `plat`: 3 (constant)
|
||||
- `sid`: Session start time (from `getSessionStartTime`)
|
||||
- `seid`: Session event ID (from `getSessionEventId`)
|
||||
- **Response**: JSON with `success`, `auth_token`, or `error` and `error_field`
|
||||
- **Description**: Creates a new user account.
|
||||
|
||||
2. **Login User**
|
||||
- **URL**: `/api/users/auth-token`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `username`: User username
|
||||
- `password`: User password
|
||||
- `guid`: Unique identifier
|
||||
- `plat`: 3
|
||||
- `sid`: Session start time
|
||||
- `seid`: Session event ID
|
||||
- **Response**: JSON with `success`, `auth_token`, or `error`
|
||||
- **Description**: Authenticates user and returns auth token.
|
||||
|
||||
3. **Edit Profile**
|
||||
- **URL**: `/api/users/me/edit-profile`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`: Token ID
|
||||
- `token_key`: Token key
|
||||
- `user_id`: User ID
|
||||
- `guid`, `plat`, `sid`, `seid`
|
||||
- `profile_about`: User bio
|
||||
- `profile_skills`: User skills
|
||||
- `profile_location`: User location
|
||||
- `profile_website`: User website
|
||||
- `profile_github`: GitHub username
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Updates user profile information.
|
||||
|
||||
4. **Forgot Password**
|
||||
- **URL**: `/api/users/forgot-password`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `username`: User username
|
||||
- `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Initiates password reset process.
|
||||
|
||||
5. **Resend Confirmation Email**
|
||||
- **URL**: `/api/users/me/resend-confirm`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Resends account confirmation email.
|
||||
|
||||
6. **Delete Account**
|
||||
- **URL**: `/api/users/me`
|
||||
- **Method**: DELETE
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Deletes user account.
|
||||
|
||||
7. **Mark News as Read**
|
||||
- **URL**: `/api/users/me/mark-news-read`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `news_id`: News item ID
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Marks a news item as read for logged-in users.
|
||||
|
||||
### Rants
|
||||
1. **Get Rant**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}`
|
||||
- **Method**: GET
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `last_comment_id`: 999999999999 (optional)
|
||||
- `links`: 0 (optional)
|
||||
- **Response**: JSON with `rant` (text, tags)
|
||||
- **Description**: Retrieves a specific rant by ID.
|
||||
|
||||
2. **Post Rant**
|
||||
- **URL**: `/api/devrant/rants`
|
||||
- **Method**: POST
|
||||
- **Parameters** (FormData):
|
||||
- `app`: 3
|
||||
- `rant`: Rant text
|
||||
- `tags`: Comma-separated tags
|
||||
- `token_id`, `token_key`, `user_id`
|
||||
- `type`: Rant type ID
|
||||
- `image`: Optional image file (img/gif)
|
||||
- **Response**: JSON with `success`, `rant_id`, or `error`
|
||||
- **Description**: Creates a new rant.
|
||||
|
||||
3. **Edit Rant**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}`
|
||||
- **Method**: POST
|
||||
- **Parameters** (FormData):
|
||||
- `app`: 3
|
||||
- `rant`: Rant text
|
||||
- `tags`: Comma-separated tags
|
||||
- `token_id`, `token_key`, `user_id`
|
||||
- `image`: Optional image file
|
||||
- **Response**: JSON with `success` or `fail_reason`
|
||||
- **Description**: Updates an existing rant.
|
||||
|
||||
4. **Delete Rant**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}`
|
||||
- **Method**: DELETE
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Deletes a rant.
|
||||
|
||||
5. **Vote on Rant**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}/vote`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `vote`: 1 (upvote), -1 (downvote), 0 (remove vote)
|
||||
- `reason`: Downvote reason ID (required for downvote)
|
||||
- **Response**: JSON with `success` or `confirmed` (false if unverified)
|
||||
- **Description**: Votes on a rant.
|
||||
|
||||
6. **Favorite/Unfavorite Rant**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}/{favorite|unfavorite}`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Favorites or unfavorites a rant.
|
||||
|
||||
7. **Get Rant Feed**
|
||||
- **URL**: `/api/devrant/rants`
|
||||
- **Method**: GET
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `ids`: JSON string of IDs (optional)
|
||||
- **Response**: JSON with `success`, `num_notifs`
|
||||
- **Description**: Retrieves rant feed with notification count.
|
||||
|
||||
### Comments
|
||||
1. **Get Comment**
|
||||
- **URL**: `/api/comments/{comment_id}`
|
||||
- **Method**: GET
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `links`: 0 (optional)
|
||||
- **Response**: JSON with `comment` (body)
|
||||
- **Description**: Retrieves a specific comment by ID.
|
||||
|
||||
2. **Post Comment**
|
||||
- **URL**: `/api/devrant/rants/{rant_id}/comments`
|
||||
- **Method**: POST
|
||||
- **Parameters** (FormData):
|
||||
- `app`: 3
|
||||
- `comment`: Comment text
|
||||
- `token_id`, `token_key`, `user_id`
|
||||
- `image`: Optional image file (img/gif)
|
||||
- **Response**: JSON with `success` or `confirmed` (false if unverified)
|
||||
- **Description**: Posts a comment on a rant.
|
||||
|
||||
3. **Edit Comment**
|
||||
- **URL**: `/api/comments/{comment_id}`
|
||||
- **Method**: POST
|
||||
- **Parameters** (FormData):
|
||||
- `app`: 3
|
||||
- `comment`: Comment text
|
||||
- `token_id`, `token_key`, `user_id`
|
||||
- **Response**: JSON with `success` or `fail_reason`
|
||||
- **Description**: Updates an existing comment.
|
||||
|
||||
4. **Delete Comment**
|
||||
- **URL**: `/api/comments/{comment_id}`
|
||||
- **Method**: DELETE
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Deletes a comment.
|
||||
|
||||
5. **Vote on Comment**
|
||||
- **URL**: `/api/comments/{comment_id}/vote`
|
||||
- **Method**: POST
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `vote`: 1 (upvote), -1 (downvote), 0 (remove vote)
|
||||
- `reason`: Downvote reason ID (required for downvote)
|
||||
- **Response**: JSON with `success` or `confirmed` (false if unverified)
|
||||
- **Description**: Votes on a comment.
|
||||
|
||||
### Notifications
|
||||
1. **Get Notification Feed**
|
||||
- **URL**: `/api/users/me/notif-feed`
|
||||
- **Method**: GET
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- `ext_prof`: 1 (optional)
|
||||
- `last_time`: Last notification check time
|
||||
- **Response**: JSON with `success`, `data` (items, check_time, username_map)
|
||||
- **Description**: Retrieves user notifications.
|
||||
|
||||
2. **Clear Notifications**
|
||||
- **URL**: `/api/users/me/notif-feed`
|
||||
- **Method**: DELETE
|
||||
- **Parameters**:
|
||||
- `app`: 3
|
||||
- `token_id`, `token_key`, `user_id`, `guid`, `plat`, `sid`, `seid`
|
||||
- **Response**: JSON with `success`
|
||||
- **Description**: Clears user notifications.
|
||||
|
||||
## External API
|
||||
- **Beta List Signup**
|
||||
- **URL**: `https://www.hexicallabs.com/api/beta-list`
|
||||
- **Method**: GET (JSONP)
|
||||
- **Parameters**:
|
||||
- `email`: User email
|
||||
- `platform`: Platform name
|
||||
- `app`: 3
|
||||
- **Description**: Signs up user for beta list (external service).
|
||||
|
||||
## Notes
|
||||
- All endpoints expect `app=3` for identification.
|
||||
- Authenticated endpoints require `dr_token` cookie with `token_id`, `token_key`, `user_id`.
|
||||
- `guid`, `plat`, `sid`, `seid` are used for session tracking.
|
||||
- Image uploads use FormData for rants and comments.
|
||||
- Downvotes require a reason ID, prompting a modal if not provided.
|
||||
- Responses typically include `success` boolean; errors include `error` or `fail_reason`.
|
||||
- Cookies (`dr_token`, `dr_guid`, `dr_session_start`, `dr_event_id`, `dr_feed_sort`, `dr_theme`, `dr_rants_viewed`, `dr_stickers_seen`, `rant_type_filters`, `news_seen`) manage state and preferences.
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ setup.cfg
|
||||
src/devranta/__init__.py
|
||||
src/devranta/__main__.py
|
||||
src/devranta/api.py
|
||||
src/devranta/api_plain.py
|
||||
src/devranta/api_requests.py
|
||||
src/devranta/tests.py
|
||||
src/devranta.egg-info/PKG-INFO
|
||||
src/devranta.egg-info/SOURCES.txt
|
||||
|
||||
+46
-133
@@ -29,8 +29,8 @@ class Image(TypedDict):
|
||||
height: int
|
||||
|
||||
class UserAvatar(TypedDict):
|
||||
b: str # background color
|
||||
i: NotRequired[str] # image identifier
|
||||
b: str # background color
|
||||
i: Optional[str] # image identifier
|
||||
|
||||
class Rant(TypedDict):
|
||||
id: int
|
||||
@@ -81,7 +81,7 @@ class Notification(TypedDict):
|
||||
comment_id: int
|
||||
created_time: int
|
||||
read: int
|
||||
uid: int # User ID of the notifier
|
||||
uid: int # User ID of the notifier
|
||||
username: str
|
||||
|
||||
# --- API Class ---
|
||||
@@ -162,7 +162,7 @@ class Api:
|
||||
"""
|
||||
if not self.username or not self.password:
|
||||
raise Exception("No authentication details supplied.")
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url("users/auth-token"),
|
||||
data={
|
||||
@@ -180,7 +180,7 @@ class Api:
|
||||
self.user_id = self.auth.get("user_id")
|
||||
self.token_id = self.auth.get("id")
|
||||
self.token_key = self.auth.get("key")
|
||||
return bool(self.auth)
|
||||
return bool(self.auth)
|
||||
|
||||
async def ensure_login(self) -> bool:
|
||||
"""Ensures the user is logged in before making a request."""
|
||||
@@ -188,17 +188,6 @@ class Api:
|
||||
return await self.login()
|
||||
return True
|
||||
|
||||
async def __aenter__(self) -> aiohttp.ClientSession:
|
||||
"""Asynchronous context manager entry."""
|
||||
self.session = aiohttp.ClientSession()
|
||||
return self.session
|
||||
|
||||
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Asynchronous context manager exit."""
|
||||
if self.session and not self.session.closed:
|
||||
await self.session.close()
|
||||
self.session = None
|
||||
|
||||
async def register_user(self, email: str, username: str, password: str) -> bool:
|
||||
"""
|
||||
Registers a new user.
|
||||
@@ -220,8 +209,7 @@ class Api:
|
||||
}
|
||||
```
|
||||
"""
|
||||
response = None
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url(f"users"),
|
||||
data=self.patch_auth({
|
||||
@@ -231,10 +219,8 @@ class Api:
|
||||
"plat": 3
|
||||
}),
|
||||
)
|
||||
if not response:
|
||||
return False
|
||||
obj = await response.json()
|
||||
return obj.get('success', False)
|
||||
obj = await response.json()
|
||||
return obj.get('success', False)
|
||||
|
||||
async def get_comments_from_user(self, username: str) -> List[Comment]:
|
||||
"""
|
||||
@@ -244,8 +230,7 @@ class Api:
|
||||
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.
|
||||
List[Comment]: A list of comment objects.
|
||||
"""
|
||||
user_id = await self.get_user_id(username)
|
||||
if not user_id:
|
||||
@@ -253,7 +238,6 @@ class Api:
|
||||
profile = await self.get_profile(user_id)
|
||||
if not profile:
|
||||
return []
|
||||
# The API nests content twice
|
||||
return profile.get("content", {}).get("content", {}).get("comments", [])
|
||||
|
||||
async def post_comment(self, rant_id: int, comment: str) -> bool:
|
||||
@@ -269,13 +253,13 @@ class Api:
|
||||
"""
|
||||
if not await self.ensure_login():
|
||||
return False
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url(f"devrant/rants/{rant_id}/comments"),
|
||||
data=self.patch_auth({"comment": comment, "plat": 2}),
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
|
||||
async def get_comment(self, id_: int) -> Optional[Comment]:
|
||||
"""
|
||||
@@ -287,16 +271,12 @@ class Api:
|
||||
Returns:
|
||||
Optional[Comment]: A dictionary representing the comment, or None if not found.
|
||||
"""
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url(f"comments/{id_}"), params=self.patch_auth()
|
||||
)
|
||||
obj = await response.json()
|
||||
|
||||
if not obj.get("success"):
|
||||
return None
|
||||
|
||||
return obj.get("comment")
|
||||
obj = await response.json()
|
||||
return obj.get("comment") if obj.get("success") else None
|
||||
|
||||
async def delete_comment(self, id_: int) -> bool:
|
||||
"""
|
||||
@@ -310,12 +290,12 @@ class Api:
|
||||
"""
|
||||
if not await self.ensure_login():
|
||||
return False
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.delete(
|
||||
url=self.patch_url(f"comments/{id_}"), params=self.patch_auth()
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
|
||||
async def get_profile(self, id_: int) -> Optional[UserProfile]:
|
||||
"""
|
||||
@@ -326,38 +306,13 @@ class Api:
|
||||
|
||||
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 aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url(f"users/{id_}"), params=self.patch_auth()
|
||||
)
|
||||
obj = await response.json()
|
||||
if not obj.get("success"):
|
||||
return None
|
||||
return obj.get("profile")
|
||||
obj = await response.json()
|
||||
return obj.get("profile") if obj.get("success") else None
|
||||
|
||||
async def search(self, term: str) -> List[Rant]:
|
||||
"""
|
||||
@@ -369,15 +324,13 @@ class Api:
|
||||
Returns:
|
||||
List[Rant]: A list of rant objects from the search results.
|
||||
"""
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url("devrant/search"),
|
||||
params=self.patch_auth({"term": term}),
|
||||
)
|
||||
obj = await response.json()
|
||||
if not obj.get("success"):
|
||||
return []
|
||||
return obj.get("results", [])
|
||||
obj = await response.json()
|
||||
return obj.get("results", []) if obj.get("success") else []
|
||||
|
||||
async def get_rant(self, id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
@@ -388,23 +341,13 @@ class Api:
|
||||
|
||||
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 aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
self.patch_url(f"devrant/rants/{id}"),
|
||||
params=self.patch_auth(),
|
||||
)
|
||||
return await response.json()
|
||||
return await response.json()
|
||||
|
||||
async def get_rants(self, sort: str = "recent", limit: int = 20, skip: int = 0) -> List[Rant]:
|
||||
"""
|
||||
@@ -418,15 +361,13 @@ class Api:
|
||||
Returns:
|
||||
List[Rant]: A list of rant objects.
|
||||
"""
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url("devrant/rants"),
|
||||
params=self.patch_auth({"sort": sort, "limit": limit, "skip": skip}),
|
||||
)
|
||||
obj = await response.json()
|
||||
if not obj.get("success"):
|
||||
return []
|
||||
return obj.get("rants", [])
|
||||
obj = await response.json()
|
||||
return obj.get("rants", []) if obj.get("success") else []
|
||||
|
||||
async def get_user_id(self, username: str) -> Optional[int]:
|
||||
"""
|
||||
@@ -437,26 +378,15 @@ class Api:
|
||||
|
||||
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 aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url("get-user-id"),
|
||||
params=self.patch_auth({"username": username}),
|
||||
)
|
||||
obj = await response.json()
|
||||
if not obj.get("success"):
|
||||
return None
|
||||
return obj.get("user_id")
|
||||
obj = await response.json()
|
||||
return obj.get("user_id") if obj.get("success") else None
|
||||
|
||||
@property
|
||||
async def mentions(self) -> List[Notification]:
|
||||
"""
|
||||
Fetches notifications where the user was mentioned.
|
||||
@@ -464,7 +394,7 @@ class Api:
|
||||
Returns:
|
||||
List[Notification]: A list of mention notification objects.
|
||||
"""
|
||||
notifications = await self.notifs
|
||||
notifications = await self.notifs()
|
||||
return [
|
||||
notif for notif in notifications if notif.get("type") == "comment_mention"
|
||||
]
|
||||
@@ -482,13 +412,13 @@ class Api:
|
||||
"""
|
||||
if not await self.ensure_login():
|
||||
return False
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url(f"comments/{comment_id}"),
|
||||
data=self.patch_auth({"comment": comment}),
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
|
||||
async def vote_rant(self, rant_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None) -> bool:
|
||||
"""
|
||||
@@ -504,13 +434,13 @@ class Api:
|
||||
"""
|
||||
if not await self.ensure_login():
|
||||
return False
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url(f"devrant/rants/{rant_id}/vote"),
|
||||
data=self.patch_auth({"vote": vote, "reason": reason.value if reason else None}),
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
|
||||
async def vote_comment(self, comment_id: int, vote: Literal[-1, 0, 1], reason: Optional[VoteReason] = None) -> bool:
|
||||
"""
|
||||
@@ -526,44 +456,27 @@ class Api:
|
||||
"""
|
||||
if not await self.ensure_login():
|
||||
return False
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=self.patch_url(f"comments/{comment_id}/vote"),
|
||||
data=self.patch_auth({"vote": vote, "reason": reason.value if reason else None}),
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
obj = await response.json()
|
||||
return obj.get("success", False)
|
||||
|
||||
@property
|
||||
async def notifs(self) -> List[Notification]:
|
||||
"""
|
||||
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():
|
||||
return []
|
||||
async with self as session:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.get(
|
||||
url=self.patch_url("users/me/notif-feed"), params=self.patch_auth()
|
||||
)
|
||||
obj = await response.json()
|
||||
return obj.get("data", {}).get("items", [])
|
||||
obj = await response.json()
|
||||
return obj.get("data", {}).get("items", [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user