mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-07-11 12:28:03 +00:00
Add support for sending messages and managing webhooks in VoiceChannel
This commit is contained in:
parent
b049cf77f2
commit
2aca705b95
@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -32,6 +33,7 @@ from typing import (
|
|||||||
AsyncIterator,
|
AsyncIterator,
|
||||||
Callable,
|
Callable,
|
||||||
Dict,
|
Dict,
|
||||||
|
Iterable,
|
||||||
List,
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
@ -81,7 +83,7 @@ if TYPE_CHECKING:
|
|||||||
from .channel import CategoryChannel
|
from .channel import CategoryChannel
|
||||||
from .embeds import Embed
|
from .embeds import Embed
|
||||||
from .message import Message, MessageReference, PartialMessage
|
from .message import Message, MessageReference, PartialMessage
|
||||||
from .channel import TextChannel, DMChannel, GroupChannel, PartialMessageable
|
from .channel import TextChannel, DMChannel, GroupChannel, PartialMessageable, VoiceChannel
|
||||||
from .threads import Thread
|
from .threads import Thread
|
||||||
from .enums import InviteTarget
|
from .enums import InviteTarget
|
||||||
from .ui.view import View
|
from .ui.view import View
|
||||||
@ -95,7 +97,7 @@ if TYPE_CHECKING:
|
|||||||
SnowflakeList,
|
SnowflakeList,
|
||||||
)
|
)
|
||||||
|
|
||||||
PartialMessageableChannel = Union[TextChannel, Thread, DMChannel, PartialMessageable]
|
PartialMessageableChannel = Union[TextChannel, VoiceChannel, Thread, DMChannel, PartialMessageable]
|
||||||
MessageableChannel = Union[PartialMessageableChannel, GroupChannel]
|
MessageableChannel = Union[PartialMessageableChannel, GroupChannel]
|
||||||
SnowflakeTime = Union["Snowflake", datetime]
|
SnowflakeTime = Union["Snowflake", datetime]
|
||||||
|
|
||||||
@ -110,6 +112,69 @@ class _Undefined:
|
|||||||
_undefined: Any = _Undefined()
|
_undefined: Any = _Undefined()
|
||||||
|
|
||||||
|
|
||||||
|
async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None):
|
||||||
|
for m in messages:
|
||||||
|
await m.delete()
|
||||||
|
|
||||||
|
|
||||||
|
async def _purge_helper(
|
||||||
|
channel: Union[Thread, TextChannel, VoiceChannel],
|
||||||
|
*,
|
||||||
|
limit: Optional[int] = 100,
|
||||||
|
check: Callable[[Message], bool] = MISSING,
|
||||||
|
before: Optional[SnowflakeTime] = None,
|
||||||
|
after: Optional[SnowflakeTime] = None,
|
||||||
|
around: Optional[SnowflakeTime] = None,
|
||||||
|
oldest_first: Optional[bool] = False,
|
||||||
|
bulk: bool = True,
|
||||||
|
reason: Optional[str] = None,
|
||||||
|
) -> List[Message]:
|
||||||
|
if check is MISSING:
|
||||||
|
check = lambda m: True
|
||||||
|
|
||||||
|
iterator = channel.history(limit=limit, before=before, after=after, oldest_first=oldest_first, around=around)
|
||||||
|
ret: List[Message] = []
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22
|
||||||
|
strategy = channel.delete_messages if bulk else _single_delete_strategy
|
||||||
|
|
||||||
|
async for message in iterator:
|
||||||
|
if count == 100:
|
||||||
|
to_delete = ret[-100:]
|
||||||
|
await strategy(to_delete, reason=reason)
|
||||||
|
count = 0
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
if not check(message):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if message.id < minimum_time:
|
||||||
|
# older than 14 days old
|
||||||
|
if count == 1:
|
||||||
|
await ret[-1].delete()
|
||||||
|
elif count >= 2:
|
||||||
|
to_delete = ret[-count:]
|
||||||
|
await strategy(to_delete, reason=reason)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
strategy = _single_delete_strategy
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
ret.append(message)
|
||||||
|
|
||||||
|
# Some messages remaining to poll
|
||||||
|
if count >= 2:
|
||||||
|
# more than 2 messages -> bulk delete
|
||||||
|
to_delete = ret[-count:]
|
||||||
|
await strategy(to_delete, reason=reason)
|
||||||
|
elif count == 1:
|
||||||
|
# delete a single message
|
||||||
|
await ret[-1].delete()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@runtime_checkable
|
@runtime_checkable
|
||||||
class Snowflake(Protocol):
|
class Snowflake(Protocol):
|
||||||
"""An ABC that details the common operations on a Discord model.
|
"""An ABC that details the common operations on a Discord model.
|
||||||
|
@ -24,8 +24,6 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import time
|
|
||||||
import asyncio
|
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
AsyncIterator,
|
AsyncIterator,
|
||||||
@ -87,11 +85,6 @@ if TYPE_CHECKING:
|
|||||||
from .types.snowflake import SnowflakeList
|
from .types.snowflake import SnowflakeList
|
||||||
|
|
||||||
|
|
||||||
async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None):
|
|
||||||
for m in messages:
|
|
||||||
await m.delete()
|
|
||||||
|
|
||||||
|
|
||||||
class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
||||||
"""Represents a Discord guild text channel.
|
"""Represents a Discord guild text channel.
|
||||||
|
|
||||||
@ -493,51 +486,17 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
|||||||
List[:class:`.Message`]
|
List[:class:`.Message`]
|
||||||
The list of messages that were deleted.
|
The list of messages that were deleted.
|
||||||
"""
|
"""
|
||||||
|
return await discord.abc._purge_helper(
|
||||||
if check is MISSING:
|
self,
|
||||||
check = lambda m: True
|
limit=limit,
|
||||||
|
check=check,
|
||||||
iterator = self.history(limit=limit, before=before, after=after, oldest_first=oldest_first, around=around)
|
before=before,
|
||||||
ret: List[Message] = []
|
after=after,
|
||||||
count = 0
|
around=around,
|
||||||
|
oldest_first=oldest_first,
|
||||||
minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22
|
bulk=bulk,
|
||||||
strategy = self.delete_messages if bulk else _single_delete_strategy
|
reason=reason,
|
||||||
|
)
|
||||||
async for message in iterator:
|
|
||||||
if count == 100:
|
|
||||||
to_delete = ret[-100:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
count = 0
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
if not check(message):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if message.id < minimum_time:
|
|
||||||
# older than 14 days old
|
|
||||||
if count == 1:
|
|
||||||
await ret[-1].delete()
|
|
||||||
elif count >= 2:
|
|
||||||
to_delete = ret[-count:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
strategy = _single_delete_strategy
|
|
||||||
|
|
||||||
count += 1
|
|
||||||
ret.append(message)
|
|
||||||
|
|
||||||
# Some messages remaining to poll
|
|
||||||
if count >= 2:
|
|
||||||
# more than 2 messages -> bulk delete
|
|
||||||
to_delete = ret[-count:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
elif count == 1:
|
|
||||||
# delete a single message
|
|
||||||
await ret[-1].delete()
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
async def webhooks(self) -> List[Webhook]:
|
async def webhooks(self) -> List[Webhook]:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
@ -892,6 +851,7 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha
|
|||||||
'category_id',
|
'category_id',
|
||||||
'rtc_region',
|
'rtc_region',
|
||||||
'video_quality_mode',
|
'video_quality_mode',
|
||||||
|
'last_message_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *, state: ConnectionState, guild: Guild, data: Union[VoiceChannelPayload, StageChannelPayload]):
|
def __init__(self, *, state: ConnectionState, guild: Guild, data: Union[VoiceChannelPayload, StageChannelPayload]):
|
||||||
@ -911,6 +871,7 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha
|
|||||||
self.rtc_region: Optional[str] = data.get('rtc_region')
|
self.rtc_region: Optional[str] = data.get('rtc_region')
|
||||||
self.video_quality_mode: VideoQualityMode = try_enum(VideoQualityMode, data.get('video_quality_mode', 1))
|
self.video_quality_mode: VideoQualityMode = try_enum(VideoQualityMode, data.get('video_quality_mode', 1))
|
||||||
self.category_id: Optional[int] = utils._get_as_snowflake(data, 'parent_id')
|
self.category_id: Optional[int] = utils._get_as_snowflake(data, 'parent_id')
|
||||||
|
self.last_message_id: Optional[int] = utils._get_as_snowflake(data, 'last_message_id')
|
||||||
self.position: int = data['position']
|
self.position: int = data['position']
|
||||||
self.bitrate: int = data['bitrate']
|
self.bitrate: int = data['bitrate']
|
||||||
self.user_limit: int = data['user_limit']
|
self.user_limit: int = data['user_limit']
|
||||||
@ -976,7 +937,7 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha
|
|||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
class VoiceChannel(VocalGuildChannel):
|
class VoiceChannel(discord.abc.Messageable, VocalGuildChannel):
|
||||||
"""Represents a Discord guild voice channel.
|
"""Represents a Discord guild voice channel.
|
||||||
|
|
||||||
.. container:: operations
|
.. container:: operations
|
||||||
@ -1025,6 +986,11 @@ class VoiceChannel(VocalGuildChannel):
|
|||||||
video_quality_mode: :class:`VideoQualityMode`
|
video_quality_mode: :class:`VideoQualityMode`
|
||||||
The camera video quality for the voice channel's participants.
|
The camera video quality for the voice channel's participants.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
last_message_id: Optional[:class:`int`]
|
||||||
|
The last message ID of the message sent to this channel. It may
|
||||||
|
*not* point to an existing or valid message.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -1044,11 +1010,262 @@ class VoiceChannel(VocalGuildChannel):
|
|||||||
joined = ' '.join('%s=%r' % t for t in attrs)
|
joined = ' '.join('%s=%r' % t for t in attrs)
|
||||||
return f'<{self.__class__.__name__} {joined}>'
|
return f'<{self.__class__.__name__} {joined}>'
|
||||||
|
|
||||||
|
async def _get_channel(self) -> Self:
|
||||||
|
return self
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> ChannelType:
|
def type(self) -> ChannelType:
|
||||||
""":class:`ChannelType`: The channel's Discord type."""
|
""":class:`ChannelType`: The channel's Discord type."""
|
||||||
return ChannelType.voice
|
return ChannelType.voice
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_message(self) -> Optional[Message]:
|
||||||
|
"""Fetches the last message from this channel in cache.
|
||||||
|
|
||||||
|
The message might not be valid or point to an existing message.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
.. admonition:: Reliable Fetching
|
||||||
|
:class: helpful
|
||||||
|
|
||||||
|
For a slightly more reliable method of fetching the
|
||||||
|
last message, consider using either :meth:`history`
|
||||||
|
or :meth:`fetch_message` with the :attr:`last_message_id`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
Optional[:class:`Message`]
|
||||||
|
The last message in this channel or ``None`` if not found.
|
||||||
|
"""
|
||||||
|
return self._state._get_message(self.last_message_id) if self.last_message_id else None
|
||||||
|
|
||||||
|
def get_partial_message(self, message_id: int, /) -> PartialMessage:
|
||||||
|
"""Creates a :class:`PartialMessage` from the message ID.
|
||||||
|
|
||||||
|
This is useful if you want to work with a message and only have its ID without
|
||||||
|
doing an unnecessary API call.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
message_id: :class:`int`
|
||||||
|
The message ID to create a partial message for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
:class:`PartialMessage`
|
||||||
|
The partial message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .message import PartialMessage
|
||||||
|
|
||||||
|
return PartialMessage(channel=self, id=message_id)
|
||||||
|
|
||||||
|
async def delete_messages(self, messages: Iterable[Snowflake], *, reason: Optional[str] = None) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Deletes a list of messages. This is similar to :meth:`Message.delete`
|
||||||
|
except it bulk deletes multiple messages.
|
||||||
|
|
||||||
|
As a special case, if the number of messages is 0, then nothing
|
||||||
|
is done. If the number of messages is 1 then single message
|
||||||
|
delete is done. If it's more than two, then bulk delete is used.
|
||||||
|
|
||||||
|
You cannot bulk delete more than 100 messages or messages that
|
||||||
|
are older than 14 days old.
|
||||||
|
|
||||||
|
You must have the :attr:`~Permissions.manage_messages` permission to
|
||||||
|
use this.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
messages: Iterable[:class:`abc.Snowflake`]
|
||||||
|
An iterable of messages denoting which ones to bulk delete.
|
||||||
|
reason: Optional[:class:`str`]
|
||||||
|
The reason for deleting the messages. Shows up on the audit log.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ClientException
|
||||||
|
The number of messages to delete was more than 100.
|
||||||
|
Forbidden
|
||||||
|
You do not have proper permissions to delete the messages.
|
||||||
|
NotFound
|
||||||
|
If single delete, then the message was already deleted.
|
||||||
|
HTTPException
|
||||||
|
Deleting the messages failed.
|
||||||
|
"""
|
||||||
|
if not isinstance(messages, (list, tuple)):
|
||||||
|
messages = list(messages)
|
||||||
|
|
||||||
|
if len(messages) == 0:
|
||||||
|
return # do nothing
|
||||||
|
|
||||||
|
if len(messages) == 1:
|
||||||
|
message_id: int = messages[0].id
|
||||||
|
await self._state.http.delete_message(self.id, message_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(messages) > 100:
|
||||||
|
raise ClientException('Can only bulk delete messages up to 100 messages')
|
||||||
|
|
||||||
|
message_ids: SnowflakeList = [m.id for m in messages]
|
||||||
|
await self._state.http.delete_messages(self.id, message_ids, reason=reason)
|
||||||
|
|
||||||
|
async def purge(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
limit: Optional[int] = 100,
|
||||||
|
check: Callable[[Message], bool] = MISSING,
|
||||||
|
before: Optional[SnowflakeTime] = None,
|
||||||
|
after: Optional[SnowflakeTime] = None,
|
||||||
|
around: Optional[SnowflakeTime] = None,
|
||||||
|
oldest_first: Optional[bool] = False,
|
||||||
|
bulk: bool = True,
|
||||||
|
reason: Optional[str] = None,
|
||||||
|
) -> List[Message]:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Purges a list of messages that meet the criteria given by the predicate
|
||||||
|
``check``. If a ``check`` is not provided then all messages are deleted
|
||||||
|
without discrimination.
|
||||||
|
|
||||||
|
You must have the :attr:`~Permissions.manage_messages` permission to
|
||||||
|
delete messages even if they are your own.
|
||||||
|
The :attr:`~Permissions.read_message_history` permission is
|
||||||
|
also needed to retrieve message history.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Examples
|
||||||
|
---------
|
||||||
|
|
||||||
|
Deleting bot's messages ::
|
||||||
|
|
||||||
|
def is_me(m):
|
||||||
|
return m.author == client.user
|
||||||
|
|
||||||
|
deleted = await channel.purge(limit=100, check=is_me)
|
||||||
|
await channel.send(f'Deleted {len(deleted)} message(s)')
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
limit: Optional[:class:`int`]
|
||||||
|
The number of messages to search through. This is not the number
|
||||||
|
of messages that will be deleted, though it can be.
|
||||||
|
check: Callable[[:class:`Message`], :class:`bool`]
|
||||||
|
The function used to check if a message should be deleted.
|
||||||
|
It must take a :class:`Message` as its sole parameter.
|
||||||
|
before: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
|
||||||
|
Same as ``before`` in :meth:`history`.
|
||||||
|
after: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
|
||||||
|
Same as ``after`` in :meth:`history`.
|
||||||
|
around: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
|
||||||
|
Same as ``around`` in :meth:`history`.
|
||||||
|
oldest_first: Optional[:class:`bool`]
|
||||||
|
Same as ``oldest_first`` in :meth:`history`.
|
||||||
|
bulk: :class:`bool`
|
||||||
|
If ``True``, use bulk delete. Setting this to ``False`` is useful for mass-deleting
|
||||||
|
a bot's own messages without :attr:`Permissions.manage_messages`. When ``True``, will
|
||||||
|
fall back to single delete if messages are older than two weeks.
|
||||||
|
reason: Optional[:class:`str`]
|
||||||
|
The reason for purging the messages. Shows up on the audit log.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have proper permissions to do the actions required.
|
||||||
|
HTTPException
|
||||||
|
Purging the messages failed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
List[:class:`.Message`]
|
||||||
|
The list of messages that were deleted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await discord.abc._purge_helper(
|
||||||
|
self,
|
||||||
|
limit=limit,
|
||||||
|
check=check,
|
||||||
|
before=before,
|
||||||
|
after=after,
|
||||||
|
around=around,
|
||||||
|
oldest_first=oldest_first,
|
||||||
|
bulk=bulk,
|
||||||
|
reason=reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def webhooks(self) -> List[Webhook]:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Gets the list of webhooks from this channel.
|
||||||
|
|
||||||
|
Requires :attr:`~.Permissions.manage_webhooks` permissions.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You don't have permissions to get the webhooks.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
List[:class:`Webhook`]
|
||||||
|
The webhooks for this channel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .webhook import Webhook
|
||||||
|
|
||||||
|
data = await self._state.http.channel_webhooks(self.id)
|
||||||
|
return [Webhook.from_state(d, state=self._state) for d in data]
|
||||||
|
|
||||||
|
async def create_webhook(self, *, name: str, avatar: Optional[bytes] = None, reason: Optional[str] = None) -> Webhook:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Creates a webhook for this channel.
|
||||||
|
|
||||||
|
Requires :attr:`~.Permissions.manage_webhooks` permissions.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-------------
|
||||||
|
name: :class:`str`
|
||||||
|
The webhook's name.
|
||||||
|
avatar: Optional[:class:`bytes`]
|
||||||
|
A :term:`py:bytes-like object` representing the webhook's default avatar.
|
||||||
|
This operates similarly to :meth:`~ClientUser.edit`.
|
||||||
|
reason: Optional[:class:`str`]
|
||||||
|
The reason for creating this webhook. Shows up in the audit logs.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Creating the webhook failed.
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to create a webhook.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Webhook`
|
||||||
|
The created webhook.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .webhook import Webhook
|
||||||
|
|
||||||
|
if avatar is not None:
|
||||||
|
avatar = utils._bytes_to_base64_data(avatar) # type: ignore # Silence reassignment error
|
||||||
|
|
||||||
|
data = await self._state.http.create_webhook(self.id, name=str(name), avatar=avatar, reason=reason)
|
||||||
|
return Webhook.from_state(data, state=self._state)
|
||||||
|
|
||||||
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
||||||
async def clone(self, *, name: Optional[str] = None, reason: Optional[str] = None) -> VoiceChannel:
|
async def clone(self, *, name: Optional[str] = None, reason: Optional[str] = None) -> VoiceChannel:
|
||||||
return await self._clone_impl({'bitrate': self.bitrate, 'user_limit': self.user_limit}, name=name, reason=reason)
|
return await self._clone_impl({'bitrate': self.bitrate, 'user_limit': self.user_limit}, name=name, reason=reason)
|
||||||
|
@ -641,7 +641,9 @@ class PartialMessage(Hashable):
|
|||||||
ChannelType.public_thread,
|
ChannelType.public_thread,
|
||||||
ChannelType.private_thread,
|
ChannelType.private_thread,
|
||||||
):
|
):
|
||||||
raise TypeError(f'Expected PartialMessageable, TextChannel, DMChannel or Thread not {type(channel)!r}')
|
raise TypeError(
|
||||||
|
f'expected PartialMessageable, TextChannel, VoiceChannel, DMChannel or Thread not {type(channel)!r}'
|
||||||
|
)
|
||||||
|
|
||||||
self.channel: MessageableChannel = channel
|
self.channel: MessageableChannel = channel
|
||||||
self._state: ConnectionState = channel._state
|
self._state: ConnectionState = channel._state
|
||||||
|
@ -592,8 +592,8 @@ class ConnectionState:
|
|||||||
self.dispatch('message', message)
|
self.dispatch('message', message)
|
||||||
if self._messages is not None:
|
if self._messages is not None:
|
||||||
self._messages.append(message)
|
self._messages.append(message)
|
||||||
# we ensure that the channel is either a TextChannel or Thread
|
# we ensure that the channel is either a TextChannel, VoiceChannel, or Thread
|
||||||
if channel and channel.__class__ in (TextChannel, Thread):
|
if channel and channel.__class__ in (TextChannel, VoiceChannel, Thread):
|
||||||
channel.last_message_id = message.id # type: ignore
|
channel.last_message_id = message.id # type: ignore
|
||||||
|
|
||||||
def parse_message_delete(self, data: gw.MessageDeleteEvent) -> None:
|
def parse_message_delete(self, data: gw.MessageDeleteEvent) -> None:
|
||||||
@ -1497,9 +1497,7 @@ class ConnectionState:
|
|||||||
if channel is not None:
|
if channel is not None:
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
def create_message(
|
def create_message(self, *, channel: MessageableChannel, data: MessagePayload) -> Message:
|
||||||
self, *, channel: Union[TextChannel, Thread, DMChannel, GroupChannel, PartialMessageable], data: MessagePayload
|
|
||||||
) -> Message:
|
|
||||||
return Message(state=self, channel=channel, data=data)
|
return Message(state=self, channel=channel, data=data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import time
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
from .abc import Messageable
|
from .abc import Messageable, _purge_helper
|
||||||
from .enums import ChannelType, try_enum
|
from .enums import ChannelType, try_enum
|
||||||
from .errors import ClientException
|
from .errors import ClientException
|
||||||
from .utils import MISSING, parse_time, _get_as_snowflake
|
from .utils import MISSING, parse_time, _get_as_snowflake
|
||||||
@ -489,55 +489,17 @@ class Thread(Messageable, Hashable):
|
|||||||
The list of messages that were deleted.
|
The list of messages that were deleted.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if check is MISSING:
|
return await _purge_helper(
|
||||||
check = lambda m: True
|
self,
|
||||||
|
limit=limit,
|
||||||
iterator = self.history(limit=limit, before=before, after=after, oldest_first=oldest_first, around=around)
|
check=check,
|
||||||
ret: List[Message] = []
|
before=before,
|
||||||
count = 0
|
after=after,
|
||||||
|
around=around,
|
||||||
minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22
|
oldest_first=oldest_first,
|
||||||
|
bulk=bulk,
|
||||||
async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None):
|
reason=reason,
|
||||||
for m in messages:
|
)
|
||||||
await m.delete()
|
|
||||||
|
|
||||||
strategy = self.delete_messages if bulk else _single_delete_strategy
|
|
||||||
|
|
||||||
async for message in iterator:
|
|
||||||
if count == 100:
|
|
||||||
to_delete = ret[-100:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
count = 0
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
if not check(message):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if message.id < minimum_time:
|
|
||||||
# older than 14 days old
|
|
||||||
if count == 1:
|
|
||||||
await ret[-1].delete()
|
|
||||||
elif count >= 2:
|
|
||||||
to_delete = ret[-count:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
strategy = _single_delete_strategy
|
|
||||||
|
|
||||||
count += 1
|
|
||||||
ret.append(message)
|
|
||||||
|
|
||||||
# SOme messages remaining to poll
|
|
||||||
if count >= 2:
|
|
||||||
# more than 2 messages -> bulk delete
|
|
||||||
to_delete = ret[-count:]
|
|
||||||
await strategy(to_delete, reason=reason)
|
|
||||||
elif count == 1:
|
|
||||||
# delete a single message
|
|
||||||
await ret[-1].delete()
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
async def edit(
|
async def edit(
|
||||||
self,
|
self,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user