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
|
# Revolt.py
|
||||||
|
|
||||||
|
> # This project is archived and is no longer receiving updates.
|
||||||
|
|
||||||
An async library to interact with the https://revolt.chat API.
|
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/).
|
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:
|
:members:
|
||||||
|
|
||||||
Enums
|
Enums
|
||||||
======
|
------
|
||||||
|
|
||||||
The api uses enums to say what variant of something is,
|
The api uses enums to say what variant of something is,
|
||||||
these represent those enums
|
these represent those enums
|
||||||
@@ -278,7 +278,7 @@ All enums subclass `aenum.Enum`.
|
|||||||
The embed is unknown
|
The embed is unknown
|
||||||
|
|
||||||
Utils
|
Utils
|
||||||
======
|
------
|
||||||
|
|
||||||
.. currentmodule:: revolt.utils
|
.. currentmodule:: revolt.utils
|
||||||
|
|
||||||
@@ -287,3 +287,9 @@ A collection a utility functions and classes to aid in making your bot
|
|||||||
.. autofunction:: get
|
.. autofunction:: get
|
||||||
|
|
||||||
.. autofunction:: client_session
|
.. autofunction:: client_session
|
||||||
|
|
||||||
|
.. autoclass:: Ulid
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Object
|
||||||
|
:members:
|
||||||
@@ -19,6 +19,11 @@ Command
|
|||||||
.. autoclass:: revolt.ext.commands.Command
|
.. autoclass:: revolt.ext.commands.Command
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Group
|
||||||
|
~~~~~~~~
|
||||||
|
.. autoclass:: revolt.ext.commands.Group
|
||||||
|
:members:
|
||||||
|
|
||||||
Cog
|
Cog
|
||||||
~~~~
|
~~~~
|
||||||
.. autoclass:: revolt.ext.commands.Cog
|
.. autoclass:: revolt.ext.commands.Cog
|
||||||
@@ -28,6 +33,13 @@ command
|
|||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
.. autodecorator:: revolt.ext.commands.command
|
.. autodecorator:: revolt.ext.commands.command
|
||||||
|
|
||||||
|
group
|
||||||
|
~~~~~~~~
|
||||||
|
.. autodecorator:: revolt.ext.commands.group
|
||||||
|
|
||||||
|
Checks
|
||||||
|
-------
|
||||||
|
|
||||||
check
|
check
|
||||||
~~~~~~
|
~~~~~~
|
||||||
.. autodecorator:: revolt.ext.commands.check
|
.. autodecorator:: revolt.ext.commands.check
|
||||||
@@ -40,9 +52,62 @@ is_server_owner
|
|||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
.. autodecorator:: revolt.ext.commands.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
|
Exceptions
|
||||||
===========
|
-----------
|
||||||
|
|
||||||
CommandError
|
CommandError
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@@ -79,6 +144,11 @@ ServerOnly
|
|||||||
.. autoexception:: revolt.ext.commands.ServerOnly
|
.. autoexception:: revolt.ext.commands.ServerOnly
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
MissingPermissionsError
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
.. autoexception:: revolt.ext.commands.MissingPermissionsError
|
||||||
|
:members:
|
||||||
|
|
||||||
ConverterError
|
ConverterError
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
.. autoexception:: revolt.ext.commands.ConverterError
|
.. autoexception:: revolt.ext.commands.ConverterError
|
||||||
@@ -99,6 +169,11 @@ CategoryConverterError
|
|||||||
.. autoexception:: revolt.ext.commands.CategoryConverterError
|
.. autoexception:: revolt.ext.commands.CategoryConverterError
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
ChannelConverterError
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
.. autoexception:: revolt.ext.commands.ChannelConverterError
|
||||||
|
:members:
|
||||||
|
|
||||||
UserConverterError
|
UserConverterError
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
.. autoexception:: revolt.ext.commands.UserConverterError
|
.. autoexception:: revolt.ext.commands.UserConverterError
|
||||||
@@ -108,3 +183,18 @@ MemberConverterError
|
|||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
.. autoexception:: revolt.ext.commands.MemberConverterError
|
.. autoexception:: revolt.ext.commands.MemberConverterError
|
||||||
:members:
|
: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",
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aiohttp==3.8.*",
|
"aiohttp==3.10.*",
|
||||||
"ulid-py==1.1.*",
|
"ulid-py==1.1.*",
|
||||||
"aenum==3.1.*",
|
"aenum==3.1.*",
|
||||||
"typing_extensions>=4.4.0"
|
"typing_extensions>=4.4.0"
|
||||||
|
|||||||
+7
-2
@@ -53,7 +53,8 @@ class EditableChannel:
|
|||||||
|
|
||||||
if kwargs.get("icon", Missing) == None:
|
if kwargs.get("icon", Missing) == None:
|
||||||
remove.append("Icon")
|
remove.append("Icon")
|
||||||
elif kwargs.get("description", Missing) == None:
|
|
||||||
|
if kwargs.get("description", Missing) == None:
|
||||||
remove.append("Description")
|
remove.append("Description")
|
||||||
|
|
||||||
if icon := kwargs.get("icon"):
|
if icon := kwargs.get("icon"):
|
||||||
@@ -290,9 +291,10 @@ class ServerChannel(Channel):
|
|||||||
|
|
||||||
async def set_default_permissions(self, permissions: PermissionsOverwrite) -> None:
|
async def set_default_permissions(self, permissions: PermissionsOverwrite) -> None:
|
||||||
"""Sets the default permissions for the channel.
|
"""Sets the default permissions for the channel.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
-----------
|
||||||
permissions: :class:`ChannelPermissions`
|
permissions: :class:`PermissionsOverwrite`
|
||||||
The new default channel permissions
|
The new default channel permissions
|
||||||
"""
|
"""
|
||||||
allow, deny = permissions.to_pair()
|
allow, deny = permissions.to_pair()
|
||||||
@@ -300,8 +302,11 @@ class ServerChannel(Channel):
|
|||||||
|
|
||||||
async def set_role_permissions(self, role: Role, permissions: PermissionsOverwrite) -> None:
|
async def set_role_permissions(self, role: Role, permissions: PermissionsOverwrite) -> None:
|
||||||
"""Sets the permissions for a role in the channel.
|
"""Sets the permissions for a role in the channel.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
-----------
|
||||||
|
role: :class:`Role`
|
||||||
|
The role to set permissions for
|
||||||
permissions: :class:`ChannelPermissions`
|
permissions: :class:`ChannelPermissions`
|
||||||
The new channel permissions
|
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
|
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`
|
max_messages: :class:`int`
|
||||||
The max amount of messages stored in the cache, by default this is 5k
|
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):
|
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)
|
checks.append(check)
|
||||||
func._checks = checks # type: ignore
|
func._checks = checks # type: ignore
|
||||||
|
|
||||||
return func
|
return func # type: ignore
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import revolt
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .help import HelpCommand
|
from .help import HelpCommand
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
from .cog import Cog
|
from .cog import Cog
|
||||||
from .command import Command
|
from .command import Command
|
||||||
from .context import Context
|
from .context import Context
|
||||||
@@ -42,8 +44,8 @@ class CommandsMeta(type):
|
|||||||
|
|
||||||
for base in reversed(self.__mro__):
|
for base in reversed(self.__mro__):
|
||||||
for value in base.__dict__.values():
|
for value in base.__dict__.values():
|
||||||
if isinstance(value, Command) and value.parent is None:
|
if isinstance(value, Command) and value.parent is None: # type: ignore
|
||||||
commands.append(value)
|
commands.append(value) # type: ignore
|
||||||
|
|
||||||
self._commands = commands
|
self._commands = commands
|
||||||
|
|
||||||
@@ -79,14 +81,42 @@ class CaseInsensitiveDict(dict[str, V]):
|
|||||||
|
|
||||||
|
|
||||||
class CommandsClient(revolt.Client, metaclass=CommandsMeta):
|
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]]
|
_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
|
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.cogs: dict[str, Cog[Self]] = {}
|
||||||
self.extensions: dict[str, ExtensionProtocol] = {}
|
self.extensions: dict[str, ExtensionProtocol] = {}
|
||||||
|
|
||||||
@@ -104,7 +134,7 @@ class CommandsClient(revolt.Client, metaclass=CommandsMeta):
|
|||||||
else:
|
else:
|
||||||
self.help_command = None
|
self.help_command = None
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(session, token, api_url=api_url, max_messages=max_messages, bot=bot)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def commands(self) -> list[Command[Self]]:
|
def commands(self) -> list[Command[Self]]:
|
||||||
|
|||||||
+27
-15
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
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 typing_extensions import ParamSpec
|
||||||
|
|
||||||
from revolt.errors import RevoltError
|
from revolt.errors import RevoltError
|
||||||
@@ -13,13 +13,13 @@ R = TypeVar("R")
|
|||||||
|
|
||||||
__all__ = ("Cog", "CogMeta")
|
__all__ = ("Cog", "CogMeta")
|
||||||
|
|
||||||
class CogMeta(type, Generic[ClientT_D]):
|
class CogMeta(type):
|
||||||
_cog_commands: list[Command[ClientT_D]]
|
_cog_commands: list[Command[Any]]
|
||||||
_cog_listeners: dict[str, list[str]]
|
_cog_listeners: dict[str, list[str]]
|
||||||
qualified_name: 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:
|
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]] = {}
|
listeners: dict[str, list[str]] = {}
|
||||||
|
|
||||||
self = super().__new__(cls, name, bases, attrs)
|
self = super().__new__(cls, name, bases, attrs)
|
||||||
@@ -29,9 +29,9 @@ class CogMeta(type, Generic[ClientT_D]):
|
|||||||
for key, value in base.__dict__.items():
|
for key, value in base.__dict__.items():
|
||||||
if isinstance(value, Command):
|
if isinstance(value, Command):
|
||||||
for extra_key, extra_value in extras.items():
|
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):
|
elif event_name := getattr(value, "__listener_name", None):
|
||||||
listeners.setdefault(event_name, []).append(key)
|
listeners.setdefault(event_name, []).append(key)
|
||||||
@@ -57,26 +57,38 @@ class Cog(Generic[ClientT_D], metaclass=CogMeta):
|
|||||||
def _inject(self, client: ClientT_D) -> None:
|
def _inject(self, client: ClientT_D) -> None:
|
||||||
client.cogs[self.qualified_name] = self
|
client.cogs[self.qualified_name] = self
|
||||||
|
|
||||||
for command in self._cog_commands:
|
try:
|
||||||
command.cog = self
|
for command in self._cog_commands:
|
||||||
|
command.cog = self
|
||||||
|
|
||||||
if command.parent is None:
|
if command.parent is None:
|
||||||
client.add_command(command)
|
client.add_command(command)
|
||||||
|
|
||||||
for key, listeners in self._cog_listeners.items():
|
for key, listeners in self._cog_listeners.items():
|
||||||
for listener_name in listeners:
|
for listener_name in listeners:
|
||||||
client.listeners.setdefault(key, []).append(getattr(self, listener_name))
|
client.listeners.setdefault(key, []).append(getattr(self, listener_name))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self._uninject(client)
|
||||||
|
|
||||||
|
raise e
|
||||||
|
|
||||||
self.cog_load()
|
self.cog_load()
|
||||||
|
|
||||||
def _uninject(self, client: ClientT_D) -> None:
|
def _uninject(self, client: ClientT_D) -> None:
|
||||||
for name, command in client.all_commands.copy().items():
|
for name, command in client.all_commands.copy().items():
|
||||||
if command in self._cog_commands:
|
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 key, listeners in self._cog_listeners.items():
|
||||||
for listener_name in listeners:
|
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()
|
self.cog_unload()
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,16 @@ from contextlib import suppress
|
|||||||
from typing import (TYPE_CHECKING, Annotated, Any, Callable, Coroutine,
|
from typing import (TYPE_CHECKING, Annotated, Any, Callable, Coroutine,
|
||||||
Generic, Literal, Optional, Union, get_args, get_origin)
|
Generic, Literal, Optional, Union, get_args, get_origin)
|
||||||
from typing_extensions import ParamSpec
|
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 .errors import CommandOnCooldown, InvalidLiteralArgument, UnionConverterError
|
||||||
from .utils import ClientT_Co_D, evaluate_parameters, ClientT_Co
|
from .utils import ClientT_Co_D, evaluate_parameters, ClientT_Co
|
||||||
@@ -126,7 +134,7 @@ class Command(Generic[ClientT_Co_D]):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def handle_origin(cls, context: Context[ClientT_Co_D], origin: Any, annotation: Any, arg: str) -> Any:
|
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):
|
for converter in get_args(annotation):
|
||||||
try:
|
try:
|
||||||
return await cls.convert_argument(arg, converter, context)
|
return await cls.convert_argument(arg, converter, context)
|
||||||
@@ -140,8 +148,20 @@ class Command(Generic[ClientT_Co_D]):
|
|||||||
elif origin is Annotated:
|
elif origin is Annotated:
|
||||||
annotated_args = get_args(annotation)
|
annotated_args = get_args(annotation)
|
||||||
|
|
||||||
if origin := get_origin(annotated_args[0]):
|
if annotated_args[1] == "_revolt_greedy_marker":
|
||||||
return await cls.handle_origin(context, origin, annotated_args[1], arg)
|
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:
|
else:
|
||||||
return await cls.convert_argument(arg, annotated_args[1], context)
|
return await cls.convert_argument(arg, annotated_args[1], context)
|
||||||
|
|
||||||
@@ -175,6 +195,10 @@ class Command(Generic[ClientT_Co_D]):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
if parameter.default is not parameter.empty:
|
if parameter.default is not parameter.empty:
|
||||||
arg = parameter.default
|
arg = parameter.default
|
||||||
|
|
||||||
|
elif is_optional(parameter.annotation):
|
||||||
|
arg = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -192,7 +216,10 @@ class Command(Generic[ClientT_Co_D]):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
if parameter.default is not parameter.empty:
|
if parameter.default is not parameter.empty:
|
||||||
arg = parameter.default
|
arg = parameter.default
|
||||||
context.view.undo()
|
|
||||||
|
elif is_optional(parameter.annotation):
|
||||||
|
arg = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -251,6 +278,9 @@ class Command(Generic[ClientT_Co_D]):
|
|||||||
|
|
||||||
return f"{' '.join(parents[::-1])} {self.name} {' '.join(parameters)}"
|
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(
|
def command(
|
||||||
*,
|
*,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ from .errors import (BadBoolArgument, CategoryConverterError,
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .client import CommandsClient
|
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})>")
|
channel_regex: re.Pattern[str] = re.compile("<#([A-z0-9]{26})>")
|
||||||
user_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:
|
try:
|
||||||
return context.server.get_category(arg)
|
return context.server.get_category(arg)
|
||||||
except KeyError:
|
except LookupError:
|
||||||
try:
|
try:
|
||||||
return utils.get(context.server.categories, name=arg)
|
return utils.get(context.server.categories, name=arg)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
@@ -50,7 +52,7 @@ def channel_converter(arg: str, context: Context[ClientT]) -> Channel:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return context.server.get_channel(arg)
|
return context.server.get_channel(arg)
|
||||||
except KeyError:
|
except LookupError:
|
||||||
try:
|
try:
|
||||||
return utils.get(context.server.channels, name=arg)
|
return utils.get(context.server.channels, name=arg)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
@@ -62,7 +64,7 @@ def user_converter(arg: str, context: Context[ClientT]) -> User:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return context.client.get_user(arg)
|
return context.client.get_user(arg)
|
||||||
except KeyError:
|
except LookupError:
|
||||||
try:
|
try:
|
||||||
parts = arg.split("#")
|
parts = arg.split("#")
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ def member_converter(arg: str, context: Context[ClientT]) -> Member:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return context.server.get_member(arg)
|
return context.server.get_member(arg)
|
||||||
except KeyError:
|
except LookupError:
|
||||||
try:
|
try:
|
||||||
parts = arg.split("#")
|
parts = arg.split("#")
|
||||||
|
|
||||||
@@ -120,3 +122,5 @@ CategoryConverter = Annotated[Category, category_converter]
|
|||||||
UserConverter = Annotated[User, user_converter]
|
UserConverter = Annotated[User, user_converter]
|
||||||
MemberConverter = Annotated[Member, member_converter]
|
MemberConverter = Annotated[Member, member_converter]
|
||||||
ChannelConverter = Annotated[Channel, channel_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._cooldown = CooldownMapping(rate, per) # type: ignore
|
||||||
func._bucket = bucket # type: ignore
|
func._bucket = bucket # type: ignore
|
||||||
|
|
||||||
return func
|
return func # type: ignore
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
@@ -8,6 +8,7 @@ __all__ = (
|
|||||||
"NotBotOwner",
|
"NotBotOwner",
|
||||||
"NotServerOwner",
|
"NotServerOwner",
|
||||||
"ServerOnly",
|
"ServerOnly",
|
||||||
|
"MissingPermissionsError",
|
||||||
"ConverterError",
|
"ConverterError",
|
||||||
"InvalidLiteralArgument",
|
"InvalidLiteralArgument",
|
||||||
"BadBoolArgument",
|
"BadBoolArgument",
|
||||||
@@ -15,6 +16,7 @@ __all__ = (
|
|||||||
"ChannelConverterError",
|
"ChannelConverterError",
|
||||||
"UserConverterError",
|
"UserConverterError",
|
||||||
"MemberConverterError",
|
"MemberConverterError",
|
||||||
|
"UnionConverterError",
|
||||||
"MissingSetup",
|
"MissingSetup",
|
||||||
"CommandOnCooldown"
|
"CommandOnCooldown"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -186,7 +186,6 @@ async def help_command_impl(client: ClientT_D, context: Context[ClientT_D], *arg
|
|||||||
break
|
break
|
||||||
|
|
||||||
if isinstance(command, Group):
|
if isinstance(command, Group):
|
||||||
command = cast(Group[ClientT_D], command)
|
|
||||||
parent = command
|
parent = command
|
||||||
else:
|
else:
|
||||||
payload = await help_command.create_command_help(context, command)
|
payload = await help_command.create_command_help(context, command)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
from .errors import NoClosingQuote
|
from .errors import NoClosingQuote
|
||||||
|
|
||||||
|
|
||||||
@@ -16,7 +18,8 @@ class StringView:
|
|||||||
|
|
||||||
def get_rest(self) -> str:
|
def get_rest(self) -> str:
|
||||||
if self.should_undo:
|
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)
|
return "".join(self.value)
|
||||||
|
|
||||||
@@ -51,3 +54,9 @@ class StringView:
|
|||||||
self.temp = output
|
self.temp = output
|
||||||
|
|
||||||
return 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 SendableEmbed as SendableEmbedPayload
|
||||||
from .types import User as UserPayload
|
from .types import User as UserPayload
|
||||||
from .types import (Server, ServerBans, TextChannel, UserProfile, VoiceChannel, Member, Invite, ApiInfo, Channel, SavedMessages,
|
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",)
|
__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:
|
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}"
|
url = f"{self.api_url}{route}"
|
||||||
|
|
||||||
kwargs = {}
|
kwargs: _RequestOptions = {}
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": "Revolt.py (https://github.com/revoltchat/revolt.py)",
|
"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]:
|
def fetch_bans(self, server_id: str) -> Request[ServerBans]:
|
||||||
return self.request("GET", f"/servers/{server_id}/bans")
|
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)
|
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]:
|
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:
|
if self.server_id:
|
||||||
for mention in self.raw_mentions:
|
for mention in self.raw_mentions:
|
||||||
try:
|
try:
|
||||||
self.mentions.append(self.server.get_member(mention))
|
mentions.append(self.server.get_member(mention))
|
||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for mention in self.raw_mentions:
|
for mention in self.raw_mentions:
|
||||||
try:
|
try:
|
||||||
self.mentions.append(self.state.get_user(mention))
|
mentions.append(self.state.get_user(mention))
|
||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -178,11 +178,11 @@ class PermissionsOverwrite:
|
|||||||
|
|
||||||
def __setattr__(self, key: str, value: Any) -> None:
|
def __setattr__(self, key: str, value: Any) -> None:
|
||||||
if key in Permissions.FLAG_NAMES:
|
if key in Permissions.FLAG_NAMES:
|
||||||
if key is True:
|
if value is True:
|
||||||
setattr(self._allow, key, True)
|
setattr(self._allow, key, True)
|
||||||
super().__setattr__(key, True)
|
super().__setattr__(key, True)
|
||||||
|
|
||||||
elif key is False:
|
elif value is False:
|
||||||
setattr(self._deny, key, True)
|
setattr(self._deny, key, True)
|
||||||
super().__setattr__(key, False)
|
super().__setattr__(key, False)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
||||||
from revolt.enums import ChannelType
|
from revolt.enums import ChannelType
|
||||||
@@ -28,7 +28,7 @@ def calculate_permissions(member: Member, target: Server | Channel) -> Permissio
|
|||||||
for role in member.roles:
|
for role in member.roles:
|
||||||
permissions = (permissions | role.permissions._allow) & (~role.permissions._deny)
|
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()
|
permissions = permissions & Permissions.default_view_only()
|
||||||
|
|
||||||
return permissions
|
return permissions
|
||||||
|
|||||||
+4
-5
@@ -203,7 +203,7 @@ class Server(Ulid):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self) -> list[Channel]:
|
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())
|
return list(self._channels.values())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -310,12 +310,11 @@ class Server(Ulid):
|
|||||||
|
|
||||||
async def set_default_permissions(self, permissions: Permissions) -> None:
|
async def set_default_permissions(self, permissions: Permissions) -> None:
|
||||||
"""Sets the default server permissions.
|
"""Sets the default server permissions.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
-----------
|
||||||
server_permissions: Optional[:class:`ServerPermissions`]
|
permissions: :class:`Permissions`
|
||||||
The new default server 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)
|
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)
|
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:
|
async def create_emoji(self, name: str, file: File, *, nsfw: bool = False) -> Emoji:
|
||||||
"""Creates an emoji
|
"""Creates an emoji
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ class State:
|
|||||||
raise LookupError from None
|
raise LookupError from None
|
||||||
|
|
||||||
def add_user(self, payload: UserPayload) -> User:
|
def add_user(self, payload: UserPayload) -> User:
|
||||||
|
|
||||||
|
|
||||||
user = User(payload, self)
|
user = User(payload, self)
|
||||||
|
|
||||||
if payload.get("relationship") == "User":
|
if payload.get("relationship") == "User":
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ if TYPE_CHECKING:
|
|||||||
from .member import Member
|
from .member import Member
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .user import User
|
from .user import User
|
||||||
|
from .role import Role
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@@ -15,6 +16,7 @@ __all__ = (
|
|||||||
"Autumn",
|
"Autumn",
|
||||||
"GetServerMembers",
|
"GetServerMembers",
|
||||||
"MessageWithUserData",
|
"MessageWithUserData",
|
||||||
|
"CreateRole",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -51,3 +53,7 @@ class MessageWithUserData(TypedDict):
|
|||||||
messages: list[Message]
|
messages: list[Message]
|
||||||
members: NotRequired[list[Member]]
|
members: NotRequired[list[Member]]
|
||||||
users: list[User]
|
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:
|
class Ulid:
|
||||||
|
"""Base class for any revolt object with an id
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
id: :class:`str`
|
||||||
|
The id of the object
|
||||||
|
"""
|
||||||
id: str
|
id: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at(self) -> datetime.datetime:
|
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
|
return ulid.from_str(self.id).timestamp().datetime
|
||||||
|
|
||||||
class Object(Ulid):
|
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):
|
def __init__(self, id: str):
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -318,7 +318,7 @@ class WebsocketHandler:
|
|||||||
|
|
||||||
self.dispatch("member_join", member)
|
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"])
|
await self._wait_for_server_ready(payload["id"])
|
||||||
|
|
||||||
server = self.state.get_server(payload["id"])
|
server = self.state.get_server(payload["id"])
|
||||||
|
|||||||
Reference in New Issue
Block a user