4 Commits

Author SHA1 Message Date
Sami Altamimi
e48493b36c Actually block out Python 3.9.7
reference PR https://github.com/iDevision/enhanced-discord.py/pull/93
2021-10-05 21:12:40 -05:00
Sami Altamimi
4931100b44 Advise version incompatibility with Python 3.9.7
Due to a bug in Python 3.9.7, code situations where we call an __init__ function in a subclass will fail with a TypeError. 

This is only a bug within the Python language and was patched out with Python 3.10 and this doesn't affect Python 3.9.6. 

Here, we are advising the incompatibility.
2021-10-05 21:03:05 -05:00
Ian Webster
ec1e2add21 Update user-agent (#92) 2021-10-04 21:11:10 +01:00
Gnome
4277f65051 Implement _FakeSlashMessage.clean_content
Closes #83
2021-10-03 21:05:00 +01:00
7 changed files with 44 additions and 67 deletions

View File

@@ -41,7 +41,9 @@ Key Features
Installing Installing
---------- ----------
**Python 3.8 or higher is required** **Python 3.8 or higher is required***
***Do not use 3.9.7 as Python 3.9.7 has a bug that causes a TypeError with __init__ methods in subclasses.** View more `here <https://bugs.python.org/issue45121/>`_.
To install the library without full voice support, you can just run the following command: To install the library without full voice support, you can just run the following command:

View File

@@ -28,6 +28,7 @@ from __future__ import annotations
import asyncio import asyncio
import collections import collections
import collections.abc import collections.abc
from functools import cached_property
import inspect import inspect
import importlib.util import importlib.util
@@ -72,7 +73,9 @@ from .cog import Cog
if TYPE_CHECKING: if TYPE_CHECKING:
import importlib.machinery import importlib.machinery
from discord.role import Role
from discord.message import Message from discord.message import Message
from discord.abc import PartialMessageableChannel
from ._types import ( from ._types import (
Check, Check,
CoroFunc, CoroFunc,
@@ -94,10 +97,17 @@ CXT = TypeVar("CXT", bound="Context")
class _FakeSlashMessage(discord.PartialMessage): class _FakeSlashMessage(discord.PartialMessage):
activity = application = edited_at = reference = webhook_id = None activity = application = edited_at = reference = webhook_id = None
attachments = components = reactions = stickers = mentions = [] attachments = components = reactions = stickers = []
author: Union[discord.User, discord.Member]
tts = False tts = False
raw_mentions = discord.Message.raw_mentions
clean_content = discord.Message.clean_content
channel_mentions = discord.Message.channel_mentions
raw_role_mentions = discord.Message.raw_role_mentions
raw_channel_mentions = discord.Message.raw_channel_mentions
author: Union[discord.User, discord.Member]
@classmethod @classmethod
def from_interaction( def from_interaction(
cls, interaction: discord.Interaction, channel: Union[discord.TextChannel, discord.DMChannel, discord.Thread] cls, interaction: discord.Interaction, channel: Union[discord.TextChannel, discord.DMChannel, discord.Thread]
@@ -108,6 +118,22 @@ class _FakeSlashMessage(discord.PartialMessage):
return self return self
@cached_property
def mentions(self) -> List[Union[discord.Member, discord.User]]:
client = self._state._get_client()
if self.guild:
ensure_user = lambda id: self.guild.get_member(id) or client.get_user(id) # type: ignore
else:
ensure_user = client.get_user
return discord.utils._unique(filter(None, map(ensure_user, self.raw_mentions)))
@cached_property
def role_mentions(self) -> List[Role]:
if self.guild is None:
return []
return discord.utils._unique(filter(None, map(self.guild.get_role, self.raw_role_mentions)))
def when_mentioned(bot: Union[Bot, AutoShardedBot], msg: Message) -> List[str]: def when_mentioned(bot: Union[Bot, AutoShardedBot], msg: Message) -> List[str]:
"""A callable that implements a command prefix equivalent to being mentioned. """A callable that implements a command prefix equivalent to being mentioned.

View File

@@ -465,6 +465,7 @@ class Context(discord.abc.Messageable, Generic[BotT]):
kwargs.pop("nonce", None) kwargs.pop("nonce", None)
kwargs.pop("stickers", None) kwargs.pop("stickers", None)
kwargs.pop("reference", None) kwargs.pop("reference", None)
kwargs.pop("delete_after", None)
kwargs.pop("mention_author", None) kwargs.pop("mention_author", None)
if not ( if not (

View File

@@ -188,7 +188,7 @@ class HTTPClient:
self.proxy_auth: Optional[aiohttp.BasicAuth] = proxy_auth self.proxy_auth: Optional[aiohttp.BasicAuth] = proxy_auth
self.use_clock: bool = not unsync_clock self.use_clock: bool = not unsync_clock
user_agent = "DiscordBot (https://github.com/Rapptz/discord.py {0}) Python/{1[0]}.{1[1]} aiohttp/{2}" user_agent = "DiscordBot (https://github.com/iDevision/enhanced-discord.py {0}) Python/{1[0]}.{1[1]} aiohttp/{2}"
self.user_agent: str = user_agent.format(__version__, sys.version_info, aiohttp.__version__) self.user_agent: str = user_agent.format(__version__, sys.version_info, aiohttp.__version__)
def recreate(self) -> None: def recreate(self) -> None:

View File

@@ -337,7 +337,7 @@ class Interaction:
self._state.store_view(view, message.id) self._state.store_view(view, message.id)
return message return message
async def delete_original_message(self, delay: Optional[float] = None) -> None: async def delete_original_message(self) -> None:
"""|coro| """|coro|
Deletes the original interaction response message. Deletes the original interaction response message.
@@ -345,14 +345,6 @@ class Interaction:
This is a lower level interface to :meth:`InteractionMessage.delete` in case This is a lower level interface to :meth:`InteractionMessage.delete` in case
you do not want to fetch the message and save an HTTP request. you do not want to fetch the message and save an HTTP request.
Parameters
------------
delay: Optional[:class:`float`]
If provided, the number of seconds to wait before deleting the message.
The waiting is done in the background and deletion failures are ignored.
.. versionadded:: 2.0
Raises Raises
------- -------
HTTPException HTTPException
@@ -360,8 +352,6 @@ class Interaction:
Forbidden Forbidden
Deleted a message that is not yours. Deleted a message that is not yours.
""" """
if delay is not None:
await asyncio.sleep(delay)
adapter = async_context.get() adapter = async_context.get()
await adapter.delete_original_interaction_response( await adapter.delete_original_interaction_response(
self.application_id, self.application_id,
@@ -470,7 +460,6 @@ class InteractionResponse:
view: View = MISSING, view: View = MISSING,
tts: bool = False, tts: bool = False,
ephemeral: bool = False, ephemeral: bool = False,
delete_after: Optional[float] = None,
) -> None: ) -> None:
"""|coro| """|coro|
@@ -494,9 +483,6 @@ class InteractionResponse:
Indicates if the message should only be visible to the user who started the interaction. Indicates if the message should only be visible to the user who started the interaction.
If a view is sent with an ephemeral message and it has no timeout set then the timeout If a view is sent with an ephemeral message and it has no timeout set then the timeout
is set to 15 minutes. is set to 15 minutes.
delete_after: Optional[:class:`float`]
The amount of seconds the bot should wait before deleting the message sent.
.. versionadded:: 2.0
Raises Raises
------- -------
@@ -553,8 +539,6 @@ class InteractionResponse:
self._parent._state.store_view(view) self._parent._state.store_view(view)
self.responded_at = utils.utcnow() self.responded_at = utils.utcnow()
if delete_after is not None:
self._parent.delete_original_message(delay=delete_after)
async def edit_message( async def edit_message(
self, self,
@@ -749,7 +733,7 @@ class InteractionMessage(Message):
allowed_mentions=allowed_mentions, allowed_mentions=allowed_mentions,
) )
async def delete(self, *, delay: Optional[float] = None, silent: bool = False) -> None: async def delete(self, *, delay: Optional[float] = None) -> None:
"""|coro| """|coro|
Deletes the message. Deletes the message.
@@ -759,12 +743,6 @@ class InteractionMessage(Message):
delay: Optional[:class:`float`] delay: Optional[:class:`float`]
If provided, the number of seconds to wait before deleting the message. If provided, the number of seconds to wait before deleting the message.
The waiting is done in the background and deletion failures are ignored. The waiting is done in the background and deletion failures are ignored.
silent: :class:`bool`
If silent is set to ``True``, the error will not be raised, it will be ignored.
This defaults to ``False`
.. versionadded:: 2.0
Raises Raises
------ ------
@@ -779,15 +757,12 @@ class InteractionMessage(Message):
if delay is not None: if delay is not None:
async def inner_call(delay: float = delay): async def inner_call(delay: float = delay):
await asyncio.sleep(delay)
try: try:
await self._state._interaction.delete_original_message(delay=delay) await self._state._interaction.delete_original_message()
except HTTPException: except HTTPException:
pass pass
asyncio.create_task(inner_call()) asyncio.create_task(inner_call())
else: else:
try: await self._state._interaction.delete_original_message()
await self._state._interaction.delete_original_message(delay=delay)
except Exception:
if not silent:
raise

View File

@@ -717,7 +717,7 @@ class WebhookMessage(Message):
allowed_mentions=allowed_mentions, allowed_mentions=allowed_mentions,
) )
async def delete(self, *, delay: Optional[float] = None, silent: bool = False) -> None: async def delete(self, *, delay: Optional[float] = None) -> None:
"""|coro| """|coro|
Deletes the message. Deletes the message.
@@ -727,12 +727,6 @@ class WebhookMessage(Message):
delay: Optional[:class:`float`] delay: Optional[:class:`float`]
If provided, the number of seconds to wait before deleting the message. If provided, the number of seconds to wait before deleting the message.
The waiting is done in the background and deletion failures are ignored. The waiting is done in the background and deletion failures are ignored.
silent: :class:`bool`
If silent is set to ``True``, the error will not be raised, it will be ignored.
This defaults to ``False`
.. versionadded:: 2.0
Raises Raises
------ ------
@@ -747,18 +741,15 @@ class WebhookMessage(Message):
if delay is not None: if delay is not None:
async def inner_call(delay: float = delay): async def inner_call(delay: float = delay):
await asyncio.sleep(delay)
try: try:
await self._state._webhook.delete_message(self.id, delay) await self._state._webhook.delete_message(self.id)
except HTTPException: except HTTPException:
pass pass
asyncio.create_task(inner_call()) asyncio.create_task(inner_call())
else: else:
try: await self._state._webhook.delete_message(self.id)
await self._state._webhook.delete_message(self.id, delay)
except Exception:
if not silent:
raise
class BaseWebhook(Hashable): class BaseWebhook(Hashable):
@@ -1279,7 +1270,6 @@ class Webhook(BaseWebhook):
view: View = MISSING, view: View = MISSING,
thread: Snowflake = MISSING, thread: Snowflake = MISSING,
wait: bool = False, wait: bool = False,
delete_after: Optional[float] = None,
) -> Optional[WebhookMessage]: ) -> Optional[WebhookMessage]:
"""|coro| """|coro|
@@ -1345,11 +1335,6 @@ class Webhook(BaseWebhook):
The thread to send this webhook to. The thread to send this webhook to.
.. versionadded:: 2.0 .. versionadded:: 2.0
delete_after: Optional[:class:`float`]
If provided, the number of seconds to wait before deleting the message.
The waiting is done in the background and deletion failures are ignored.
.. versionadded:: 2.0
Raises Raises
-------- --------
@@ -1431,9 +1416,6 @@ class Webhook(BaseWebhook):
if view is not MISSING and not view.is_finished(): if view is not MISSING and not view.is_finished():
message_id = None if msg is None else msg.id message_id = None if msg is None else msg.id
self._state.store_view(view, message_id) self._state.store_view(view, message_id)
if delete_after is not None and msg is not None:
await msg.delete(delay=delete_after)
return msg return msg
@@ -1588,7 +1570,7 @@ class Webhook(BaseWebhook):
self._state.store_view(view, message_id) self._state.store_view(view, message_id)
return message return message
async def delete_message(self, message_id: int, delay: Optional[float] = None, /) -> None: async def delete_message(self, message_id: int, /) -> None:
"""|coro| """|coro|
Deletes a message owned by this webhook. Deletes a message owned by this webhook.
@@ -1602,12 +1584,6 @@ class Webhook(BaseWebhook):
------------ ------------
message_id: :class:`int` message_id: :class:`int`
The message ID to delete. The message ID to delete.
delay: Optional[:class:`float`]
If provided, the number of seconds to wait before deleting the message.
The waiting is done in the background and deletion failures are ignored.
.. versionadded:: 2.0
Raises Raises
------- -------
@@ -1619,9 +1595,6 @@ class Webhook(BaseWebhook):
if self.token is None: if self.token is None:
raise InvalidArgument("This webhook does not have a token associated with it") raise InvalidArgument("This webhook does not have a token associated with it")
if delay is not None:
await asyncio.sleep(delay)
adapter = async_context.get() adapter = async_context.get()
await adapter.delete_webhook_message( await adapter.delete_webhook_message(
self.id, self.id,

View File

@@ -70,7 +70,7 @@ setup(
include_package_data=True, include_package_data=True,
install_requires=requirements, install_requires=requirements,
extras_require=extras_require, extras_require=extras_require,
python_requires=">=3.8.0", python_requires=">=3.8.0, !=3.9.7",
classifiers=[ classifiers=[
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",