Skip to content

Commit

Permalink
Merge branch 'master' into fix/user-app-caveats
Browse files Browse the repository at this point in the history
  • Loading branch information
shiftinv authored Dec 28, 2024
2 parents ceb47fc + df5e391 commit 914b1fc
Show file tree
Hide file tree
Showing 47 changed files with 1,406 additions and 242 deletions.
9 changes: 9 additions & 0 deletions changelog/1068.feature.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Implement soundboard features.
- Sound models: :class:`PartialSoundboardSound`, :class:`SoundboardSound`, :class:`GuildSoundboardSound`
- Managing sounds:
- Get soundboard sounds using :attr:`Guild.soundboard_sounds`, :attr:`Client.get_soundboard_sound`, or fetch them using :meth:`Guild.fetch_soundboard_sound`, :meth:`Guild.fetch_soundboard_sounds`, or :meth:`Client.fetch_default_soundboard_sounds`
- New sounds can be created with :meth:`Guild.create_soundboard_sound`
- Handle guild soundboard sound updates using the :attr:`~Event.guild_soundboard_sounds_update` event
- Send sounds using :meth:`VoiceChannel.send_soundboard_sound`
- New attributes: :attr:`Guild.soundboard_limit`, :attr:`VoiceChannelEffect.sound`, :attr:`Client.soundboard_sounds`
- New audit log actions: :attr:`AuditLogAction.soundboard_sound_create`, :attr:`~AuditLogAction.soundboard_sound_update`, :attr:`~AuditLogAction.soundboard_sound_delete`
1 change: 1 addition & 0 deletions changelog/1068.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rename :attr:`Intents.emojis_and_stickers` to :attr:`Intents.expressions`. An alias is provided for backwards compatibility.
1 change: 1 addition & 0 deletions changelog/1190.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The ``cls`` parameter of UI component decorators (such as :func:`ui.button`) now accepts any matching callable, in addition to item subclasses.
1 change: 1 addition & 0 deletions changelog/1256.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :meth:`GroupChannel.get_partial_message`.
1 change: 1 addition & 0 deletions changelog/914.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :class:`ui.Modal` timeout issues with long-running callbacks, and multiple modals with the same user and ``custom_id``.
1 change: 1 addition & 0 deletions disnake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from .role import *
from .shard import *
from .sku import *
from .soundboard import *
from .stage_instance import *
from .sticker import *
from .team import *
Expand Down
2 changes: 1 addition & 1 deletion disnake/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ async def to_file(
# if the filename doesn't have an extension (e.g. widget member avatars),
# try to infer it from the data
if not os.path.splitext(filename)[1]:
ext = utils._get_extension_for_image(data)
ext = utils._get_extension_for_data(data)
if ext:
filename += ext

Expand Down
20 changes: 20 additions & 0 deletions disnake/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,16 @@ class AuditLogChanges:
"available_tags": (None, _list_transformer(_transform_tag)),
"default_reaction_emoji": ("default_reaction", _transform_default_reaction),
"default_sort_order": (None, _enum_transformer(enums.ThreadSortOrder)),
"sound_id": ("id", _transform_snowflake),
}
# fmt: on

def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]) -> None:
self.before = AuditLogDiff()
self.after = AuditLogDiff()

has_emoji_fields = False

for elem in data:
attr = elem["key"]

Expand All @@ -390,6 +393,10 @@ def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]) -> N
)
continue

# special case for flat emoji fields (discord, why), these will be merged later
if attr == "emoji_id" or attr == "emoji_name":
has_emoji_fields = True

transformer: Optional[Transformer]

try:
Expand Down Expand Up @@ -420,6 +427,9 @@ def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]) -> N

setattr(self.after, attr, after)

if has_emoji_fields:
self._merge_emoji(entry)

# add an alias
if hasattr(self.after, "colour"):
self.after.color = self.after.colour
Expand Down Expand Up @@ -478,6 +488,16 @@ def _handle_command_permissions(
data=new, guild_id=guild_id
)

def _merge_emoji(self, entry: AuditLogEntry) -> None:
for diff in (self.before, self.after):
emoji_id: Optional[str] = diff.__dict__.pop("emoji_id", None)
emoji_name: Optional[str] = diff.__dict__.pop("emoji_name", None)

diff.emoji = entry._state._get_emoji_from_fields(
name=emoji_name,
id=int(emoji_id) if emoji_id else None,
)


class _AuditLogProxyMemberPrune:
delete_member_days: int
Expand Down
79 changes: 75 additions & 4 deletions disnake/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from .mixins import Hashable
from .partial_emoji import PartialEmoji
from .permissions import PermissionOverwrite, Permissions
from .soundboard import GuildSoundboardSound, PartialSoundboardSound, SoundboardSound
from .stage_instance import StageInstance
from .threads import ForumTag, Thread
from .utils import MISSING
Expand Down Expand Up @@ -91,6 +92,7 @@
VoiceChannel as VoiceChannelPayload,
)
from .types.snowflake import SnowflakeList
from .types.soundboard import PartialSoundboardSound as PartialSoundboardSoundPayload
from .types.threads import ThreadArchiveDurationLiteral
from .types.voice import VoiceChannelEffect as VoiceChannelEffectPayload
from .ui.action_row import Components, MessageUIComponent
Expand All @@ -110,17 +112,22 @@ class VoiceChannelEffect:
Attributes
----------
emoji: Optional[Union[:class:`Emoji`, :class:`PartialEmoji`]]
The emoji, for emoji reaction effects.
The emoji, for emoji reaction effects and soundboard effects.
animation_type: Optional[:class:`VoiceChannelEffectAnimationType`]
The emoji animation type, for emoji reaction effects.
The emoji animation type, for emoji reaction and soundboard effects.
animation_id: Optional[:class:`int`]
The emoji animation ID, for emoji reaction effects.
The emoji animation ID, for emoji reaction and soundboard effects.
sound: Optional[Union[:class:`GuildSoundboardSound`, :class:`PartialSoundboardSound`]]
The sound data, for soundboard effects.
This will be a :class:`PartialSoundboardSound` if it's a default sound
or from an external guild.
"""

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

def __init__(self, *, data: VoiceChannelEffectPayload, state: ConnectionState) -> None:
Expand All @@ -138,10 +145,21 @@ def __init__(self, *, data: VoiceChannelEffectPayload, state: ConnectionState) -
)
self.animation_id: Optional[int] = utils._get_as_snowflake(data, "animation_id")

self.sound: Optional[Union[GuildSoundboardSound, PartialSoundboardSound]] = None
if sound_id := utils._get_as_snowflake(data, "sound_id"):
if sound := state.get_soundboard_sound(sound_id):
self.sound = sound
else:
sound_data: PartialSoundboardSoundPayload = {
"sound_id": sound_id,
"volume": data.get("sound_volume"), # type: ignore # assume this exists if sound_id is set
}
self.sound = PartialSoundboardSound(data=sound_data, state=state)

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


Expand Down Expand Up @@ -1916,6 +1934,37 @@ async def create_webhook(
)
return Webhook.from_state(data, state=self._state)

async def send_soundboard_sound(self, sound: SoundboardSound, /) -> None:
"""|coro|
Sends a soundboard sound in this channel.
You must have :attr:`~Permissions.speak` and :attr:`~Permissions.use_soundboard`
permissions to do this. For sounds from different guilds, you must also have
:attr:`~Permissions.use_external_sounds` permission.
Additionally, you may not be muted or deafened.
Parameters
----------
sound: Union[:class:`SoundboardSound`, :class:`GuildSoundboardSound`]
The sound to send in the channel.
Raises
------
Forbidden
You are not allowed to send soundboard sounds.
HTTPException
An error occurred sending the soundboard sound.
"""
if isinstance(sound, GuildSoundboardSound):
source_guild_id = sound.guild_id
else:
source_guild_id = None

await self._state.http.send_soundboard_sound(
self.id, sound.id, source_guild_id=source_guild_id
)


class StageChannel(disnake.abc.Messageable, VocalGuildChannel):
"""Represents a Discord guild stage channel.
Expand Down Expand Up @@ -4968,6 +5017,28 @@ def permissions_for(

return base

def get_partial_message(self, message_id: int, /) -> PartialMessage:
"""Creates a :class:`PartialMessage` from the given 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.10
Parameters
----------
message_id: :class:`int`
The message ID to create a partial message for.
Returns
-------
:class:`PartialMessage`
The partial message object.
"""
from .message import PartialMessage

return PartialMessage(channel=self, id=message_id)

async def leave(self) -> None:
"""|coro|
Expand Down
47 changes: 46 additions & 1 deletion disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
from .mentions import AllowedMentions
from .object import Object
from .sku import SKU
from .soundboard import GuildSoundboardSound, SoundboardSound
from .stage_instance import StageInstance
from .state import ConnectionState
from .sticker import GuildSticker, StandardSticker, StickerPack, _sticker_factory
Expand Down Expand Up @@ -571,6 +572,14 @@ def stickers(self) -> List[GuildSticker]:
"""
return self._connection.stickers

@property
def soundboard_sounds(self) -> List[GuildSoundboardSound]:
"""List[:class:`.GuildSoundboardSound`]: The soundboard sounds that the connected client has.
.. versionadded:: 2.10
"""
return self._connection.soundboard_sounds

@property
def cached_messages(self) -> Sequence[Message]:
"""Sequence[:class:`.Message`]: Read-only list of messages the connected client has cached.
Expand Down Expand Up @@ -1501,7 +1510,7 @@ def get_sticker(self, id: int, /) -> Optional[GuildSticker]:
.. note::
To retrieve standard stickers, use :meth:`.fetch_sticker`.
To retrieve standard stickers, use :meth:`.fetch_sticker`
or :meth:`.fetch_sticker_packs`.
Returns
Expand All @@ -1511,6 +1520,22 @@ def get_sticker(self, id: int, /) -> Optional[GuildSticker]:
"""
return self._connection.get_sticker(id)

def get_soundboard_sound(self, id: int, /) -> Optional[GuildSoundboardSound]:
"""Returns a guild soundboard sound with the given ID.
.. versionadded:: 2.10
.. note::
To retrieve standard soundboard sounds, use :meth:`.fetch_default_soundboard_sounds`.
Returns
-------
Optional[:class:`.GuildSoundboardSound`]
The soundboard sound or ``None`` if not found.
"""
return self._connection.get_soundboard_sound(id)

def get_all_channels(self) -> Generator[GuildChannel, None, None]:
"""A generator that retrieves every :class:`.abc.GuildChannel` the client can 'access'.
Expand Down Expand Up @@ -2357,6 +2382,26 @@ async def fetch_widget(self, guild_id: int, /) -> Widget:
data = await self.http.get_widget(guild_id)
return Widget(state=self._connection, data=data)

async def fetch_default_soundboard_sounds(self) -> List[SoundboardSound]:
"""|coro|
Retrieves the list of default :class:`.SoundboardSound`\\s provided by Discord.
.. versionadded:: 2.10
Raises
------
HTTPException
Retrieving the soundboard sounds failed.
Returns
-------
List[:class:`.SoundboardSound`]
The default soundboard sounds.
"""
data = await self.http.get_default_soundboard_sounds()
return [SoundboardSound(data=d, state=self._connection) for d in data]

async def application_info(self) -> AppInfo:
"""|coro|
Expand Down
30 changes: 26 additions & 4 deletions disnake/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ class AuditLogAction(Enum):
thread_update = 111
thread_delete = 112
application_command_permission_update = 121
soundboard_sound_create = 130
soundboard_sound_update = 131
soundboard_sound_delete = 132
automod_rule_create = 140
automod_rule_update = 141
automod_rule_delete = 142
Expand Down Expand Up @@ -465,6 +468,9 @@ def category(self) -> Optional[AuditLogActionCategory]:
AuditLogAction.guild_scheduled_event_update: AuditLogActionCategory.update,
AuditLogAction.guild_scheduled_event_delete: AuditLogActionCategory.delete,
AuditLogAction.application_command_permission_update: AuditLogActionCategory.update,
AuditLogAction.soundboard_sound_create: AuditLogActionCategory.create,
AuditLogAction.soundboard_sound_update: AuditLogActionCategory.update,
AuditLogAction.soundboard_sound_delete: AuditLogActionCategory.delete,
AuditLogAction.automod_rule_create: AuditLogActionCategory.create,
AuditLogAction.automod_rule_update: AuditLogActionCategory.update,
AuditLogAction.automod_rule_delete: AuditLogActionCategory.delete,
Expand Down Expand Up @@ -1065,6 +1071,12 @@ class Event(Enum):
"""Called when a `Guild` updates its stickers.
Represents the :func:`on_guild_stickers_update` event.
"""
guild_soundboard_sounds_update = "guild_soundboard_sounds_update"
"""Called when a `Guild` updates its soundboard sounds.
Represents the :func:`on_guild_soundboard_sounds_update` event.
.. versionadded:: 2.10
"""
guild_integrations_update = "guild_integrations_update"
"""Called whenever an integration is created, modified, or removed from a guild.
Represents the :func:`on_guild_integrations_update` event.
Expand Down Expand Up @@ -1288,7 +1300,8 @@ class Event(Enum):
"""
raw_presence_update = "raw_presence_update"
"""Called when a user's presence changes regardless of the state of the internal member cache.
Represents the :func:`on_raw_presence_update` event."""
Represents the :func:`on_raw_presence_update` event.
"""
raw_reaction_add = "raw_reaction_add"
"""Called when a message has a reaction added regardless of the state of the internal message cache.
Represents the :func:`on_raw_reaction_add` event.
Expand All @@ -1315,13 +1328,22 @@ class Event(Enum):
"""
entitlement_create = "entitlement_create"
"""Called when a user subscribes to an SKU, creating a new :class:`Entitlement`.
Represents the :func:`on_entitlement_create` event."""
Represents the :func:`on_entitlement_create` event.
.. versionadded:: 2.10
"""
entitlement_update = "entitlement_update"
"""Called when a user's subscription renews.
Represents the :func:`on_entitlement_update` event."""
Represents the :func:`on_entitlement_update` event.
.. versionadded:: 2.10
"""
entitlement_delete = "entitlement_delete"
"""Called when a user's entitlement is deleted.
Represents the :func:`on_entitlement_delete` event."""
Represents the :func:`on_entitlement_delete` event.
.. versionadded:: 2.10
"""
# ext.commands events
command = "command"
"""Called when a command is found and is about to be invoked.
Expand Down
Loading

0 comments on commit 914b1fc

Please sign in to comment.