Skip to content

Commit

Permalink
Merge branch 'master' into feat/app_emojis
Browse files Browse the repository at this point in the history
  • Loading branch information
Snipy7374 authored Nov 26, 2024
2 parents 74bdc75 + 2d0f91a commit 0e7ad20
Show file tree
Hide file tree
Showing 77 changed files with 2,374 additions and 324 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ repos:
name: "run black in all files"

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.3.4
rev: v0.8.0
hooks:
- id: ruff
args: [--fix, --fixable=I]
1 change: 1 addition & 0 deletions changelog/1012.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:class:`Interaction`\s now always have a proper :attr:`~Interaction.channel` attribute, even when the bot is not part of the guild or cannot access the channel due to other reasons.
1 change: 1 addition & 0 deletions changelog/1115.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :class:`SelectDefaultValue`, and add :attr:`~UserSelectMenu.default_values` to all auto-populated select menu types.
1 change: 1 addition & 0 deletions changelog/1160.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:attr:`InteractionReference.user` can now be a :class:`Member` in guild contexts.
6 changes: 6 additions & 0 deletions changelog/1175.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Add the new poll discord API feature. This includes the following new classes and events:

- New types: :class:`Poll`, :class:`PollAnswer`, :class:`PollMedia`, :class:`RawPollVoteActionEvent` and :class:`PollLayoutType`.
- Edited :meth:`abc.Messageable.send`, :meth:`Webhook.send`, :meth:`ext.commands.Context.send` and :meth:`disnake.InteractionResponse.send_message` to be able to send polls.
- Edited :class:`Message` to store a new :attr:`Message.poll` attribute for polls.
- Edited :class:`Event` to contain the new :func:`on_poll_vote_add`, :func:`on_poll_vote_remove`, :func:`on_raw_poll_vote_add` and :func:`on_raw_poll_vote_remove`.
1 change: 0 additions & 1 deletion changelog/1180.doc.rst

This file was deleted.

1 change: 1 addition & 0 deletions changelog/1184.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the possibility to pass :class:`disnake.File` objects to :meth:`Embed.set_author` and :meth:`~Embed.set_footer`.
1 change: 1 addition & 0 deletions changelog/1203.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement new :attr:`.Member.guild_banner` property.
1 change: 1 addition & 0 deletions changelog/1212.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new :attr:`~MessageType.poll_result` message type.
2 changes: 2 additions & 0 deletions changelog/1233.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
For interactions in private channels, :attr:`Interaction.channel` is now always a proper :class:`DMChannel` or :class:`GroupChannel` object, instead of :class:`PartialMessageable`.
- This also applies to other channel objects in interactions, e.g. channel parameters in slash commands, or :attr:`ui.ChannelSelect.values`.
1 change: 1 addition & 0 deletions changelog/1247.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement the new :meth:`.Guild.fetch_role` API method.
4 changes: 4 additions & 0 deletions changelog/993.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Support voice channel effect events.
- New events: :func:`on_voice_channel_effect`, :func:`on_raw_voice_channel_effect`.
- New types: :class:`VoiceChannelEffect`, :class:`RawVoiceChannelEffectEvent`.
- New enum: :class:`VoiceChannelEffectAnimationType`.
1 change: 1 addition & 0 deletions disnake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
from .partial_emoji import *
from .permissions import *
from .player import *
from .poll import *
from .raw_models import *
from .reaction import *
from .role import *
Expand Down
38 changes: 29 additions & 9 deletions disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
from .permissions import PermissionOverwrite, Permissions
from .role import Role
from .sticker import GuildSticker, StandardSticker, StickerItem
from .ui.action_row import components_to_dict
from .utils import _overload_with_permissions
from .voice_client import VoiceClient, VoiceProtocol

Expand All @@ -64,16 +63,17 @@
from typing_extensions import Self

from .asset import Asset
from .channel import CategoryChannel, DMChannel, PartialMessageable
from .channel import CategoryChannel, DMChannel, GroupChannel, PartialMessageable
from .client import Client
from .embeds import Embed
from .emoji import Emoji
from .enums import InviteTarget
from .guild import Guild, GuildMessageable
from .guild import Guild, GuildChannel as AnyGuildChannel, GuildMessageable
from .guild_scheduled_event import GuildScheduledEvent
from .iterators import HistoryIterator
from .member import Member
from .message import Message, MessageReference, PartialMessage
from .poll import Poll
from .state import ConnectionState
from .threads import AnyThreadArchiveDuration, ForumTag
from .types.channel import (
Expand All @@ -89,7 +89,10 @@
from .user import ClientUser
from .voice_region import VoiceRegion

MessageableChannel = Union[GuildMessageable, DMChannel, PartialMessageable]
MessageableChannel = Union[GuildMessageable, DMChannel, GroupChannel, PartialMessageable]
# include non-messageable channels, e.g. category/forum
AnyChannel = Union[MessageableChannel, AnyGuildChannel]

SnowflakeTime = Union["Snowflake", datetime]

MISSING = utils.MISSING
Expand Down Expand Up @@ -175,6 +178,7 @@ def avatar(self) -> Optional[Asset]:
raise NotImplementedError


# FIXME: this shouldn't be a protocol. isinstance(thread, PrivateChannel) returns true, and issubclass doesn't work.
@runtime_checkable
class PrivateChannel(Snowflake, Protocol):
"""An ABC that details the common operations on a private Discord channel.
Expand Down Expand Up @@ -640,6 +644,7 @@ def _apply_implict_permissions(self, base: Permissions) -> None:
if not base.send_messages:
base.send_tts_messages = False
base.send_voice_messages = False
base.send_polls = False
base.mention_everyone = False
base.embed_links = False
base.attach_files = False
Expand Down Expand Up @@ -887,6 +892,7 @@ async def set_permissions(
request_to_speak: Optional[bool] = ...,
send_messages: Optional[bool] = ...,
send_messages_in_threads: Optional[bool] = ...,
send_polls: Optional[bool] = ...,
send_tts_messages: Optional[bool] = ...,
send_voice_messages: Optional[bool] = ...,
speak: Optional[bool] = ...,
Expand Down Expand Up @@ -1435,6 +1441,7 @@ async def send(
mention_author: bool = ...,
view: View = ...,
components: Components[MessageUIComponent] = ...,
poll: Poll = ...,
) -> Message:
...

Expand All @@ -1456,6 +1463,7 @@ async def send(
mention_author: bool = ...,
view: View = ...,
components: Components[MessageUIComponent] = ...,
poll: Poll = ...,
) -> Message:
...

Expand All @@ -1477,6 +1485,7 @@ async def send(
mention_author: bool = ...,
view: View = ...,
components: Components[MessageUIComponent] = ...,
poll: Poll = ...,
) -> Message:
...

Expand All @@ -1498,6 +1507,7 @@ async def send(
mention_author: bool = ...,
view: View = ...,
components: Components[MessageUIComponent] = ...,
poll: Poll = ...,
) -> Message:
...

Expand All @@ -1520,6 +1530,7 @@ async def send(
mention_author: Optional[bool] = None,
view: Optional[View] = None,
components: Optional[Components[MessageUIComponent]] = None,
poll: Optional[Poll] = None,
):
"""|coro|
Expand All @@ -1528,7 +1539,7 @@ async def send(
The content must be a type that can convert to a string through ``str(content)``.
At least one of ``content``, ``embed``/``embeds``, ``file``/``files``,
``stickers``, ``components``, or ``view`` must be provided.
``stickers``, ``components``, ``poll`` or ``view`` must be provided.
To upload a single file, the ``file`` parameter should be used with a
single :class:`.File` object. To upload multiple files, the ``files``
Expand Down Expand Up @@ -1624,6 +1635,11 @@ async def send(
.. versionadded:: 2.9
poll: :class:`.Poll`
The poll to send with the message.
.. versionadded:: 2.10
Raises
------
HTTPException
Expand Down Expand Up @@ -1676,6 +1692,10 @@ async def send(
if stickers is not None:
stickers_payload = [sticker.id for sticker in stickers]

poll_payload = None
if poll:
poll_payload = poll._to_dict()

allowed_mentions_payload = None
if allowed_mentions is None:
allowed_mentions_payload = state.allowed_mentions and state.allowed_mentions.to_dict()
Expand All @@ -1699,16 +1719,14 @@ async def send(

if view is not None and components is not None:
raise TypeError("cannot pass both view and components parameter to send()")

elif view:
if not hasattr(view, "__discord_ui_view__"):
raise TypeError(f"view parameter must be View not {view.__class__!r}")

components_payload = view.to_components()

elif components:
components_payload = components_to_dict(components)
from .ui.action_row import components_to_dict

components_payload = components_to_dict(components)
else:
components_payload = None

Expand Down Expand Up @@ -1737,6 +1755,7 @@ async def send(
message_reference=reference_payload,
stickers=stickers_payload,
components=components_payload,
poll=poll_payload,
flags=flags_payload,
)
finally:
Expand All @@ -1753,6 +1772,7 @@ async def send(
message_reference=reference_payload,
stickers=stickers_payload,
components=components_payload,
poll=poll_payload,
flags=flags_payload,
)

Expand Down
2 changes: 1 addition & 1 deletion disnake/app_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def localize(self, store: LocalizationProtocol) -> None:
o.localize(store)


class ApplicationCommand(ABC):
class ApplicationCommand(ABC): # noqa: B024 # this will get refactored eventually
"""The base class for application commands.
The following classes implement this ABC:
Expand Down
13 changes: 13 additions & 0 deletions disnake/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,19 @@ def _from_guild_avatar(
animated=animated,
)

@classmethod
def _from_guild_banner(
cls, state: AnyState, guild_id: int, member_id: int, banner: str
) -> Self:
animated = banner.startswith("a_")
format = "gif" if animated else "png"
return cls(
state,
url=f"{cls.BASE}/guilds/{guild_id}/users/{member_id}/banners/{banner}.{format}?size=1024",
key=banner,
animated=animated,
)

@classmethod
def _from_icon(cls, state: AnyState, object_id: int, icon_hash: str, path: str) -> Self:
return cls(
Expand Down
58 changes: 56 additions & 2 deletions disnake/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ThreadLayout,
ThreadSortOrder,
VideoQualityMode,
VoiceChannelEffectAnimationType,
try_enum,
try_enum_to_int,
)
Expand All @@ -50,6 +51,7 @@
from .utils import MISSING

__all__ = (
"VoiceChannelEffect",
"TextChannel",
"VoiceChannel",
"StageChannel",
Expand Down Expand Up @@ -90,13 +92,59 @@
)
from .types.snowflake import SnowflakeList
from .types.threads import ThreadArchiveDurationLiteral
from .types.voice import VoiceChannelEffect as VoiceChannelEffectPayload
from .ui.action_row import Components, MessageUIComponent
from .ui.view import View
from .user import BaseUser, ClientUser, User
from .voice_region import VoiceRegion
from .webhook import Webhook


class VoiceChannelEffect:
"""An effect sent by a member in a voice channel.
Different sets of attributes will be present, depending on the type of effect.
.. versionadded:: 2.10
Attributes
----------
emoji: Optional[Union[:class:`Emoji`, :class:`PartialEmoji`]]
The emoji, for emoji reaction effects.
animation_type: Optional[:class:`VoiceChannelEffectAnimationType`]
The emoji animation type, for emoji reaction effects.
animation_id: Optional[:class:`int`]
The emoji animation ID, for emoji reaction effects.
"""

__slots__ = (
"emoji",
"animation_type",
"animation_id",
)

def __init__(self, *, data: VoiceChannelEffectPayload, state: ConnectionState) -> None:
self.emoji: Optional[Union[Emoji, PartialEmoji]] = None
if emoji_data := data.get("emoji"):
emoji = state._get_emoji_from_data(emoji_data)
if isinstance(emoji, str):
emoji = PartialEmoji(name=emoji)
self.emoji = emoji

self.animation_type = (
try_enum(VoiceChannelEffectAnimationType, value)
if (value := data.get("animation_type")) is not None
else None
)
self.animation_id: Optional[int] = utils._get_as_snowflake(data, "animation_id")

def __repr__(self) -> str:
return (
f"<VoiceChannelEffect emoji={self.emoji!r} animation_type={self.animation_type!r}"
f" animation_id={self.animation_id!r}>"
)


async def _single_delete_strategy(messages: Iterable[Message]) -> None:
for m in messages:
await m.delete()
Expand Down Expand Up @@ -4663,7 +4711,10 @@ class DMChannel(disnake.abc.Messageable, Hashable):

def __init__(self, *, me: ClientUser, state: ConnectionState, data: DMChannelPayload) -> None:
self._state: ConnectionState = state
self.recipient: Optional[User] = state.store_user(data["recipients"][0]) # type: ignore
self.recipient: Optional[User] = None
if recipients := data.get("recipients"):
self.recipient = state.store_user(recipients[0]) # type: ignore

self.me: ClientUser = me
self.id: int = int(data["id"])
self.last_pin_timestamp: Optional[datetime.datetime] = utils.parse_time(
Expand Down Expand Up @@ -4803,8 +4854,10 @@ class GroupChannel(disnake.abc.Messageable, Hashable):
----------
recipients: List[:class:`User`]
The users you are participating with in the group channel.
If this channel is received through the gateway, the recipient information
may not be always available.
me: :class:`ClientUser`
The user presenting yourself.
The user representing yourself.
id: :class:`int`
The group channel ID.
owner: Optional[:class:`User`]
Expand Down Expand Up @@ -5039,6 +5092,7 @@ def _channel_type_factory(
cls: Union[Type[disnake.abc.GuildChannel], Type[Thread]]
) -> List[ChannelType]:
return {
# FIXME: this includes private channels; improve this once there's a common base type for all channels
disnake.abc.GuildChannel: list(ChannelType.__members__.values()),
VocalGuildChannel: [ChannelType.voice, ChannelType.stage_voice],
disnake.abc.PrivateChannel: [ChannelType.private, ChannelType.group],
Expand Down
7 changes: 6 additions & 1 deletion disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,12 @@ def run(self, *args: Any, **kwargs: Any) -> None:
This function must be the last function to call due to the fact that it
is blocking. That means that registration of events or anything being
called after this function call will not execute until it returns.
called after this function call will not execute until it returns
Parameters
----------
token: :class:`str`
The discord token of the bot that is being ran.
"""
loop = self.loop

Expand Down
Loading

0 comments on commit 0e7ad20

Please sign in to comment.