add enums, add more stuff

This commit is contained in:
Zomatree
2021-08-30 16:22:16 +01:00
parent 6f50ae16db
commit 013885dfb4
15 changed files with 263 additions and 52 deletions
+94
View File
@@ -34,6 +34,25 @@ DMChannel
.. autoclass:: DMChannel
:members:
GroupDMChannel
~~~~~~~~~~
.. autoclass:: GroupDMChannel
:members:
TextChannel
~~~~~~~~~~
.. autoclass:: TextChannel
:members:
VoiceChannel
~~~~~~~~~~
.. autoclass:: VoiceChannel
:members:
Embed
~~~~~~~~~~
@@ -88,3 +107,78 @@ User
.. autoclass:: User
:members:
Enums
------
The api uses enums to say what variant of something is, these represent those enums
All enums subclass `aenum.Enum`.
.. class:: ChannelType
Specifies the type of channel.
.. attribute:: saved_message
A private channel only you can access.
.. attribute:: direct_message
A private direct message channel between you and another user
.. attribute:: group
A private group channel for messages between a group of users
.. attribute:: text_channel
A text channel in a server
.. attribute:: voice_channel
A voice only channel
.. class:: PresenceType
Specifies what a users presence is
.. attribute:: busy
The user is busy and wont receive notification
.. attribute:: idle
The user is idle
.. attribute:: invisible
The user is invisible, you will never receive this, instead they will appear offline
.. attribute:: online
The user is online
.. attribute:: offline
The user is offline or invisible
.. attribute:: RelationshipType
Specifies the relationship between two users
.. attribute:: blocked
You have blocked them
.. attribute:: blocked_other
They have blocked you
.. attribute:: friend
You are friends with them
.. attribute:: incoming_friend_request
They are sending you a friend request
.. attribute:: none
You have no relationship with them
.. attribute:: outgoing_friend_request
You are sending them a friend request
.. attribute:: user
That user is yourself
+1
View File
@@ -1,2 +1,3 @@
aiohttp==3.7.4.post0
ulid-py==1.1.0
aenum==3.1.0
+1 -1
View File
@@ -1,5 +1,5 @@
from .asset import Asset
from .channel import DMChannel, Channel, GroupDMChannel, PartialTextChannel, SavedMessageChannel, VoiceChannel
from .channel import DMChannel, Channel, GroupDMChannel, SavedMessageChannel, VoiceChannel
from .client import Client
from .embed import Embed
from .errors import HTTPError, RevoltError, ServerError
+43 -8
View File
@@ -2,6 +2,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from .errors import AutumnDisabled
from .enums import AssetType
if TYPE_CHECKING:
from .types import File as FilePayload
from .state import State
@@ -12,22 +15,26 @@ class Asset:
Attributes
-----------
id: str
id: :class:`str`
The id of the asset
tag: str
tag: :class:`str`
The tag of the asset, this corrasponds to where the asset is used
size: int
size: :class:`int`
Amount of bytes in the file
filename: str
filename: :class:`str`
The name of the file
height: Optional[int]
height: Optional[:class:`int`]
The height of the file if it is an image or video
width: Optional[int]
width: Optional[:class:`int`]
The width of the file if it is an image or video
content_type: str
The content type of the file
content_type: :class:`str`
The content type of the file
type: :class:`AssetType`
The type of asset it is
"""
def __init__(self, data: FilePayload, state: State):
self.state = state
self.id = data['_id']
self.tag = data['tag']
self.size = data['size']
@@ -44,6 +51,8 @@ class Asset:
self.content_type = data["content_type"]
self.type = AssetType(metadata["type"])
async def read(self) -> bytes:
"""Reads the files content into bytes"""
...
@@ -57,3 +66,29 @@ class Asset:
The file to write too.
"""
fp.write(await self.read())
@property
def url(self) -> str:
"""Returns the url for the asset
.. note:: This can error if autumn is disabled on the instance of revolt
Returns
--------
:class:`str`
The url
Raises
-------
:class:`AutumnDisabled`
Raises if autumn is disabled
"""
enabled = self.state.api_info["features"]["autumn"]["enabled"]
if not enabled:
raise AutumnDisabled
base_url = self.state.api_info["features"]["autumn"]["url"]
return f"{base_url}/{self.tag}/{self.id}"
+22 -19
View File
@@ -1,12 +1,14 @@
from __future__ import annotations
from .messageable import Messageable
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING
from .messageable import Messageable
from .enums import ChannelType
if TYPE_CHECKING:
from .types import Channel as ChannelPayload
from .types import Channel as ChannelPayload, SavedMessages as SavedMessagesPayload, DMChannel as DMChannelPayload, Group as GroupDMChannelPayload, TextChannel as TextChannelPayload
from .state import State
from .user import User
class Channel:
"""Base class for all channels
@@ -15,7 +17,7 @@ class Channel:
-----------
id: :class:`str`
The id of the channel
channel_type: Literal['SavedMessage', 'DirectMessage', 'Group', 'TextChannel', 'VoiceChannel']
channel_type: ChannelType
The type of the channel
server: Optional[:class:`Server`]
The server the channel is part of
@@ -23,39 +25,40 @@ class Channel:
def __init__(self, data: ChannelPayload, state: State):
self.state = state
self.id = data["_id"]
self.channel_type = data["channel_type"]
self.channel_type = ChannelType(data["channel_type"])
self.server = None
class SavedMessageChannel(Channel, Messageable):
"""The Saved Message Channel"""
def __init__(self, data: ChannelPayload, state: State):
def __init__(self, data: SavedMessagesPayload, state: State):
super().__init__(data, state)
class DMChannel(Channel, Messageable):
"""A DM channel"""
def __init__(self, data: ChannelPayload, state: State):
def __init__(self, data: DMChannelPayload, state: State):
super().__init__(data, state)
class GroupDMChannel(Channel, Messageable):
"""A group DM channel"""
def __init__(self, data: ChannelPayload, state: State):
def __init__(self, data: GroupDMChannelPayload, state: State):
super().__init__(data, state)
self.recipients = cast(list[User], list(filter(bool, [state.get_user(user_id) for user_id in data["recipients"]])))
self.name = data["name"]
self.owner = state.get_user(data["owner"])
class TextChannel(Channel, Messageable):
"""A text channel"""
def __init__(self, data: ChannelPayload, state: State):
def __init__(self, data: TextChannelPayload, state: State):
super().__init__(data, state)
Messageable.__init__(self, state)
class PartialTextChannel(Messageable):
"""A partial text channel, this will appear when the channel isnt cached"""
def __init__(self, channel_id: str, state: State):
super().__init__(state)
self.id = channel_id
self.name = "Unknown Channel"
self.channel_type = "TextChannel"
self.server = None
self.server = state.get_server(data["server"])
self.name = data["name"]
self.description = data.get("description")
last_message_id = data.get("last_message")
self.last_message = state.get_message(last_message_id)
self.last_message_id = last_message_id
class VoiceChannel(Channel):
"""A voice channel"""
+37
View File
@@ -0,0 +1,37 @@
from typing import TYPE_CHECKING
# typing doesnt understand aenum so im pretending its stdlib enum while type checking
if TYPE_CHECKING:
import enum
else:
import aenum as enum
class ChannelType(enum.Enum):
saved_message = "SavedMessage"
direct_message = "DirectMessage"
group = "Group"
text_channel = "TextChannel"
voice_channel = "VoiceChannel"
class PresenceType(enum.Enum):
busy = "Busy"
idle = "Idle"
invisible = "Invisible"
online = "Online"
class RelationshipType(enum.Enum):
blocked = "Blocked"
blocked_other = "BlockedOther"
friend = "Friend"
incoming_friend_request = "Incoming"
none = "None"
outgoing_friend_request = "Outgoing"
user = "User"
class AssetType(enum.Enum):
image = "Image"
video = "Video"
text = "Text"
audio = "Audio"
file = "File"
+6
View File
@@ -6,3 +6,9 @@ class HTTPError(RevoltError):
class ServerError(RevoltError):
"Internal server error"
class FeatureDisabled(RevoltError):
"""Base class for any any feature disabled"""
class AutumnDisabled(FeatureDisabled):
"""The autumn feature is disabled"""
+8 -5
View File
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
from .asset import Asset
from .embed import Embed
from .channel import TextChannel, PartialTextChannel, Messageable
from .channel import Messageable
if TYPE_CHECKING:
from .state import State
@@ -38,13 +38,16 @@ class Message:
self.attachments = [Asset(attachment, state) for attachment in data.get("attachments", [])]
self.embeds = [Embed.from_dict(embed) for embed in data.get("embeds", [])]
channel = state.get_channel(data["channel"]) or PartialTextChannel(data["channel"], state)
channel = state.get_channel(data["channel"])
assert isinstance(channel, Messageable)
self.channel = channel
self.server = self.channel and self.channel.server
if isinstance(self.channel, TextChannel) and self.server:
self.author = state.get_member(self.server.id, data["author"])
if self.server:
author = state.get_member(self.server.id, data["author"])
else:
self.author = state.get_user(data["author"])
author = state.get_user(data["author"])
assert author
self.author = author
+4 -5
View File
@@ -1,16 +1,15 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Optional, cast
from .permissions import Permissions
from .channel import channel_factory
from .role import Role
from .channel import Channel
if TYPE_CHECKING:
from .types import Server as ServerPayload
from .state import State
from .member import Member
from .role import Role
from .channel import Channel
class Server:
"""Represents a server
@@ -33,7 +32,7 @@ class Server:
self._members: dict[str, Member] = {}
self._roles: dict[str, Role] = {role_id: Role(role, role_id, state) for role_id, role in data.get("roles", {}).items()}
channels = [channel_factory(channel, state) for channel in data.get("channels", [])]
channels = cast(list[Channel], list(filter(bool, [state.get_channel(channel_id) for channel_id in data["channels"]])))
self._channels: dict[str, Channel] = {channel.id: channel for channel in channels}
@property
+5
View File
@@ -75,6 +75,11 @@ class State:
self.messages.appendleft(message)
return message
def get_message(self, message_id: str) -> Optional[Message]:
for msg in self.messages:
if msg.id == message_id:
return msg
async def fetch_all_server_members(self):
for server_id in self.servers.keys():
data = await self.http.get_server_members(server_id)
+3 -3
View File
@@ -16,7 +16,7 @@ class SavedMessages(_NonceChannel, BaseChannel):
user: str
channel_type: Literal["SavedMessage"]
class DirectMessage(_NonceChannel, BaseChannel):
class DMChannel(_NonceChannel, BaseChannel):
active: bool
recipients: list[str]
last_message: Message
@@ -42,7 +42,7 @@ class TextChannel(_NonceChannel, _TextChannelOptional, BaseChannel):
server: str
name: str
description: str
last_message: Message
last_message: str
channel_type: Literal["TextChannel"]
class _VoiceChannelOptional(TypedDict, total=False):
@@ -56,4 +56,4 @@ class VoiceChannel(_NonceChannel, _TextChannelOptional, BaseChannel):
description: str
channel_type: Literal["VoiceChannel"]
Channel = Union[SavedMessages, DirectMessage, Group, TextChannel, VoiceChannel]
Channel = Union[SavedMessages, DMChannel, Group, TextChannel, VoiceChannel]
+1 -1
View File
@@ -28,5 +28,5 @@ class Server(TypedDict):
_id: str
owner: str
name: str
channels: list[Channel]
channels: list[str]
default_permissions: Permission
+11 -3
View File
@@ -5,19 +5,27 @@ from typing import TYPE_CHECKING, TypedDict, Literal
if TYPE_CHECKING:
from .file import File
Relation = Literal["Blocked", "BlockedOther", "Friend", "Incoming", "None", "Outgoing", "User"]
class UserBot(TypedDict):
owner: str
class Status(TypedDict, total=False):
class _OptionalStatus(TypedDict, total=False):
text: str
class Status(_OptionalStatus):
presence: Literal["Busy", "Idle", "Invisible", "Online"]
class UserRelation(TypedDict):
status: Relation
_id: str
class _OptionalUser(TypedDict, total=False):
avatar: File
relations: list
relations: list[UserRelation]
badges: int
status: Status
relationship: Literal["Blocked", "BlockedOther", "Friend", "Incoming", "None", "Outgoing", "User"]
relationship: Relation
online: bool
flags: int
bot: UserBot
+24 -5
View File
@@ -1,10 +1,21 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, NamedTuple, Optional
from .asset import Asset
from .enums import RelationshipType, PresenceType
if TYPE_CHECKING:
from .state import State
from .types import User as UserPayload
from .types import User as UserPayload, UserRelation
class Relation(NamedTuple):
type: RelationshipType
user: Optional["User"]
class Status(NamedTuple):
text: Optional[str]
presence: PresenceType
class User:
"""Represents a user
@@ -25,6 +36,8 @@ class User:
Whether or not the user is online
flags: :class:`int`
The user flags
avatar: :class:`Asset`
The users avatar if they have one
"""
__flattern_attributes__ = ("id", "name", "bot", "owner", "badges", "online", "flags")
@@ -45,6 +58,12 @@ class User:
self.online = data.get("online", False)
self.flags = data.get("flags", 0)
# avatar
# relations
# relationship
avatar = data.get("avatar")
self.avatar = Asset(avatar, state) if avatar else None
self.relations = [Relation(RelationshipType(relation["status"]), state.get_user(relation["_id"])) for relation in data.get("relations", [])]
relationship = data.get("relationship")
self.relationship = RelationshipType(relationship) if relationship else None
status = data.get("status")
self.status = Status(status.get("text"), PresenceType(status["presence"])) if status else None
+3 -2
View File
@@ -32,6 +32,7 @@ class WebsocketHandler:
self.dispatch = dispatch
self.state = state
self.websocket: aiohttp.ClientWebSocketResponse
self.loop = asyncio.get_running_loop()
async def send_payload(self, payload: BasePayload):
if use_msgpack:
@@ -42,7 +43,7 @@ class WebsocketHandler:
async def heartbeat(self):
while not self.websocket.closed:
logger.info("Sending hearbeat")
await self.send_payload({"type": "Ping"})
await self.websocket.ping()
await asyncio.sleep(15)
async def send_authenticate(self):
@@ -104,4 +105,4 @@ class WebsocketHandler:
else:
payload = json.loads(msg.data)
await self.handle_event(payload)
self.loop.create_task(self.handle_event(payload))