Skip to content

Commit

Permalink
Merge branch 'master' into ft/voice_states
Browse files Browse the repository at this point in the history
  • Loading branch information
Snipy7374 authored Sep 14, 2024
2 parents 12c1d37 + a34d0f9 commit b4fdc36
Show file tree
Hide file tree
Showing 55 changed files with 1,505 additions and 217 deletions.
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/1142.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support fetching invites with ``null`` channel (e.g. friend invites).
1 change: 1 addition & 0 deletions changelog/1142.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`Invite.type`.
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: 1 addition & 0 deletions changelog/1180.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding some clarifying documentation around the type of :attr:`AuditLogEntry.extra` when the action is :attr:`~AuditLogAction.overwrite_create`.
1 change: 1 addition & 0 deletions changelog/1218.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new :attr:`Attachment.title` attribute.
1 change: 1 addition & 0 deletions changelog/1220.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`AppInfo.approximate_guild_count` and :attr:`AppInfo.approximate_user_install_count`.
1 change: 1 addition & 0 deletions changelog/1221.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new :meth:`.Client.fetch_sticker_pack` method.
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 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
30 changes: 26 additions & 4 deletions disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,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 +90,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 @@ -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 Down Expand Up @@ -1737,6 +1757,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 +1774,7 @@ async def send(
message_reference=reference_payload,
stickers=stickers_payload,
components=components_payload,
poll=poll_payload,
flags=flags_payload,
)

Expand Down
19 changes: 15 additions & 4 deletions disnake/appinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ class AppInfo:
.. versionadded:: 1.3
guild_id: Optional[:class:`int`]
If this application is a game sold on Discord,
this field will be the guild to which it has been linked to.
The ID of the guild associated with the application, if any.
.. versionadded:: 1.3
Expand Down Expand Up @@ -151,6 +150,15 @@ class AppInfo:
in the guild role verification configuration.
.. versionadded:: 2.8
approximate_guild_count: :class:`int`
The approximate number of guilds the application is installed to.
.. versionadded:: 2.10
approximate_user_install_count: :class:`int`
The approximate number of users that have installed the application
(for user-installable apps).
.. versionadded:: 2.10
"""

__slots__ = (
Expand All @@ -177,6 +185,8 @@ class AppInfo:
"install_params",
"custom_install_url",
"role_connections_verification_url",
"approximate_guild_count",
"approximate_user_install_count",
)

def __init__(self, state: ConnectionState, data: AppInfoPayload) -> None:
Expand Down Expand Up @@ -218,6 +228,8 @@ def __init__(self, state: ConnectionState, data: AppInfoPayload) -> None:
self.role_connections_verification_url: Optional[str] = data.get(
"role_connections_verification_url"
)
self.approximate_guild_count: int = data.get("approximate_guild_count", 0)
self.approximate_user_install_count: int = data.get("approximate_user_install_count", 0)

def __repr__(self) -> str:
return (
Expand Down Expand Up @@ -245,8 +257,7 @@ def cover_image(self) -> Optional[Asset]:

@property
def guild(self) -> Optional[Guild]:
"""Optional[:class:`Guild`]: If this application is a game sold on Discord,
this field will be the guild to which it has been linked
"""Optional[:class:`Guild`]: The guild associated with the application, if any.
.. versionadded:: 1.3
"""
Expand Down
9 changes: 7 additions & 2 deletions disnake/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
DefaultReaction as DefaultReactionPayload,
PermissionOverwrite as PermissionOverwritePayload,
)
from .types.invite import Invite as InvitePayload
from .types.role import Role as RolePayload
from .types.snowflake import Snowflake
from .types.threads import ForumTag as ForumTagPayload
Expand Down Expand Up @@ -799,15 +800,19 @@ def _convert_target_invite(self, target_id: int) -> Invite:
# so figure out which change has the full invite data
changeset = self.before if self.action is enums.AuditLogAction.invite_delete else self.after

fake_payload = {
fake_payload: InvitePayload = {
"max_age": changeset.max_age,
"max_uses": changeset.max_uses,
"code": changeset.code,
"temporary": changeset.temporary,
"uses": changeset.uses,
"type": 0,
"channel": None,
}

obj = Invite(state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel) # type: ignore
obj = Invite(
state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel
)
try:
obj.inviter = changeset.inviter
except AttributeError:
Expand Down
19 changes: 13 additions & 6 deletions disnake/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3494,7 +3494,7 @@ async def create_thread(
) -> ThreadWithMessage:
"""|coro|
Creates a thread in this channel.
Creates a thread (with an initial message) in this channel.
You must have the :attr:`~Permissions.create_forum_threads` permission to do this.
Expand All @@ -3507,6 +3507,10 @@ async def create_thread(
.. versionchanged:: 2.6
The ``content`` parameter is no longer required.
.. note::
Unlike :meth:`TextChannel.create_thread`,
this **returns a tuple** with both the created **thread and message**.
Parameters
----------
name: :class:`str`
Expand Down Expand Up @@ -3583,10 +3587,8 @@ async def create_thread(
Returns
-------
Tuple[:class:`Thread`, :class:`Message`]
:class:`ThreadWithMessage`
A :class:`~typing.NamedTuple` with the newly created thread and the message sent in it.
These values can also be accessed through the ``thread`` and ``message`` fields.
"""
from .message import Message
from .webhook.async_ import handle_message_parameters_dict
Expand Down Expand Up @@ -4661,7 +4663,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 @@ -4801,8 +4806,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
27 changes: 27 additions & 0 deletions disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2512,6 +2512,33 @@ async def fetch_sticker(self, sticker_id: int, /) -> Union[StandardSticker, Guil
cls, _ = _sticker_factory(data["type"]) # type: ignore
return cls(state=self._connection, data=data) # type: ignore

async def fetch_sticker_pack(self, pack_id: int, /) -> StickerPack:
"""|coro|
Retrieves a :class:`.StickerPack` with the given ID.
.. versionadded:: 2.10
Parameters
----------
pack_id: :class:`int`
The ID of the sticker pack to retrieve.
Raises
------
HTTPException
Retrieving the sticker pack failed.
NotFound
Invalid sticker pack ID.
Returns
-------
:class:`.StickerPack`
The sticker pack you requested.
"""
data = await self.http.get_sticker_pack(pack_id)
return StickerPack(state=self._connection, data=data)

async def fetch_sticker_packs(self) -> List[StickerPack]:
"""|coro|
Expand Down
Loading

0 comments on commit b4fdc36

Please sign in to comment.