Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions interactions/api/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def log(self, message: str, *args):

@staticmethod
def lookup(code: int) -> str:
# https://discord.com/developers/docs/topics/opcodes-and-status-codes#json
return {
# Default error integer enum
0: "Unknown error",
Expand Down Expand Up @@ -197,6 +198,7 @@ def lookup(code: int) -> str:
30047: "Maximum number of pinned threads in a forum channel has been reached",
30048: "Maximum number of tags in a forum channel has been reached",
30052: "Bitrate is too high for channel of this type",
31001: "Undocumented error/rate-limit related.",
40001: "Unauthorized. Provide a valid token and try again",
40002: "You need to verify your account in order to perform this action",
40003: "You are opening direct messages too fast",
Expand Down
12 changes: 6 additions & 6 deletions interactions/api/http/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
self.buckets[route.endpoint] = _bucket
# real-time replacement/update/add if needed.
if isinstance(data, dict) and (
data.get("errors") or (code and code != 429 and message)
data.get("errors") or (code and code not in {429, 31001} and message)
):
log.debug(
f"RETURN {response.status}: {dumps(data, indent=4, sort_keys=True)}"
Expand All @@ -189,15 +189,15 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
message=f"'{message}'. Make sure that your token is set properly.",
severity=50,
)
if code == 429:
if code in {429, 31001}:
hours = int(reset_after // 3600)
minutes = int((reset_after % 3600) // 60)
seconds = int(reset_after % 60)
log.warning(
f"(429) {LibraryException.lookup(429)} Locking down future requests for "
+ f"{f'{hours} hours ' if hours else ''}"
+ f"{f'{minutes} minutes ' if minutes else ''}"
+ f"{f'{seconds} seconds ' if seconds else ''}"
"(429/31001) The Bot has encountered a rate-limit. Resuming future requests after "
f"{f'{hours} hours ' if hours else ''}"
f"{f'{minutes} minutes ' if minutes else ''}"
f"{f'{seconds} seconds ' if seconds else ''}"
)
if is_global:
self._global_lock.reset_after = reset_after
Expand Down
57 changes: 41 additions & 16 deletions interactions/api/models/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from ...utils.utils import search_iterable
from ..error import LibraryException
from .emoji import Emoji
from .flags import Permissions
from .flags import MessageFlags, Permissions
from .misc import AllowedMentions, File, IDMixin, Overwrite, Snowflake
from .role import Role
from .user import User
Expand Down Expand Up @@ -1178,11 +1178,17 @@ async def normal_delete():
before=_before,
)
]
if not messages:
return _all

amount -= min(amount, 100)
messages2 = messages.copy()
for message in messages2:
if message.flags == (1 << 7):
if (
message.flags & MessageFlags.EPHEMERAL
or message.flags & MessageFlags.LOADING
or not message.deletable
):
messages.remove(message)
amount += 1
_before = int(message.id)
Expand All @@ -1194,14 +1200,17 @@ async def normal_delete():
messages.remove(message)
amount += 1
_before = int(message.id)

for message in messages: # show results faster
await self._client.delete_message(
channel_id=int(self.id),
message_id=int(message.id),
reason=reason,
)

_all += messages

for message in _all:
await self._client.delete_message(
channel_id=int(self.id),
message_id=int(message.id),
reason=reason,
)
return _all

async def bulk_delete():
nonlocal _before, _all, amount, check, reason
Expand All @@ -1218,14 +1227,19 @@ async def bulk_delete():
before=_before,
)
]
if not messages:
return _all
messages2 = messages.copy()
for message in messages2:
if datetime.fromisoformat(str(message.timestamp)) < _allowed_time:
messages.remove(message)
_stop = True
messages2 = messages.copy()
for message in messages2:
if message.flags == (1 << 7):

elif (
message.flags & MessageFlags.EPHEMERAL
or message.flags & MessageFlags.LOADING
or not message.deletable
):
messages.remove(message)
amount += 1
_before = int(message.id)
Expand Down Expand Up @@ -1268,15 +1282,19 @@ async def bulk_delete():
before=_before,
)
]
if not messages:
return _all
amount -= amount
messages2 = messages.copy()
for message in messages2:
if datetime.fromisoformat(str(message.timestamp)) < _allowed_time:
messages.remove(message)
_stop = True
amount -= amount
messages2 = messages.copy()
for message in messages2:
if message.flags == (1 << 7):
elif (
message.flags & MessageFlags.EPHEMERAL
or message.flags & MessageFlags.LOADING
or not message.deletable
):
messages.remove(message)
amount += 1
_before = int(message.id)
Expand Down Expand Up @@ -1316,10 +1334,16 @@ async def bulk_delete():
before=_before,
)
]
if not messages:
return _all
amount -= 1
messages2 = messages.copy()
for message in messages2:
if message.flags == (1 << 7):
if (
message.flags & MessageFlags.EPHEMERAL
or message.flags & MessageFlags.LOADING
or not message.deletable
):
messages.remove(message)
amount += 1
_before = int(message.id)
Expand All @@ -1339,6 +1363,7 @@ async def bulk_delete():
message_id=int(messages[0].id),
reason=reason,
)
return _all

if bulk:
await bulk_delete()
Expand Down
30 changes: 29 additions & 1 deletion interactions/api/models/flags.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum, IntFlag

__all__ = ("Intents", "AppFlags", "StatusType", "UserFlags", "Permissions")
__all__ = ("Intents", "AppFlags", "StatusType", "UserFlags", "Permissions", "MessageFlags")


class Intents(IntFlag):
Expand Down Expand Up @@ -186,3 +186,31 @@ class StatusType(str, Enum):
IDLE = "idle"
INVISIBLE = "invisible"
OFFLINE = "offline"


class MessageFlags(IntFlag):
"""
.. versionadded:: 4.4.0

An integer flag bitshift object representing the different message flags given by Discord.

:ivar int CROSSPOSTED: this message has been published to subscribed channels (via Channel Following)
:ivar int IS_CROSSPOST: this message originated from a message in another channel (via Channel Following)
:ivar int SUPPRESS_EMBEDS: do not include any embeds when serializing this message
:ivar int SOURCE_MESSAGE_DELETED: the source message for this crosspost has been deleted (via Channel Following)
:ivar int URGENT: this message came from the urgent message system
:ivar int HAS_THREAD: this message has an associated thread, with the same id as the message
:ivar int EPHEMERAL: this message is only visible to the user who invoked the Interaction
:ivar int LOADING: this message is an Interaction Response and the bot is thinking
:ivar int FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: this message failed to mention some roles and add their members to the thread
"""

CROSSPOSTED = 1 << 0
IS_CROSSPOST = 1 << 1
SUPPRESS_EMBEDS = 1 << 2
SOURCE_MESSAGE_DELETED = 1 << 3
URGENT = 1 << 4
HAS_THREAD = 1 << 5
EPHEMERAL = 1 << 6
LOADING = 1 << 7
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8
25 changes: 22 additions & 3 deletions interactions/api/models/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from ..error import LibraryException
from .channel import Channel
from .emoji import Emoji
from .flags import MessageFlags
from .member import Member
from .misc import AllowedMentions, File, IDMixin, Snowflake
from .team import Application
Expand Down Expand Up @@ -75,6 +76,15 @@ class MessageType(IntEnum):
CONTEXT_MENU_COMMAND = 23
AUTO_MODERATION_ACTION = 24

@staticmethod
def not_deletable() -> List[int]:
"""
.. versionadded:: 4.4.0

returns A list of message types which are not deletable
"""
return [1, 2, 3, 4, 5, 14, 15, 16, 17, 21]


@define()
class MessageActivity(DictSerializerMixin):
Expand Down Expand Up @@ -692,7 +702,7 @@ class Message(ClientSerializerMixin, IDMixin):
application: Optional[Application] = field(converter=Application, default=None)
application_id: Optional[Snowflake] = field(converter=Snowflake, default=None)
message_reference: Optional[MessageReference] = field(converter=MessageReference, default=None)
flags: int = field(default=None)
flags: Optional[Union[int, MessageFlags]] = field(converter=MessageFlags, default=None)
referenced_message: Optional[MessageReference] = field(converter=MessageReference, default=None)
interaction: Optional[MessageInteraction] = field(
converter=MessageInteraction, default=None, add_client=True, repr=False
Expand All @@ -708,6 +718,15 @@ class Message(ClientSerializerMixin, IDMixin):
) # deprecated
position: Optional[int] = field(default=None, repr=False)

@property
def deletable(self) -> bool:
"""
.. versionadded:: 4.4.0

Returns if the message can be deleted or not
"""
return self.type not in self.type.not_deletable()

def __attrs_post_init__(self):
if self.member and self.guild_id:
self.member._extras["guild_id"] = self.guild_id
Expand Down Expand Up @@ -793,9 +812,9 @@ async def edit(
raise LibraryException(message="You cannot edit a hidden message!", code=12)
_flags = self.flags
if suppress_embeds is not MISSING and suppress_embeds:
_flags |= 1 << 2
_flags |= MessageFlags.SUPPRESS_EMBEDS
elif suppress_embeds is not MISSING:
_flags &= ~1 << 2
_flags &= ~MessageFlags.SUPPRESS_EMBEDS

from ...client.models.component import _build_components

Expand Down
12 changes: 6 additions & 6 deletions interactions/client/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from ..api.error import LibraryException
from ..api.models.channel import Channel
from ..api.models.flags import Permissions
from ..api.models.flags import MessageFlags, Permissions
from ..api.models.guild import Guild
from ..api.models.member import Member
from ..api.models.message import Attachment, Embed, File, Message, MessageReference
Expand Down Expand Up @@ -180,9 +180,9 @@ async def send(
else:
_components = []

_flags: int = (1 << 6) if ephemeral else 0
_flags = MessageFlags.EPHEMERAL if ephemeral else MessageFlags(0)
if suppress_embeds:
_flags += 1 << 2
_flags |= MessageFlags.SUPPRESS_EMBEDS

_attachments = [] if attachments is MISSING else [a._json for a in attachments]

Expand All @@ -204,7 +204,7 @@ async def send(
allowed_mentions=_allowed_mentions,
components=_components,
attachments=_files,
flags=_flags,
flags=_flags.value,
),
files,
)
Expand Down Expand Up @@ -476,7 +476,7 @@ async def defer(self, ephemeral: Optional[bool] = False) -> None:
"""
if not self.responded:
self.deferred = True
_ephemeral: int = (1 << 6) if ephemeral else 0
_ephemeral: int = MessageFlags.EPHEMERAL.value if ephemeral else 0
self.callback = InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE

await self._client.create_interaction_response(
Expand Down Expand Up @@ -720,7 +720,7 @@ async def defer(
if not self.responded:

self.deferred = True
_ephemeral: int = (1 << 6) if bool(ephemeral) else 0
_ephemeral: int = MessageFlags.EPHEMERAL.value if bool(ephemeral) else 0

# ephemeral doesn't change callback typings. just data json
if edit_origin:
Expand Down