mirror of
https://github.com/stoatchat/python-client-sdk.git
synced 2026-07-25 16:35:33 -04:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efce2aedac | |||
| ca37846df7 | |||
| 86c07a6d6b | |||
| a8be358339 | |||
| e34ca0afda | |||
| 060e6ea32a | |||
| 2dabb0dbb8 | |||
| 48e0f0fb2e | |||
| bc8d650659 | |||
| d8fde3d0a6 | |||
| d4568c02ea | |||
| ff49f8e3e5 |
@@ -1,5 +1,7 @@
|
||||
# Revolt.py
|
||||
|
||||
> # This project is archived and is no longer receiving updates.
|
||||
|
||||
An async library to interact with the https://revolt.chat API.
|
||||
|
||||
You can join the support server [here](https://rvlt.gg/FDXER6hr) and find the library's documentation [here](https://revoltpy.readthedocs.io/en/latest/).
|
||||
|
||||
Vendored
+8
-2
@@ -148,7 +148,7 @@ API Reference
|
||||
:members:
|
||||
|
||||
Enums
|
||||
======
|
||||
------
|
||||
|
||||
The api uses enums to say what variant of something is,
|
||||
these represent those enums
|
||||
@@ -278,7 +278,7 @@ All enums subclass `aenum.Enum`.
|
||||
The embed is unknown
|
||||
|
||||
Utils
|
||||
======
|
||||
------
|
||||
|
||||
.. currentmodule:: revolt.utils
|
||||
|
||||
@@ -287,3 +287,9 @@ A collection a utility functions and classes to aid in making your bot
|
||||
.. autofunction:: get
|
||||
|
||||
.. autofunction:: client_session
|
||||
|
||||
.. autoclass:: Ulid
|
||||
:members:
|
||||
|
||||
.. autoclass:: Object
|
||||
:members:
|
||||
@@ -19,6 +19,11 @@ Command
|
||||
.. autoclass:: revolt.ext.commands.Command
|
||||
:members:
|
||||
|
||||
Group
|
||||
~~~~~~~~
|
||||
.. autoclass:: revolt.ext.commands.Group
|
||||
:members:
|
||||
|
||||
Cog
|
||||
~~~~
|
||||
.. autoclass:: revolt.ext.commands.Cog
|
||||
@@ -28,6 +33,13 @@ command
|
||||
~~~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.command
|
||||
|
||||
group
|
||||
~~~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.group
|
||||
|
||||
Checks
|
||||
-------
|
||||
|
||||
check
|
||||
~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.check
|
||||
@@ -40,9 +52,62 @@ is_server_owner
|
||||
~~~~~~~~~~~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.is_server_owner
|
||||
|
||||
has_permissions
|
||||
~~~~~~~~~~~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.has_permissions
|
||||
|
||||
has_channel_permissions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. autodecorator:: revolt.ext.commands.has_channel_permissions
|
||||
|
||||
Converters
|
||||
-----------
|
||||
|
||||
IntConverter
|
||||
~~~~~~~~~~~~~
|
||||
Converts the parameter to an int
|
||||
|
||||
BoolConverter
|
||||
~~~~~~~~~~~~~~
|
||||
Converts the parameter to a bool
|
||||
|
||||
CategoryConverter
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Converts the parameter to a category
|
||||
|
||||
UserConverter
|
||||
~~~~~~~~~~~~~~
|
||||
Converts the parameter to a category
|
||||
|
||||
MemberConverter
|
||||
~~~~~~~~~~~~~~~~
|
||||
Converts the parameter to a category
|
||||
|
||||
ChannelConverter
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Converts the parameter to a category
|
||||
|
||||
Greedy
|
||||
~~~~~~
|
||||
Converts the parameter to a greedy parameter which will take as many arguments which convert successfully.
|
||||
|
||||
Allows you to have var-args in the middle of a signature.
|
||||
|
||||
Help Commands
|
||||
--------------
|
||||
|
||||
HelpCommand
|
||||
~~~~~~~~~~~~
|
||||
.. autoclass:: revolt.ext.commands.HelpCommand
|
||||
:members:
|
||||
|
||||
DefaultHelpCommand
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. autoclass:: revolt.ext.commands.DefaultHelpCommand
|
||||
:members:
|
||||
|
||||
Exceptions
|
||||
===========
|
||||
-----------
|
||||
|
||||
CommandError
|
||||
~~~~~~~~~~~~~
|
||||
@@ -79,6 +144,11 @@ ServerOnly
|
||||
.. autoexception:: revolt.ext.commands.ServerOnly
|
||||
:members:
|
||||
|
||||
MissingPermissionsError
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.MissingPermissionsError
|
||||
:members:
|
||||
|
||||
ConverterError
|
||||
~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.ConverterError
|
||||
@@ -99,6 +169,11 @@ CategoryConverterError
|
||||
.. autoexception:: revolt.ext.commands.CategoryConverterError
|
||||
:members:
|
||||
|
||||
ChannelConverterError
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.ChannelConverterError
|
||||
:members:
|
||||
|
||||
UserConverterError
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.UserConverterError
|
||||
@@ -108,3 +183,18 @@ MemberConverterError
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.MemberConverterError
|
||||
:members:
|
||||
|
||||
UnionConverterError
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.UnionConverterError
|
||||
:members:
|
||||
|
||||
MissingSetup
|
||||
~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.MissingSetup
|
||||
:members:
|
||||
|
||||
CommandOnCooldown
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
.. autoexception:: revolt.ext.commands.CommandOnCooldown
|
||||
:members:
|
||||
+1
-1
@@ -16,7 +16,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
]
|
||||
dependencies = [
|
||||
"aiohttp==3.8.*",
|
||||
"aiohttp==3.10.*",
|
||||
"ulid-py==1.1.*",
|
||||
"aenum==3.1.*",
|
||||
"typing_extensions>=4.4.0"
|
||||
|
||||
+7
-2
@@ -53,7 +53,8 @@ class EditableChannel:
|
||||
|
||||
if kwargs.get("icon", Missing) == None:
|
||||
remove.append("Icon")
|
||||
elif kwargs.get("description", Missing) == None:
|
||||
|
||||
if kwargs.get("description", Missing) == None:
|
||||
remove.append("Description")
|
||||
|
||||
if icon := kwargs.get("icon"):
|
||||
@@ -290,9 +291,10 @@ class ServerChannel(Channel):
|
||||
|
||||
async def set_default_permissions(self, permissions: PermissionsOverwrite) -> None:
|
||||
"""Sets the default permissions for the channel.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
permissions: :class:`ChannelPermissions`
|
||||
permissions: :class:`PermissionsOverwrite`
|
||||
The new default channel permissions
|
||||
"""
|
||||
allow, deny = permissions.to_pair()
|
||||
@@ -300,8 +302,11 @@ class ServerChannel(Channel):
|
||||
|
||||
async def set_role_permissions(self, role: Role, permissions: PermissionsOverwrite) -> None:
|
||||
"""Sets the permissions for a role in the channel.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
role: :class:`Role`
|
||||
The role to set permissions for
|
||||
permissions: :class:`ChannelPermissions`
|
||||
The new channel permissions
|
||||
"""
|
||||
|
||||
@@ -52,6 +52,8 @@ class Client:
|
||||
The api url for the revolt instance you are connecting to, by default it uses the offical instance hosted at revolt.chat
|
||||
max_messages: :class:`int`
|
||||
The max amount of messages stored in the cache, by default this is 5k
|
||||
bot: :class:`bool`
|
||||
Denotes whether the account used is a bot account or user account, by default this it assumes a bot account
|
||||
"""
|
||||
|
||||
def __init__(self, session: aiohttp.ClientSession, token: str, *, api_url: str = "https://api.revolt.chat", max_messages: int = 5000, bot: bool = True):
|
||||
|
||||
@@ -34,7 +34,7 @@ def check(check: Check[ClientT_D]) -> Callable[[T], T]:
|
||||
checks.append(check)
|
||||
func._checks = checks # type: ignore
|
||||
|
||||
return func
|
||||
return func # type: ignore
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import revolt
|
||||
if TYPE_CHECKING:
|
||||
from .help import HelpCommand
|
||||
|
||||
import aiohttp
|
||||
|
||||
from .cog import Cog
|
||||
from .command import Command
|
||||
from .context import Context
|
||||
@@ -42,8 +44,8 @@ class CommandsMeta(type):
|
||||
|
||||
for base in reversed(self.__mro__):
|
||||
for value in base.__dict__.values():
|
||||
if isinstance(value, Command) and value.parent is None:
|
||||
commands.append(value)
|
||||
if isinstance(value, Command) and value.parent is None: # type: ignore
|
||||
commands.append(value) # type: ignore
|
||||
|
||||
self._commands = commands
|
||||
|
||||
@@ -79,14 +81,42 @@ class CaseInsensitiveDict(dict[str, V]):
|
||||
|
||||
|
||||
class CommandsClient(revolt.Client, metaclass=CommandsMeta):
|
||||
"""Main class that adds commands, this class should be subclassed along with `revolt.Client`."""
|
||||
"""A subclass of :class:`~revolt.Client` which has support for commands.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
session: :class:`~aiohttp.ClientSession`
|
||||
The aiohttp session to use for http request and the websocket
|
||||
token: :class:`str`
|
||||
The bots token
|
||||
api_url: :class:`str`
|
||||
The api url for the revolt instance you are connecting to, by default it uses the offical instance hosted at revolt.chat
|
||||
max_messages: :class:`int`
|
||||
The max amount of messages stored in the cache, by default this is 5k
|
||||
bot: :class:`bool`
|
||||
Denotes whether the account used is a bot account or user account, by default this it assumes a bot account
|
||||
help_command: Optional[:class:`~revolt.ext.commands.HelpCommand`]
|
||||
Sets the custom help command, or remove it if passed ``None``
|
||||
case_insensitive: :class:`bool`
|
||||
Whether or not commands should be case insensitive
|
||||
"""
|
||||
|
||||
_commands: list[Command[Self]]
|
||||
|
||||
def __init__(self, *args: Any, help_command: Union[HelpCommand[Self], None, revolt.utils._Missing] = revolt.utils.Missing, case_insensitive: bool = False, **kwargs: Any):
|
||||
def __init__(
|
||||
self,
|
||||
session: aiohttp.ClientSession,
|
||||
token: str,
|
||||
*,
|
||||
api_url: str = "https://api.revolt.chat",
|
||||
max_messages: int = 5000,
|
||||
bot: bool = True,
|
||||
help_command: Union[HelpCommand[Self], None, revolt.utils._Missing] = revolt.utils.Missing,
|
||||
case_insensitive: bool = False
|
||||
):
|
||||
from .help import DefaultHelpCommand, HelpCommandImpl
|
||||
|
||||
self.all_commands: dict[str, Command[Self]] = {} if not case_insensitive else CaseInsensitiveDict()
|
||||
self.all_commands: dict[str, Command[Self]] | CaseInsensitiveDict[Command[Self]] = {} if not case_insensitive else CaseInsensitiveDict()
|
||||
self.cogs: dict[str, Cog[Self]] = {}
|
||||
self.extensions: dict[str, ExtensionProtocol] = {}
|
||||
|
||||
@@ -104,7 +134,7 @@ class CommandsClient(revolt.Client, metaclass=CommandsMeta):
|
||||
else:
|
||||
self.help_command = None
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__(session, token, api_url=api_url, max_messages=max_messages, bot=bot)
|
||||
|
||||
@property
|
||||
def commands(self) -> list[Command[Self]]:
|
||||
|
||||
+27
-15
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Coroutine, Generic, Optional, TypeVar, cast
|
||||
from typing import Any, Callable, Coroutine, Generic, Optional, TypeVar
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
from revolt.errors import RevoltError
|
||||
@@ -13,13 +13,13 @@ R = TypeVar("R")
|
||||
|
||||
__all__ = ("Cog", "CogMeta")
|
||||
|
||||
class CogMeta(type, Generic[ClientT_D]):
|
||||
_cog_commands: list[Command[ClientT_D]]
|
||||
class CogMeta(type):
|
||||
_cog_commands: list[Command[Any]]
|
||||
_cog_listeners: dict[str, list[str]]
|
||||
qualified_name: str
|
||||
|
||||
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any], *, qualified_name: Optional[str] = None, extras: dict[str, Any] | None = None) -> Any:
|
||||
commands: list[Command[ClientT_D]] = []
|
||||
commands: list[Command[Any]] = []
|
||||
listeners: dict[str, list[str]] = {}
|
||||
|
||||
self = super().__new__(cls, name, bases, attrs)
|
||||
@@ -29,9 +29,9 @@ class CogMeta(type, Generic[ClientT_D]):
|
||||
for key, value in base.__dict__.items():
|
||||
if isinstance(value, Command):
|
||||
for extra_key, extra_value in extras.items():
|
||||
setattr(value, extra_key, extra_value)
|
||||
setattr(value, extra_key, extra_value) # type: ignore
|
||||
|
||||
commands.append(cast(Command[ClientT_D], value)) # cant verify generic at runtime so must cast
|
||||
commands.append(value) # type: ignore
|
||||
|
||||
elif event_name := getattr(value, "__listener_name", None):
|
||||
listeners.setdefault(event_name, []).append(key)
|
||||
@@ -57,26 +57,38 @@ class Cog(Generic[ClientT_D], metaclass=CogMeta):
|
||||
def _inject(self, client: ClientT_D) -> None:
|
||||
client.cogs[self.qualified_name] = self
|
||||
|
||||
for command in self._cog_commands:
|
||||
command.cog = self
|
||||
try:
|
||||
for command in self._cog_commands:
|
||||
command.cog = self
|
||||
|
||||
if command.parent is None:
|
||||
client.add_command(command)
|
||||
if command.parent is None:
|
||||
client.add_command(command)
|
||||
|
||||
for key, listeners in self._cog_listeners.items():
|
||||
for listener_name in listeners:
|
||||
client.listeners.setdefault(key, []).append(getattr(self, listener_name))
|
||||
for key, listeners in self._cog_listeners.items():
|
||||
for listener_name in listeners:
|
||||
client.listeners.setdefault(key, []).append(getattr(self, listener_name))
|
||||
|
||||
except Exception as e:
|
||||
self._uninject(client)
|
||||
|
||||
raise e
|
||||
|
||||
self.cog_load()
|
||||
|
||||
def _uninject(self, client: ClientT_D) -> None:
|
||||
for name, command in client.all_commands.copy().items():
|
||||
if command in self._cog_commands:
|
||||
del client.all_commands[name]
|
||||
try:
|
||||
del client.all_commands[name]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for key, listeners in self._cog_listeners.items():
|
||||
for listener_name in listeners:
|
||||
client.listeners[key].remove(getattr(self, listener_name))
|
||||
try:
|
||||
client.listeners[key].remove(getattr(self, listener_name))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.cog_unload()
|
||||
|
||||
|
||||
@@ -6,8 +6,16 @@ from contextlib import suppress
|
||||
from typing import (TYPE_CHECKING, Annotated, Any, Callable, Coroutine,
|
||||
Generic, Literal, Optional, Union, get_args, get_origin)
|
||||
from typing_extensions import ParamSpec
|
||||
import sys
|
||||
|
||||
from revolt.utils import maybe_coroutine
|
||||
if sys.version_info >= (3, 10):
|
||||
from types import UnionType
|
||||
|
||||
UnionTypes: tuple[Any, ...] = (Union, UnionType)
|
||||
else:
|
||||
UnionTypes = (Union,)
|
||||
|
||||
from ...utils import maybe_coroutine
|
||||
|
||||
from .errors import CommandOnCooldown, InvalidLiteralArgument, UnionConverterError
|
||||
from .utils import ClientT_Co_D, evaluate_parameters, ClientT_Co
|
||||
@@ -126,7 +134,7 @@ class Command(Generic[ClientT_Co_D]):
|
||||
|
||||
@classmethod
|
||||
async def handle_origin(cls, context: Context[ClientT_Co_D], origin: Any, annotation: Any, arg: str) -> Any:
|
||||
if origin is Union:
|
||||
if origin in UnionTypes:
|
||||
for converter in get_args(annotation):
|
||||
try:
|
||||
return await cls.convert_argument(arg, converter, context)
|
||||
@@ -140,8 +148,20 @@ class Command(Generic[ClientT_Co_D]):
|
||||
elif origin is Annotated:
|
||||
annotated_args = get_args(annotation)
|
||||
|
||||
if origin := get_origin(annotated_args[0]):
|
||||
return await cls.handle_origin(context, origin, annotated_args[1], arg)
|
||||
if annotated_args[1] == "_revolt_greedy_marker":
|
||||
real_annotation = get_args(annotated_args[0])[0]
|
||||
converted_args: list[Any] = []
|
||||
|
||||
converted_args.append(await cls.convert_argument(arg, real_annotation, context))
|
||||
|
||||
for arg in context.view:
|
||||
try:
|
||||
converted_args.append(await cls.convert_argument(arg, real_annotation, context))
|
||||
except:
|
||||
context.view.undo()
|
||||
break
|
||||
|
||||
return converted_args
|
||||
else:
|
||||
return await cls.convert_argument(arg, annotated_args[1], context)
|
||||
|
||||
@@ -175,6 +195,10 @@ class Command(Generic[ClientT_Co_D]):
|
||||
except StopIteration:
|
||||
if parameter.default is not parameter.empty:
|
||||
arg = parameter.default
|
||||
|
||||
elif is_optional(parameter.annotation):
|
||||
arg = None
|
||||
|
||||
else:
|
||||
raise
|
||||
|
||||
@@ -192,7 +216,10 @@ class Command(Generic[ClientT_Co_D]):
|
||||
except StopIteration:
|
||||
if parameter.default is not parameter.empty:
|
||||
arg = parameter.default
|
||||
context.view.undo()
|
||||
|
||||
elif is_optional(parameter.annotation):
|
||||
arg = None
|
||||
|
||||
else:
|
||||
raise
|
||||
|
||||
@@ -251,6 +278,9 @@ class Command(Generic[ClientT_Co_D]):
|
||||
|
||||
return f"{' '.join(parents[::-1])} {self.name} {' '.join(parameters)}"
|
||||
|
||||
def is_optional(arg: Any) -> bool:
|
||||
return get_origin(arg) in UnionTypes and any(arg is NoneType for arg in get_args(arg))
|
||||
|
||||
def command(
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
|
||||
@@ -13,7 +13,9 @@ from .errors import (BadBoolArgument, CategoryConverterError,
|
||||
if TYPE_CHECKING:
|
||||
from .client import CommandsClient
|
||||
|
||||
__all__: tuple[str, ...] = ("bool_converter", "category_converter", "channel_converter", "user_converter", "member_converter", "IntConverter", "BoolConverter", "CategoryConverter", "UserConverter", "MemberConverter", "ChannelConverter")
|
||||
T = TypeVar("T")
|
||||
|
||||
__all__: tuple[str, ...] = ("bool_converter", "category_converter", "channel_converter", "user_converter", "member_converter", "IntConverter", "BoolConverter", "CategoryConverter", "UserConverter", "MemberConverter", "ChannelConverter", "Greedy")
|
||||
|
||||
channel_regex: re.Pattern[str] = re.compile("<#([A-z0-9]{26})>")
|
||||
user_regex: re.Pattern[str] = re.compile("<@([A-z0-9]{26})>")
|
||||
@@ -35,7 +37,7 @@ def category_converter(arg: str, context: Context[ClientT]) -> Category:
|
||||
|
||||
try:
|
||||
return context.server.get_category(arg)
|
||||
except KeyError:
|
||||
except LookupError:
|
||||
try:
|
||||
return utils.get(context.server.categories, name=arg)
|
||||
except LookupError:
|
||||
@@ -50,7 +52,7 @@ def channel_converter(arg: str, context: Context[ClientT]) -> Channel:
|
||||
|
||||
try:
|
||||
return context.server.get_channel(arg)
|
||||
except KeyError:
|
||||
except LookupError:
|
||||
try:
|
||||
return utils.get(context.server.channels, name=arg)
|
||||
except LookupError:
|
||||
@@ -62,7 +64,7 @@ def user_converter(arg: str, context: Context[ClientT]) -> User:
|
||||
|
||||
try:
|
||||
return context.client.get_user(arg)
|
||||
except KeyError:
|
||||
except LookupError:
|
||||
try:
|
||||
parts = arg.split("#")
|
||||
|
||||
@@ -91,7 +93,7 @@ def member_converter(arg: str, context: Context[ClientT]) -> Member:
|
||||
|
||||
try:
|
||||
return context.server.get_member(arg)
|
||||
except KeyError:
|
||||
except LookupError:
|
||||
try:
|
||||
parts = arg.split("#")
|
||||
|
||||
@@ -120,3 +122,5 @@ CategoryConverter = Annotated[Category, category_converter]
|
||||
UserConverter = Annotated[User, user_converter]
|
||||
MemberConverter = Annotated[Member, member_converter]
|
||||
ChannelConverter = Annotated[Channel, channel_converter]
|
||||
|
||||
Greedy = Annotated[list[T], "_revolt_greedy_marker"]
|
||||
@@ -139,6 +139,6 @@ def cooldown(rate: int, per: int, *, bucket: BucketType | Callable[[Context[Clie
|
||||
func._cooldown = CooldownMapping(rate, per) # type: ignore
|
||||
func._bucket = bucket # type: ignore
|
||||
|
||||
return func
|
||||
return func # type: ignore
|
||||
|
||||
return inner
|
||||
@@ -8,6 +8,7 @@ __all__ = (
|
||||
"NotBotOwner",
|
||||
"NotServerOwner",
|
||||
"ServerOnly",
|
||||
"MissingPermissionsError",
|
||||
"ConverterError",
|
||||
"InvalidLiteralArgument",
|
||||
"BadBoolArgument",
|
||||
@@ -15,6 +16,7 @@ __all__ = (
|
||||
"ChannelConverterError",
|
||||
"UserConverterError",
|
||||
"MemberConverterError",
|
||||
"UnionConverterError",
|
||||
"MissingSetup",
|
||||
"CommandOnCooldown"
|
||||
)
|
||||
|
||||
@@ -186,7 +186,6 @@ async def help_command_impl(client: ClientT_D, context: Context[ClientT_D], *arg
|
||||
break
|
||||
|
||||
if isinstance(command, Group):
|
||||
command = cast(Group[ClientT_D], command)
|
||||
parent = command
|
||||
else:
|
||||
payload = await help_command.create_command_help(context, command)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from typing import Iterator
|
||||
from typing_extensions import Self
|
||||
|
||||
from .errors import NoClosingQuote
|
||||
|
||||
|
||||
@@ -16,7 +18,8 @@ class StringView:
|
||||
|
||||
def get_rest(self) -> str:
|
||||
if self.should_undo:
|
||||
return f"{self.temp} {''.join(self.value)}"
|
||||
return f"{self.temp} {''.join(self.value)}".rstrip()
|
||||
# prevent a new space appearing at end if the buffer is depleted
|
||||
|
||||
return "".join(self.value)
|
||||
|
||||
@@ -51,3 +54,9 @@ class StringView:
|
||||
self.temp = output
|
||||
|
||||
return output
|
||||
|
||||
def __iter__(self) -> Self:
|
||||
return self
|
||||
|
||||
def __next__(self) -> str:
|
||||
return self.get_next_word()
|
||||
+5
-3
@@ -29,7 +29,9 @@ if TYPE_CHECKING:
|
||||
from .types import SendableEmbed as SendableEmbedPayload
|
||||
from .types import User as UserPayload
|
||||
from .types import (Server, ServerBans, TextChannel, UserProfile, VoiceChannel, Member, Invite, ApiInfo, Channel, SavedMessages,
|
||||
DMChannel, EmojiParent, GetServerMembers, GroupDMChannel, MessageReplyPayload, MessageWithUserData, PartialInvite, Role)
|
||||
DMChannel, EmojiParent, GetServerMembers, GroupDMChannel, MessageReplyPayload, MessageWithUserData, PartialInvite, CreateRole)
|
||||
|
||||
from aiohttp.client import _RequestOptions
|
||||
|
||||
__all__ = ("HttpClient",)
|
||||
|
||||
@@ -49,7 +51,7 @@ class HttpClient:
|
||||
async def request(self, method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"], route: str, *, json: Optional[dict[str, Any]] = None, nonce: bool = True, params: Optional[dict[str, Any]] = None) -> Any:
|
||||
url = f"{self.api_url}{route}"
|
||||
|
||||
kwargs = {}
|
||||
kwargs: _RequestOptions = {}
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Revolt.py (https://github.com/revoltchat/revolt.py)",
|
||||
@@ -341,7 +343,7 @@ class HttpClient:
|
||||
def fetch_bans(self, server_id: str) -> Request[ServerBans]:
|
||||
return self.request("GET", f"/servers/{server_id}/bans")
|
||||
|
||||
def create_role(self, server_id: str, name: str) -> Request[Role]:
|
||||
def create_role(self, server_id: str, name: str) -> Request[CreateRole]:
|
||||
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]:
|
||||
|
||||
+2
-2
@@ -149,14 +149,14 @@ class Message(Ulid):
|
||||
if self.server_id:
|
||||
for mention in self.raw_mentions:
|
||||
try:
|
||||
self.mentions.append(self.server.get_member(mention))
|
||||
mentions.append(self.server.get_member(mention))
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
else:
|
||||
for mention in self.raw_mentions:
|
||||
try:
|
||||
self.mentions.append(self.state.get_user(mention))
|
||||
mentions.append(self.state.get_user(mention))
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -178,11 +178,11 @@ class PermissionsOverwrite:
|
||||
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
if key in Permissions.FLAG_NAMES:
|
||||
if key is True:
|
||||
if value is True:
|
||||
setattr(self._allow, key, True)
|
||||
super().__setattr__(key, True)
|
||||
|
||||
elif key is False:
|
||||
elif value is False:
|
||||
setattr(self._deny, key, True)
|
||||
super().__setattr__(key, False)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from revolt.enums import ChannelType
|
||||
@@ -28,7 +28,7 @@ def calculate_permissions(member: Member, target: Server | Channel) -> Permissio
|
||||
for role in member.roles:
|
||||
permissions = (permissions | role.permissions._allow) & (~role.permissions._deny)
|
||||
|
||||
if member.current_timeout and member.current_timeout > datetime.now():
|
||||
if member.current_timeout and member.current_timeout > datetime.now(timezone.utc):
|
||||
permissions = permissions & Permissions.default_view_only()
|
||||
|
||||
return permissions
|
||||
|
||||
+4
-5
@@ -203,7 +203,7 @@ class Server(Ulid):
|
||||
|
||||
@property
|
||||
def channels(self) -> list[Channel]:
|
||||
"""list[:class:`Member`] Gets all channels in the server"""
|
||||
"""list[:class:`Channel`] Gets all channels in the server"""
|
||||
return list(self._channels.values())
|
||||
|
||||
@property
|
||||
@@ -310,12 +310,11 @@ class Server(Ulid):
|
||||
|
||||
async def set_default_permissions(self, permissions: Permissions) -> None:
|
||||
"""Sets the default server permissions.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
server_permissions: Optional[:class:`ServerPermissions`]
|
||||
permissions: :class:`Permissions`
|
||||
The new default server permissions
|
||||
channel_permissions: Optional[:class:`ChannelPermissions`]
|
||||
the new default channel permissions
|
||||
"""
|
||||
|
||||
await self.state.http.set_server_default_permissions(self.id, permissions.value)
|
||||
@@ -427,7 +426,7 @@ class Server(Ulid):
|
||||
"""
|
||||
payload = await self.state.http.create_role(self.id, name)
|
||||
|
||||
return Role(payload, name, self, self.state)
|
||||
return Role(payload["role"], payload["id"], self, self.state)
|
||||
|
||||
async def create_emoji(self, name: str, file: File, *, nsfw: bool = False) -> Emoji:
|
||||
"""Creates an emoji
|
||||
|
||||
@@ -61,8 +61,6 @@ class State:
|
||||
raise LookupError from None
|
||||
|
||||
def add_user(self, payload: UserPayload) -> User:
|
||||
|
||||
|
||||
user = User(payload, self)
|
||||
|
||||
if payload.get("relationship") == "User":
|
||||
|
||||
@@ -7,6 +7,7 @@ if TYPE_CHECKING:
|
||||
from .member import Member
|
||||
from .message import Message
|
||||
from .user import User
|
||||
from .role import Role
|
||||
|
||||
|
||||
__all__ = (
|
||||
@@ -15,6 +16,7 @@ __all__ = (
|
||||
"Autumn",
|
||||
"GetServerMembers",
|
||||
"MessageWithUserData",
|
||||
"CreateRole",
|
||||
)
|
||||
|
||||
|
||||
@@ -51,3 +53,7 @@ class MessageWithUserData(TypedDict):
|
||||
messages: list[Message]
|
||||
members: NotRequired[list[Member]]
|
||||
users: list[User]
|
||||
|
||||
class CreateRole(TypedDict):
|
||||
id: str
|
||||
role: Role
|
||||
+24
-1
@@ -47,14 +47,37 @@ async def maybe_coroutine(func: Callable[P, Union[R_T, Coroutine[Any, Any, R_T]]
|
||||
|
||||
|
||||
class Ulid:
|
||||
"""Base class for any revolt object with an id
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
id: :class:`str`
|
||||
The id of the object
|
||||
"""
|
||||
id: str
|
||||
|
||||
@property
|
||||
def created_at(self) -> datetime.datetime:
|
||||
"""Returns a datetime for when the object was created according to the id
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`datetime.datetime`
|
||||
The datetime of the creation date and time
|
||||
"""
|
||||
return ulid.from_str(self.id).timestamp().datetime
|
||||
|
||||
class Object(Ulid):
|
||||
"""Class to mock objects with an id"""
|
||||
"""Class to mock objects with an id
|
||||
|
||||
.. note::
|
||||
This does not validate or guarantee the id is correct, you must handle this yourself
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
id: :class:`str`
|
||||
The ULID id to mock
|
||||
"""
|
||||
def __init__(self, id: str):
|
||||
self.id = id
|
||||
|
||||
|
||||
+1
-1
@@ -318,7 +318,7 @@ class WebsocketHandler:
|
||||
|
||||
self.dispatch("member_join", member)
|
||||
|
||||
async def handle_memberleave(self, payload: ServerMemberLeaveEventPayload) -> None:
|
||||
async def handle_servermemberleave(self, payload: ServerMemberLeaveEventPayload) -> None:
|
||||
await self._wait_for_server_ready(payload["id"])
|
||||
|
||||
server = self.state.get_server(payload["id"])
|
||||
|
||||
Reference in New Issue
Block a user