diff --git a/revolt/asset.py b/revolt/asset.py index fc45db2..53cd431 100644 --- a/revolt/asset.py +++ b/revolt/asset.py @@ -55,7 +55,7 @@ class Asset: async def read(self) -> bytes: """Reads the files content into bytes""" - ... + return await self.state.http.request_file(self.url) async def save(self, fp: IOBase): """Reads the files content and saves it to a file diff --git a/revolt/http.py b/revolt/http.py index 4e15a4e..0bb1a5e 100644 --- a/revolt/http.py +++ b/revolt/http.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Coroutine, Optional, TYPE_CHECKING, Literal, TypeVar +from typing import Any, Coroutine, Optional, TYPE_CHECKING, Literal, TypeVar, Union import aiohttp import ulid @@ -14,7 +14,9 @@ except ImportError: if TYPE_CHECKING: import aiohttp - from .types import ApiInfo, Autumn as AutumnPayload, Message as MessagePayload, Embed as EmbedPayload, GetServerMembers + from .types import ( + ApiInfo, Autumn as AutumnPayload, Message as MessagePayload, Embed as EmbedPayload, GetServerMembers, User as UserPayload, + Server, Member, UserProfile, ServerInvite, ServerBans, Channel, DMChannel, TextChannel, VoiceChannel, Role) from .file import File T = TypeVar("T") @@ -48,8 +50,12 @@ class HttpClient: kwargs["headers"] = headers async with self.session.request(method, url, **kwargs) as resp: - response = _json.loads(await resp.text()) - + text = await resp.text() + if text: + response = _json.loads(await resp.text()) + else: + response = text + resp_code = resp.status if 200 <= resp_code <= 300: @@ -99,5 +105,89 @@ class HttpClient: return await self.request("POST", f"/channels/{channel}/messages", json=json) - def get_server_members(self, server_id: str) -> Request[GetServerMembers]: + async def request_file(self, url: str) -> bytes: + async with self.session.get(url) as resp: + return await resp.content.read() + + def fetch_user(self, user_id: str) -> Request[UserPayload]: + return self.request("GET", f"/users/{user_id}") + + def fetch_profile(self, user_id: str) -> Request[UserProfile]: + return self.request("GET", f"/users/{user_id}/profile") + + def fetch_default_avatar(self, user_id: str) -> Request[bytes]: + return self.request_file(f"{self.api_url}/users/{user_id}/default_avatar") + + def fetch_dm_channels(self) -> Request[list[Channel]]: + return self.request("GET", "/users/dms") + + def open_dm(self, user_id: str) -> Request[DMChannel]: + return self.request("GET", f"/users/{user_id}/dm") + + def fetch_channel(self, channel_id: str) -> Request[Channel]: + return self.request("GET", f"/channels/{channel_id}") + + def close_channel(self, channel_id: str) -> Request[None]: + return self.request("DELETE", f"/channels/{channel_id}") + + def fetch_server(self, server_id: str) -> Request[Server]: + return self.request("GET", f"/servers/{server_id}") + + def delete_leave_server(self, server_id: str) -> Request[None]: + return self.request("DELETE", f"/servers/{server_id}") + + def create_channel(self, server_id: str, channel_type: Literal["Text", "Voice"], name: str, description: Optional[str]) -> Request[Union[TextChannel, VoiceChannel]]: + payload = { + "type": channel_type, + "name": name + } + + if description: + payload["description"] = description + + return self.request("POST", f"/servers/{server_id}/channels", json=payload) + + def fetch_server_invites(self, server_id: str) -> Request[list[ServerInvite]]: + return self.request("GET", f"/servers/{server_id}/invites") + + def fetch_member(self, server_id: str, member_id: str) -> Request[Member]: + return self.request("GET", f"/servers/{server_id}/members/{member_id}") + + def kick_member(self, server_id: str, member_id: str) -> Request[None]: + return self.request("DELETE", f"/servers/{server_id}/members/{member_id}") + + def fetch_members(self, server_id: str) -> Request[GetServerMembers]: return self.request("GET", f"/servers/{server_id}/members") + + def ban_member(self, server_id: str, member_id: str, reason: Optional[str]) -> Request[GetServerMembers]: + payload = {"reason": reason} if reason else None + + return self.request("PUT", f"/servers/{server_id}/bans/{member_id}", json=payload, nonce=False) + + def unban_member(self, server_id: str, member_id: str) -> Request[None]: + return self.request("DELETE", f"/servers/{server_id}/bans/{member_id}") + + def fetch_bans(self, server_id: str) -> Request[ServerBans]: + return self.request("GET", f"/servers/{server_id}/bans") + + def set_role_permissions(self, server_id: str, role_id: str, server_permissions: int, channel_permissions: int) -> Request[None]: + payload = { + "server": server_permissions, + "channel": channel_permissions + } + + return self.request("PUT", f"/servers/{server_id}/permissions/{role_id}", json=payload, nonce=False) + + def set_default_permissions(self, server_id: str, server_permissions: int, channel_permissions: int) -> Request[None]: + payload = { + "server": server_permissions, + "channel": channel_permissions + } + + return self.request("PUT", f"/servers/{server_id}/permissions/default", json=payload, nonce=False) + + def create_role(self, server_id: str, name: str) -> Request[Role]: + return self.request("POST", f"/servers/{server_id}/roles", json={"name": name}, nonce=False) + + def delete_role(self, server_id: str, role_id: str) -> Request[None]: + return self.request("DELETE", f"/servers/{server_id}/roles/{role_id}") diff --git a/revolt/state.py b/revolt/state.py index 33341e9..9756748 100644 --- a/revolt/state.py +++ b/revolt/state.py @@ -82,7 +82,7 @@ class State: async def fetch_all_server_members(self): for server_id in self.servers.keys(): - data = await self.http.get_server_members(server_id) + data = await self.http.fetch_members(server_id) for user in data["users"]: self.add_user(user) diff --git a/revolt/types/__init__.py b/revolt/types/__init__.py index b1c34fe..056468f 100644 --- a/revolt/types/__init__.py +++ b/revolt/types/__init__.py @@ -3,6 +3,7 @@ from .embed import * from .file import * from .gateway import * from .http import * +from .invite import * from .member import * from .message import * from .role import * diff --git a/revolt/types/invite.py b/revolt/types/invite.py new file mode 100644 index 0000000..f8849ed --- /dev/null +++ b/revolt/types/invite.py @@ -0,0 +1,16 @@ +from typing import Literal, TypedDict, Union + +class GroupInvite(TypedDict): + type: Literal["Group"] + _id: str + creator: str + channel: str + +class ServerInvite(TypedDict): + type: Literal["Server"] + _id: str + server: str + creator: str + channel: str + +Invite = Union[ServerInvite, GroupInvite] diff --git a/revolt/types/server.py b/revolt/types/server.py index 059587d..37e592e 100644 --- a/revolt/types/server.py +++ b/revolt/types/server.py @@ -1,4 +1,5 @@ from __future__ import annotations +from _typeshed import StrOrBytesPath from typing import TYPE_CHECKING, TypedDict @@ -30,3 +31,20 @@ class Server(TypedDict): name: str channels: list[str] default_permissions: Permission + +class _OptionalBannedUser(TypedDict, total=False): + avatar: File + +class BannedUser(_OptionalBannedUser): + _id: str + username: str + +class _OptionalBan(TypedDict, total=False): + reason: str + +class Ban(_OptionalBan): + _id: str + +class ServerBans(TypedDict): + users: list[BannedUser] + bans: list[Ban] diff --git a/revolt/types/user.py b/revolt/types/user.py index 65dd52b..fabad1f 100644 --- a/revolt/types/user.py +++ b/revolt/types/user.py @@ -31,3 +31,7 @@ class _OptionalUser(TypedDict, total=False): class User(_OptionalUser): _id: str username: str + +class UserProfile(TypedDict, total=False): + content: str + background: File