mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-19 15:36:02 +00:00
implement guild stickers
This commit is contained in:
parent
ecf239d2a2
commit
60d82cf908
@ -31,12 +31,11 @@ from typing import (
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
@ -53,6 +52,7 @@ from .role import Role
|
||||
from .invite import Invite
|
||||
from .file import File
|
||||
from .voice_client import VoiceClient, VoiceProtocol
|
||||
from .sticker import GuildSticker, StickerItem
|
||||
from . import utils
|
||||
|
||||
__all__ = (
|
||||
@ -1164,6 +1164,7 @@ class Messageable:
|
||||
tts: bool = ...,
|
||||
embed: Embed = ...,
|
||||
file: File = ...,
|
||||
stickers: Sequence[Union[GuildSticker, StickerItem]] = ...,
|
||||
delete_after: float = ...,
|
||||
nonce: Union[str, int] = ...,
|
||||
allowed_mentions: AllowedMentions = ...,
|
||||
@ -1181,6 +1182,7 @@ class Messageable:
|
||||
tts: bool = ...,
|
||||
embed: Embed = ...,
|
||||
files: List[File] = ...,
|
||||
stickers: Sequence[Union[GuildSticker, StickerItem]] = ...,
|
||||
delete_after: float = ...,
|
||||
nonce: Union[str, int] = ...,
|
||||
allowed_mentions: AllowedMentions = ...,
|
||||
@ -1198,6 +1200,7 @@ class Messageable:
|
||||
tts: bool = ...,
|
||||
embeds: List[Embed] = ...,
|
||||
file: File = ...,
|
||||
stickers: Sequence[Union[GuildSticker, StickerItem]] = ...,
|
||||
delete_after: float = ...,
|
||||
nonce: Union[str, int] = ...,
|
||||
allowed_mentions: AllowedMentions = ...,
|
||||
@ -1215,6 +1218,7 @@ class Messageable:
|
||||
tts: bool = ...,
|
||||
embeds: List[Embed] = ...,
|
||||
files: List[File] = ...,
|
||||
stickers: Sequence[Union[GuildSticker, StickerItem]] = ...,
|
||||
delete_after: float = ...,
|
||||
nonce: Union[str, int] = ...,
|
||||
allowed_mentions: AllowedMentions = ...,
|
||||
@ -1233,6 +1237,7 @@ class Messageable:
|
||||
embeds=None,
|
||||
file=None,
|
||||
files=None,
|
||||
stickers=None,
|
||||
delete_after=None,
|
||||
nonce=None,
|
||||
allowed_mentions=None,
|
||||
@ -1304,6 +1309,10 @@ class Messageable:
|
||||
embeds: List[:class:`~discord.Embed`]
|
||||
A list of embeds to upload. Must be a maximum of 10.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
stickers: Sequence[Union[:class:`GuildSticker`, :class:`StickerItem`]]
|
||||
A list of stickers to upload. Must be a maximum of 3.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Raises
|
||||
@ -1340,6 +1349,9 @@ class Messageable:
|
||||
raise InvalidArgument('embeds parameter must be a list of up to 10 elements')
|
||||
embeds = [embed.to_dict() for embed in embeds]
|
||||
|
||||
if stickers is not None:
|
||||
stickers = [sticker.id for sticker in stickers]
|
||||
|
||||
if allowed_mentions is not None:
|
||||
if state.allowed_mentions is not None:
|
||||
allowed_mentions = state.allowed_mentions.merge(allowed_mentions).to_dict()
|
||||
@ -1384,6 +1396,7 @@ class Messageable:
|
||||
embeds=embeds,
|
||||
nonce=nonce,
|
||||
message_reference=reference,
|
||||
stickers=stickers,
|
||||
components=components,
|
||||
)
|
||||
finally:
|
||||
@ -1406,6 +1419,7 @@ class Messageable:
|
||||
nonce=nonce,
|
||||
allowed_mentions=allowed_mentions,
|
||||
message_reference=reference,
|
||||
stickers=stickers,
|
||||
components=components,
|
||||
)
|
||||
finally:
|
||||
@ -1421,6 +1435,7 @@ class Messageable:
|
||||
nonce=nonce,
|
||||
allowed_mentions=allowed_mentions,
|
||||
message_reference=reference,
|
||||
stickers=stickers,
|
||||
components=components,
|
||||
)
|
||||
|
||||
@ -1454,7 +1469,7 @@ class Messageable:
|
||||
This means that both ``with`` and ``async with`` work with this.
|
||||
|
||||
Example Usage: ::
|
||||
|
||||
|
||||
async with channel.typing():
|
||||
# simulate something heavy
|
||||
await asyncio.sleep(10)
|
||||
|
@ -216,11 +216,11 @@ class Asset(AssetMixin):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _from_sticker(cls, state, sticker_id: int, sticker_hash: str) -> Asset:
|
||||
def _from_sticker_banner(cls, state, banner: int) -> Asset:
|
||||
return cls(
|
||||
state,
|
||||
url=f'{cls.BASE}/stickers/{sticker_id}/{sticker_hash}.png?size=1024',
|
||||
key=sticker_hash,
|
||||
url=f'{cls.BASE}/app-assets/710982414301790216/store/{banner}.png',
|
||||
key=str(banner),
|
||||
animated=False,
|
||||
)
|
||||
|
||||
|
@ -58,6 +58,7 @@ if TYPE_CHECKING:
|
||||
from .types.snowflake import Snowflake
|
||||
from .user import User
|
||||
from .stage_instance import StageInstance
|
||||
from .sticker import GuildSticker
|
||||
from .threads import Thread
|
||||
|
||||
|
||||
@ -79,16 +80,15 @@ def _transform_channel(entry: AuditLogEntry, data: Optional[Snowflake]) -> Optio
|
||||
return entry.guild.get_channel(int(data)) or Object(id=data)
|
||||
|
||||
|
||||
def _transform_owner_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Union[Member, User, None]:
|
||||
def _transform_member_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Union[Member, User, None]:
|
||||
if data is None:
|
||||
return None
|
||||
return entry._get_member(int(data))
|
||||
|
||||
|
||||
def _transform_inviter_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Union[Member, User, None]:
|
||||
def _transform_guild_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Optional[Guild]:
|
||||
if data is None:
|
||||
return None
|
||||
return entry._get_member(int(data))
|
||||
return entry._state._get_guild(data)
|
||||
|
||||
|
||||
def _transform_overwrites(
|
||||
@ -146,6 +146,11 @@ def _enum_transformer(enum: Type[T]) -> Callable[[AuditLogEntry, int], T]:
|
||||
|
||||
return _transform
|
||||
|
||||
def _transform_type(entry: AuditLogEntry, data: Union[int]) -> Union[enums.ChannelType, enums.StickerType]:
|
||||
if entry.action.name.startswith('sticker_'):
|
||||
return enums.try_enum(enums.StickerType, data)
|
||||
else:
|
||||
return enums.try_enum(enums.ChannelType, data)
|
||||
|
||||
class AuditLogDiff:
|
||||
def __len__(self) -> int:
|
||||
@ -180,8 +185,8 @@ class AuditLogChanges:
|
||||
'permissions': (None, _transform_permissions),
|
||||
'id': (None, _transform_snowflake),
|
||||
'color': ('colour', _transform_color),
|
||||
'owner_id': ('owner', _transform_owner_id),
|
||||
'inviter_id': ('inviter', _transform_inviter_id),
|
||||
'owner_id': ('owner', _transform_member_id),
|
||||
'inviter_id': ('inviter', _transform_member_id),
|
||||
'channel_id': ('channel', _transform_channel),
|
||||
'afk_channel_id': ('afk_channel', _transform_channel),
|
||||
'system_channel_id': ('system_channel', _transform_channel),
|
||||
@ -195,12 +200,15 @@ class AuditLogChanges:
|
||||
'icon_hash': ('icon', _transform_icon),
|
||||
'avatar_hash': ('avatar', _transform_avatar),
|
||||
'rate_limit_per_user': ('slowmode_delay', None),
|
||||
'guild_id': ('guild', _transform_guild_id),
|
||||
'tags': ('emoji', None),
|
||||
'default_message_notifications': ('default_notifications', _enum_transformer(enums.NotificationLevel)),
|
||||
'region': (None, _enum_transformer(enums.VoiceRegion)),
|
||||
'rtc_region': (None, _enum_transformer(enums.VoiceRegion)),
|
||||
'video_quality_mode': (None, _enum_transformer(enums.VideoQualityMode)),
|
||||
'privacy_level': (None, _enum_transformer(enums.StagePrivacyLevel)),
|
||||
'type': (None, _enum_transformer(enums.ChannelType)),
|
||||
'format_type': (None, _enum_transformer(enums.StickerFormatType)),
|
||||
'type': (None, _transform_type),
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
@ -438,7 +446,7 @@ class AuditLogEntry(Hashable):
|
||||
return utils.snowflake_time(self.id)
|
||||
|
||||
@utils.cached_property
|
||||
def target(self) -> Union[Guild, abc.GuildChannel, Member, User, Role, Invite, Emoji, Object, Thread, None]:
|
||||
def target(self) -> Union[Guild, abc.GuildChannel, Member, User, Role, Invite, Emoji, StageInstance, GuildSticker, Thread, Object, None]:
|
||||
try:
|
||||
converter = getattr(self, '_convert_target_' + self.action.target_type)
|
||||
except AttributeError:
|
||||
@ -509,5 +517,8 @@ class AuditLogEntry(Hashable):
|
||||
def _convert_target_stage_instance(self, target_id: int) -> Union[StageInstance, Object]:
|
||||
return self.guild.get_stage_instance(target_id) or Object(id=target_id)
|
||||
|
||||
def _convert_target_sticker(self, target_id: int) -> Union[GuildSticker, Object]:
|
||||
return self._state.get_sticker(target_id) or Object(id=target_id)
|
||||
|
||||
def _convert_target_thread(self, target_id: int) -> Union[Thread, Object]:
|
||||
return self.guild.get_thread(target_id) or Object(id=target_id)
|
||||
|
@ -60,6 +60,7 @@ from .appinfo import AppInfo
|
||||
from .ui.view import View
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread
|
||||
from .sticker import GuildSticker, StandardSticker, StickerPack, _sticker_factory
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .abc import SnowflakeTime, PrivateChannel, GuildChannel, Snowflake
|
||||
@ -277,6 +278,14 @@ class Client:
|
||||
"""List[:class:`.Emoji`]: The emojis that the connected client has."""
|
||||
return self._connection.emojis
|
||||
|
||||
@property
|
||||
def stickers(self) -> List[GuildSticker]:
|
||||
"""List[:class:`GuildSticker`]: The stickers that the connected client has.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._connection.stickers
|
||||
|
||||
@property
|
||||
def cached_messages(self) -> Sequence[Message]:
|
||||
"""Sequence[:class:`.Message`]: Read-only list of messages the connected client has cached.
|
||||
@ -777,6 +786,23 @@ class Client:
|
||||
"""
|
||||
return self._connection.get_emoji(id)
|
||||
|
||||
def get_sticker(self, id: int) -> Optional[GuildSticker]:
|
||||
"""Returns a guild sticker with the given ID.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. note::
|
||||
|
||||
To retrieve standard stickers, use :meth:`.fetch_sticker`.
|
||||
or :meth:`.fetch_nitro_sticker_packs`.
|
||||
|
||||
Returns
|
||||
--------
|
||||
Optional[:class:`.GuildSticker`]
|
||||
The sticker or ``None`` if not found.
|
||||
"""
|
||||
return self._connection.get_sticker(id)
|
||||
|
||||
def get_all_channels(self) -> Generator[GuildChannel, None, None]:
|
||||
"""A generator that retrieves every :class:`.abc.GuildChannel` the client can 'access'.
|
||||
|
||||
@ -1443,6 +1469,49 @@ class Client:
|
||||
data = await self.http.get_webhook(webhook_id)
|
||||
return Webhook.from_state(data, state=self._connection)
|
||||
|
||||
async def fetch_sticker(self, sticker_id: int) -> Union[StandardSticker, GuildSticker]:
|
||||
"""|coro|
|
||||
|
||||
Retrieves a :class:`.Sticker` with the specified ID.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Raises
|
||||
--------
|
||||
:exc:`.HTTPException`
|
||||
Retrieving the sticker failed.
|
||||
:exc:`.NotFound`
|
||||
Invalid sticker ID.
|
||||
|
||||
Returns
|
||||
--------
|
||||
Union[:class:`.StandardSticker`, :class:`.GuildSticker`]
|
||||
The sticker you requested.
|
||||
"""
|
||||
data = await self.http.get_sticker(sticker_id)
|
||||
cls, _ = _sticker_factory(data['type']) # type: ignore
|
||||
return cls(state=self._connection, data=data) # type: ignore
|
||||
|
||||
async def fetch_nitro_sticker_packs(self) -> List[StickerPack]:
|
||||
"""|coro|
|
||||
|
||||
Retrieves all available nitro sticker packs.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Raises
|
||||
-------
|
||||
:exc:`.HTTPException`
|
||||
Retrieving the sticker packs failed.
|
||||
|
||||
Returns
|
||||
---------
|
||||
List[:class:`.StickerPack`]
|
||||
All available nitro sticker packs.
|
||||
"""
|
||||
data = await self.http.list_nitro_sticker_packs()
|
||||
return [StickerPack(state=self._connection, data=pack) for pack in data['sticker_packs']]
|
||||
|
||||
async def create_dm(self, user: Snowflake) -> DMChannel:
|
||||
"""|coro|
|
||||
|
||||
|
@ -46,6 +46,7 @@ __all__ = (
|
||||
'ExpireBehaviour',
|
||||
'ExpireBehavior',
|
||||
'StickerType',
|
||||
'StickerFormatType',
|
||||
'InviteTarget',
|
||||
'VideoQualityMode',
|
||||
'ComponentType',
|
||||
@ -346,6 +347,9 @@ class AuditLogAction(Enum):
|
||||
stage_instance_create = 83
|
||||
stage_instance_update = 84
|
||||
stage_instance_delete = 85
|
||||
sticker_create = 90
|
||||
sticker_update = 91
|
||||
sticker_delete = 92
|
||||
thread_create = 110
|
||||
thread_update = 111
|
||||
thread_delete = 112
|
||||
@ -393,6 +397,9 @@ class AuditLogAction(Enum):
|
||||
AuditLogAction.stage_instance_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.stage_instance_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.stage_instance_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.sticker_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.sticker_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.sticker_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.thread_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.thread_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.thread_delete: AuditLogActionCategory.delete,
|
||||
@ -427,6 +434,8 @@ class AuditLogAction(Enum):
|
||||
return 'integration'
|
||||
elif v < 90:
|
||||
return 'stage_instance'
|
||||
elif v < 93:
|
||||
return 'sticker'
|
||||
elif v < 113:
|
||||
return 'thread'
|
||||
|
||||
@ -484,10 +493,26 @@ ExpireBehavior = ExpireBehaviour
|
||||
|
||||
|
||||
class StickerType(Enum):
|
||||
standard = 1
|
||||
guild = 2
|
||||
|
||||
|
||||
class StickerFormatType(Enum):
|
||||
png = 1
|
||||
apng = 2
|
||||
lottie = 3
|
||||
|
||||
@property
|
||||
def file_extension(self) -> str:
|
||||
# fmt: off
|
||||
lookup: Dict[StickerFormatType, str] = {
|
||||
StickerFormatType.png: 'png',
|
||||
StickerFormatType.apng: 'png',
|
||||
StickerFormatType.lottie: 'json',
|
||||
}
|
||||
# fmt: on
|
||||
return lookup[self]
|
||||
|
||||
|
||||
class InviteTarget(Enum):
|
||||
unknown = 0
|
||||
|
@ -566,18 +566,34 @@ class Intents(BaseFlags):
|
||||
|
||||
@flag_value
|
||||
def emojis(self):
|
||||
""":class:`bool`: Whether guild emoji related events are enabled.
|
||||
""":class:`bool`: Alias of :attr:`.emojis_and_stickers`.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Changed to an alias.
|
||||
"""
|
||||
return 1 << 3
|
||||
|
||||
@alias_flag_value
|
||||
def emojis_and_stickers(self):
|
||||
""":class:`bool`: Whether guild emoji and sticker related events are enabled.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
This corresponds to the following events:
|
||||
|
||||
- :func:`on_guild_emojis_update`
|
||||
- :func:`on_guild_stickers_update`
|
||||
|
||||
This also corresponds to the following attributes and classes in terms of cache:
|
||||
|
||||
- :class:`Emoji`
|
||||
- :class:`GuildSticker`
|
||||
- :meth:`Client.get_emoji`
|
||||
- :meth:`Client.get_sticker`
|
||||
- :meth:`Client.emojis`
|
||||
- :meth:`Client.stickers`
|
||||
- :attr:`Guild.emojis`
|
||||
- :attr:`Guild.stickers`
|
||||
"""
|
||||
return 1 << 3
|
||||
|
||||
|
174
discord/guild.py
174
discord/guild.py
@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE.
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import unicodedata
|
||||
from typing import (
|
||||
Any,
|
||||
ClassVar,
|
||||
@ -72,6 +73,9 @@ from .flags import SystemChannelFlags
|
||||
from .integrations import Integration, _integration_factory
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread
|
||||
from .sticker import GuildSticker
|
||||
from .file import File
|
||||
|
||||
|
||||
__all__ = (
|
||||
'Guild',
|
||||
@ -107,6 +111,7 @@ class BanEntry(NamedTuple):
|
||||
|
||||
class _GuildLimit(NamedTuple):
|
||||
emoji: int
|
||||
stickers: int
|
||||
bitrate: float
|
||||
filesize: int
|
||||
|
||||
@ -140,6 +145,10 @@ class Guild(Hashable):
|
||||
The guild name.
|
||||
emojis: Tuple[:class:`Emoji`, ...]
|
||||
All emojis that the guild owns.
|
||||
stickers: Tuple[:class:`GuildSticker`, ...]
|
||||
All stickers that the guild owns.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
region: :class:`VoiceRegion`
|
||||
The region the guild belongs on. There is a chance that the region
|
||||
will be a :class:`str` if the value is not recognised by the enumerator.
|
||||
@ -234,6 +243,7 @@ class Guild(Hashable):
|
||||
'owner_id',
|
||||
'mfa_level',
|
||||
'emojis',
|
||||
'stickers',
|
||||
'features',
|
||||
'verification_level',
|
||||
'explicit_content_filter',
|
||||
@ -266,11 +276,11 @@ class Guild(Hashable):
|
||||
)
|
||||
|
||||
_PREMIUM_GUILD_LIMITS: ClassVar[Dict[Optional[int], _GuildLimit]] = {
|
||||
None: _GuildLimit(emoji=50, bitrate=96e3, filesize=8388608),
|
||||
0: _GuildLimit(emoji=50, bitrate=96e3, filesize=8388608),
|
||||
1: _GuildLimit(emoji=100, bitrate=128e3, filesize=8388608),
|
||||
2: _GuildLimit(emoji=150, bitrate=256e3, filesize=52428800),
|
||||
3: _GuildLimit(emoji=250, bitrate=384e3, filesize=104857600),
|
||||
None: _GuildLimit(emoji=50, stickers=0, bitrate=96e3, filesize=8388608),
|
||||
0: _GuildLimit(emoji=50, stickers=0, bitrate=96e3, filesize=8388608),
|
||||
1: _GuildLimit(emoji=100, stickers=15, bitrate=128e3, filesize=8388608),
|
||||
2: _GuildLimit(emoji=150, stickers=30, bitrate=256e3, filesize=52428800),
|
||||
3: _GuildLimit(emoji=250, stickers=60, bitrate=384e3, filesize=104857600),
|
||||
}
|
||||
|
||||
def __init__(self, *, data: GuildPayload, state: ConnectionState):
|
||||
@ -412,6 +422,7 @@ class Guild(Hashable):
|
||||
|
||||
self.mfa_level: MFALevel = guild.get('mfa_level')
|
||||
self.emojis: Tuple[Emoji, ...] = tuple(map(lambda d: state.store_emoji(self, d), guild.get('emojis', [])))
|
||||
self.stickers: Tuple[GuildSticker, ...] = tuple(map(lambda d: state.store_sticker(self, d), guild.get('stickers', [])))
|
||||
self.features: List[GuildFeature] = guild.get('features', [])
|
||||
self._splash: Optional[str] = guild.get('splash')
|
||||
self._system_channel_id: Optional[int] = utils._get_as_snowflake(guild, 'system_channel_id')
|
||||
@ -698,6 +709,15 @@ class Guild(Hashable):
|
||||
more_emoji = 200 if 'MORE_EMOJI' in self.features else 50
|
||||
return max(more_emoji, self._PREMIUM_GUILD_LIMITS[self.premium_tier].emoji)
|
||||
|
||||
@property
|
||||
def sticker_limit(self) -> int:
|
||||
""":class:`int`: The maximum number of sticker slots this guild has.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
more_stickers = 60 if 'MORE_STICKERS' in self.features else 15
|
||||
return max(more_stickers, self._PREMIUM_GUILD_LIMITS[self.premium_tier].stickers)
|
||||
|
||||
@property
|
||||
def bitrate_limit(self) -> float:
|
||||
""":class:`float`: The maximum bitrate for voice channels this guild can have."""
|
||||
@ -2027,6 +2047,150 @@ class Guild(Hashable):
|
||||
|
||||
return [convert(d) for d in data]
|
||||
|
||||
async def fetch_stickers(self) -> List[GuildSticker]:
|
||||
r"""|coro|
|
||||
|
||||
Retrieves a list of all :class:`Sticker`\s for the guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an API call. For general usage, consider :attr:`stickers` instead.
|
||||
|
||||
Raises
|
||||
---------
|
||||
HTTPException
|
||||
An error occurred fetching the stickers.
|
||||
|
||||
Returns
|
||||
--------
|
||||
List[:class:`GuildSticker`]
|
||||
The retrieved stickers.
|
||||
"""
|
||||
data = await self._state.http.get_all_guild_stickers(self.id)
|
||||
return [GuildSticker(state=self._state, data=d) for d in data]
|
||||
|
||||
async def fetch_sticker(self, sticker_id: int, /) -> GuildSticker:
|
||||
"""|coro|
|
||||
|
||||
Retrieves a custom :class:`Sticker` from the guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an API call.
|
||||
For general usage, consider iterating over :attr:`stickers` instead.
|
||||
|
||||
Parameters
|
||||
-------------
|
||||
sticker_id: :class:`int`
|
||||
The sticker's ID.
|
||||
|
||||
Raises
|
||||
---------
|
||||
NotFound
|
||||
The sticker requested could not be found.
|
||||
HTTPException
|
||||
An error occurred fetching the sticker.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`GuildSticker`
|
||||
The retrieved sticker.
|
||||
"""
|
||||
data = await self._state.http.get_guild_sticker(self.id, sticker_id)
|
||||
return GuildSticker(state=self._state, data=data)
|
||||
|
||||
async def create_sticker(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
description: Optional[str] = None,
|
||||
emoji: str,
|
||||
file: File,
|
||||
reason: Optional[str] = None,
|
||||
) -> GuildSticker:
|
||||
"""|coro|
|
||||
|
||||
Creates a :class:`Sticker` for the guild.
|
||||
|
||||
You must have :attr:`~Permissions.manage_emojis_and_stickers` permission to
|
||||
do this.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The sticker name. Must be at least 2 characters.
|
||||
description: Optional[:class:`str`]
|
||||
The sticker's description. Can be ``None``.
|
||||
emoji: :class:`str`
|
||||
The name of a unicode emoji that represents the sticker's expression.
|
||||
file: :class:`File`
|
||||
The file of the sticker to upload.
|
||||
reason: :class:`str`
|
||||
The reason for creating this sticker. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
Forbidden
|
||||
You are not allowed to create stickers.
|
||||
HTTPException
|
||||
An error occurred creating a sticker.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`GuildSticker`
|
||||
The created sticker.
|
||||
"""
|
||||
payload = {
|
||||
'name': name,
|
||||
}
|
||||
|
||||
if description:
|
||||
payload['description'] = description
|
||||
|
||||
try:
|
||||
emoji = unicodedata.name(emoji)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
emoji = emoji.replace(' ', '_')
|
||||
|
||||
payload['tags'] = emoji
|
||||
|
||||
data = await self._state.http.create_guild_sticker(self.id, payload, file, reason)
|
||||
return self._state.store_sticker(self, data)
|
||||
|
||||
async def delete_sticker(self, sticker: Snowflake, *, reason: Optional[str] = None) -> None:
|
||||
"""|coro|
|
||||
|
||||
Deletes the custom :class:`Sticker` from the guild.
|
||||
|
||||
You must have :attr:`~Permissions.manage_emojis_and_stickers` permission to
|
||||
do this.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
sticker: :class:`abc.Snowflake`
|
||||
The sticker you are deleting.
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for deleting this sticker. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
Forbidden
|
||||
You are not allowed to delete stickers.
|
||||
HTTPException
|
||||
An error occurred deleting the sticker.
|
||||
"""
|
||||
await self._state.http.delete_guild_sticker(self.id, sticker.id, reason)
|
||||
|
||||
async def fetch_emojis(self) -> List[Emoji]:
|
||||
r"""|coro|
|
||||
|
||||
|
@ -49,7 +49,7 @@ import weakref
|
||||
|
||||
import aiohttp
|
||||
|
||||
from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, GatewayNotFound
|
||||
from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, GatewayNotFound, InvalidArgument
|
||||
from .gateway import DiscordClientWebSocketResponse
|
||||
from . import __version__, utils
|
||||
from .utils import MISSING
|
||||
@ -84,6 +84,7 @@ if TYPE_CHECKING:
|
||||
widget,
|
||||
threads,
|
||||
voice,
|
||||
sticker,
|
||||
)
|
||||
from .types.snowflake import Snowflake, SnowflakeList
|
||||
|
||||
@ -420,9 +421,10 @@ class HTTPClient:
|
||||
tts: bool = False,
|
||||
embed: Optional[embed.Embed] = None,
|
||||
embeds: Optional[List[embed.Embed]] = None,
|
||||
nonce: Optional[str] = None,
|
||||
nonce: Optional[str] = None,
|
||||
allowed_mentions: Optional[message.AllowedMentions] = None,
|
||||
message_reference: Optional[message.MessageReference] = None,
|
||||
stickers: Optional[List[sticker.StickerItem]] = None,
|
||||
components: Optional[List[components.Component]] = None,
|
||||
) -> Response[message.Message]:
|
||||
r = Route('POST', '/channels/{channel_id}/messages', channel_id=channel_id)
|
||||
@ -452,6 +454,9 @@ class HTTPClient:
|
||||
if components:
|
||||
payload['components'] = components
|
||||
|
||||
if stickers:
|
||||
payload['sticker_items'] = stickers
|
||||
|
||||
return self.request(r, json=payload)
|
||||
|
||||
def send_typing(self, channel_id: Snowflake) -> Response[None]:
|
||||
@ -465,10 +470,11 @@ class HTTPClient:
|
||||
content: Optional[str] = None,
|
||||
tts: bool = False,
|
||||
embed: Optional[embed.Embed] = None,
|
||||
embeds: Iterable[Optional[embed.Embed]] = None,
|
||||
embeds: Optional[Iterable[Optional[embed.Embed]]] = None,
|
||||
nonce: Optional[str] = None,
|
||||
allowed_mentions: Optional[message.AllowedMentions] = None,
|
||||
message_reference: Optional[message.MessageReference] = None,
|
||||
stickers: Optional[List[sticker.StickerItem]] = None,
|
||||
components: Optional[List[components.Component]] = None,
|
||||
) -> Response[message.Message]:
|
||||
form = []
|
||||
@ -488,6 +494,8 @@ class HTTPClient:
|
||||
payload['message_reference'] = message_reference
|
||||
if components:
|
||||
payload['components'] = components
|
||||
if stickers:
|
||||
payload['sticker_items'] = stickers
|
||||
|
||||
form.append({'name': 'payload_json', 'value': utils.to_json(payload)})
|
||||
if len(files) == 1:
|
||||
@ -525,6 +533,7 @@ class HTTPClient:
|
||||
nonce: Optional[str] = None,
|
||||
allowed_mentions: Optional[message.AllowedMentions] = None,
|
||||
message_reference: Optional[message.MessageReference] = None,
|
||||
stickers: Optional[List[sticker.StickerItem]] = None,
|
||||
components: Optional[List[components.Component]] = None,
|
||||
) -> Response[message.Message]:
|
||||
r = Route('POST', '/channels/{channel_id}/messages', channel_id=channel_id)
|
||||
@ -538,6 +547,7 @@ class HTTPClient:
|
||||
nonce=nonce,
|
||||
allowed_mentions=allowed_mentions,
|
||||
message_reference=message_reference,
|
||||
stickers=stickers,
|
||||
components=components,
|
||||
)
|
||||
|
||||
@ -1160,6 +1170,54 @@ class HTTPClient:
|
||||
|
||||
return self.request(Route('GET', '/guilds/{guild_id}/prune', guild_id=guild_id), params=params)
|
||||
|
||||
def get_sticker(self, sticker_id: Snowflake) -> Response[sticker.Sticker]:
|
||||
return self.request(Route('GET', '/stickers/{sticker_id}', sticker_id=sticker_id))
|
||||
|
||||
def list_nitro_sticker_packs(self) -> Response[sticker.ListNitroStickerPacks]:
|
||||
return self.request(Route('GET', '/sticker-packs'))
|
||||
|
||||
def get_all_guild_stickers(self, guild_id: Snowflake) -> Response[List[sticker.GuildSticker]]:
|
||||
return self.request(Route('GET', '/guilds/{guild_id}/stickers', guild_id=guild_id))
|
||||
|
||||
def get_guild_sticker(self, guild_id: Snowflake, sticker_id: Snowflake) -> Response[sticker.GuildSticker]:
|
||||
return self.request(Route('GET', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id))
|
||||
|
||||
def create_guild_sticker(self, guild_id: Snowflake, payload: sticker.CreateGuildSticker, file: File, reason: str) -> Response[sticker.GuildSticker]:
|
||||
initial_bytes = file.fp.read(16)
|
||||
|
||||
try:
|
||||
mime_type = utils._get_mime_type_for_image(initial_bytes)
|
||||
except InvalidArgument:
|
||||
if initial_bytes.startswith(b'{'):
|
||||
mime_type = 'application/json'
|
||||
else:
|
||||
mime_type = 'application/octet-stream'
|
||||
finally:
|
||||
file.reset()
|
||||
|
||||
form: List[Dict[str, Any]] = [
|
||||
{
|
||||
'name': 'file',
|
||||
'value': file.fp,
|
||||
'filename': file.filename,
|
||||
'content_type': mime_type,
|
||||
}
|
||||
]
|
||||
|
||||
for k, v in payload.items():
|
||||
form.append({
|
||||
'name': k,
|
||||
'value': v,
|
||||
})
|
||||
|
||||
return self.request(Route('POST', '/guilds/{guild_id}/stickers', guild_id=guild_id), form=form, files=[file], reason=reason)
|
||||
|
||||
def modify_guild_sticker(self, guild_id: Snowflake, sticker_id: Snowflake, payload: sticker.EditGuildSticker, reason: str) -> Response[sticker.GuildSticker]:
|
||||
return self.request(Route('PATCH', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id), json=payload, reason=reason)
|
||||
|
||||
def delete_guild_sticker(self, guild_id: Snowflake, sticker_id: Snowflake, reason: str) -> Response[None]:
|
||||
return self.request(Route('DELETE', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id), reason=reason)
|
||||
|
||||
def get_all_custom_emojis(self, guild_id: Snowflake) -> Response[List[emoji.Emoji]]:
|
||||
return self.request(Route('GET', '/guilds/{guild_id}/emojis', guild_id=guild_id))
|
||||
|
||||
|
@ -45,7 +45,7 @@ from .file import File
|
||||
from .utils import escape_mentions, MISSING
|
||||
from .guild import Guild
|
||||
from .mixins import Hashable
|
||||
from .sticker import Sticker
|
||||
from .sticker import StickerItem
|
||||
from .threads import Thread
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -588,8 +588,8 @@ class Message(Hashable):
|
||||
- ``description``: A string representing the application's description.
|
||||
- ``icon``: A string representing the icon ID of the application.
|
||||
- ``cover_image``: A string representing the embed's image asset ID.
|
||||
stickers: List[:class:`Sticker`]
|
||||
A list of stickers given to the message.
|
||||
stickers: List[:class:`StickerItem`]
|
||||
A list of sticker items given to the message.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
components: List[:class:`Component`]
|
||||
@ -666,7 +666,7 @@ class Message(Hashable):
|
||||
self.tts: bool = data['tts']
|
||||
self.content: str = data['content']
|
||||
self.nonce: Optional[Union[int, str]] = data.get('nonce')
|
||||
self.stickers: List[Sticker] = [Sticker(data=d, state=state) for d in data.get('stickers', [])]
|
||||
self.stickers: List[StickerItem] = [StickerItem(data=d, state=state) for d in data.get('sticker_items', [])]
|
||||
self.components: List[Component] = [_component_factory(d) for d in data.get('components', [])]
|
||||
|
||||
try:
|
||||
|
@ -462,6 +462,14 @@ class Permissions(BaseFlags):
|
||||
""":class:`bool`: Returns ``True`` if a user can create, edit, or delete emojis."""
|
||||
return 1 << 30
|
||||
|
||||
@make_permission_alias('manage_emojis')
|
||||
def manage_emojis_and_stickers(self):
|
||||
""":class:`bool`: An alias for :attr:`manage_emojis`.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return 1 << 30
|
||||
|
||||
@flag_value
|
||||
def use_slash_commands(self) -> int:
|
||||
""":class:`bool`: Returns ``True`` if a user can use slash commands.
|
||||
@ -510,6 +518,22 @@ class Permissions(BaseFlags):
|
||||
"""
|
||||
return 1 << 36
|
||||
|
||||
@flag_value
|
||||
def external_stickers(self) -> int:
|
||||
""":class:`bool`: Returns ``True`` if a user can use stickers from other guilds.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return 1 << 37
|
||||
|
||||
@make_permission_alias('external_stickers')
|
||||
def use_external_stickers(self) -> int:
|
||||
""":class:`bool`: An alias for :attr:`external_stickers`.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return 1 << 37
|
||||
|
||||
PO = TypeVar('PO', bound='PermissionOverwrite')
|
||||
|
||||
def _augment_from_permissions(cls):
|
||||
|
@ -57,6 +57,7 @@ from .interactions import Interaction
|
||||
from .ui.view import ViewStore
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread, ThreadMember
|
||||
from .sticker import GuildSticker
|
||||
|
||||
class ChunkRequest:
|
||||
def __init__(self, guild_id, loop, resolver, *, cache=True):
|
||||
@ -204,6 +205,7 @@ class ConnectionState:
|
||||
# though more testing will have to be done.
|
||||
self._users: Dict[int, User] = {}
|
||||
self._emojis = {}
|
||||
self._stickers = {}
|
||||
self._guilds = {}
|
||||
self._view_store = ViewStore(self)
|
||||
self._voice_clients = {}
|
||||
@ -298,6 +300,11 @@ class ConnectionState:
|
||||
self._emojis[emoji_id] = emoji = Emoji(guild=guild, state=self, data=data)
|
||||
return emoji
|
||||
|
||||
def store_sticker(self, guild, data):
|
||||
sticker_id = int(data['id'])
|
||||
self._stickers[sticker_id] = sticker = GuildSticker(state=self, data=data)
|
||||
return sticker
|
||||
|
||||
def store_view(self, view, message_id=None):
|
||||
self._view_store.add_view(view, message_id)
|
||||
|
||||
@ -324,15 +331,25 @@ class ConnectionState:
|
||||
for emoji in guild.emojis:
|
||||
self._emojis.pop(emoji.id, None)
|
||||
|
||||
for sticker in guild.stickers:
|
||||
self._stickers.pop(sticker.id, None)
|
||||
|
||||
del guild
|
||||
|
||||
@property
|
||||
def emojis(self):
|
||||
return list(self._emojis.values())
|
||||
|
||||
@property
|
||||
def stickers(self):
|
||||
return list(self._stickers.values())
|
||||
|
||||
def get_emoji(self, emoji_id):
|
||||
return self._emojis.get(emoji_id)
|
||||
|
||||
def get_sticker(self, sticker_id):
|
||||
return self._stickers.get(sticker_id)
|
||||
|
||||
@property
|
||||
def private_channels(self):
|
||||
return list(self._private_channels.values())
|
||||
@ -925,6 +942,18 @@ class ConnectionState:
|
||||
guild.emojis = tuple(map(lambda d: self.store_emoji(guild, d), data['emojis']))
|
||||
self.dispatch('guild_emojis_update', guild, before_emojis, guild.emojis)
|
||||
|
||||
def parse_guild_stickers_update(self, data):
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is None:
|
||||
log.debug('GUILD_STICKERS_UPDATE referencing an unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
return
|
||||
|
||||
before_stickers = guild.stickers
|
||||
for emoji in before_stickers:
|
||||
self._stickers.pop(emoji.id, None)
|
||||
guild.stickers = tuple(map(lambda d: self.store_sticker(guild, d), data['stickers']))
|
||||
self.dispatch('guild_stickers_update', guild, before_stickers, guild.stickers)
|
||||
|
||||
def _get_create_guild(self, data):
|
||||
if data.get('unavailable') is False:
|
||||
# GUILD_CREATE with unavailable in the response
|
||||
|
@ -23,24 +23,213 @@ DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
from typing import Literal, TYPE_CHECKING, List, Optional, Tuple, Type, Union
|
||||
import unicodedata
|
||||
|
||||
from .mixins import Hashable
|
||||
from .asset import Asset
|
||||
from .utils import snowflake_time
|
||||
from .enums import StickerType, try_enum
|
||||
from .asset import Asset, AssetMixin
|
||||
from .utils import cached_slot_property, find, snowflake_time, get, MISSING
|
||||
from .errors import InvalidData
|
||||
from .enums import StickerType, StickerFormatType, try_enum
|
||||
|
||||
__all__ = (
|
||||
'StickerPack',
|
||||
'StickerItem',
|
||||
'Sticker',
|
||||
'StandardSticker',
|
||||
'GuildSticker',
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import datetime
|
||||
from .state import ConnectionState
|
||||
from .types.message import Sticker as StickerPayload
|
||||
from .user import User
|
||||
from .guild import Guild
|
||||
from .types.sticker import (
|
||||
StickerPack as StickerPackPayload,
|
||||
StickerItem as StickerItemPayload,
|
||||
Sticker as StickerPayload,
|
||||
StandardSticker as StandardStickerPayload,
|
||||
GuildSticker as GuildStickerPayload,
|
||||
ListNitroStickerPacks as ListNitroStickerPacksPayload
|
||||
)
|
||||
|
||||
|
||||
class Sticker(Hashable):
|
||||
class StickerPack(Hashable):
|
||||
"""Represents a sticker pack.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. container:: operations
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the name of the sticker pack.
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if the sticker pack is equal to another sticker pack.
|
||||
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if the sticker pack is not equal to another sticker pack.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The name of the sticker pack.
|
||||
description: :class:`str`
|
||||
The description of the sticker pack.
|
||||
id: :class:`int`
|
||||
The id of the sticker pack.
|
||||
stickers: List[:class:`StandardSticker`]
|
||||
The stickers of this sticker pack.
|
||||
sku_id: :class:`int`
|
||||
The SKU ID of the sticker pack.
|
||||
cover_sticker_id: :class:`int`
|
||||
The ID of the sticker used for the cover of the sticker pack.
|
||||
cover_sticker: :class:`StandardSticker`
|
||||
The sticker used for the cover of the sticker pack.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'_state',
|
||||
'id',
|
||||
'stickers',
|
||||
'name',
|
||||
'sku_id',
|
||||
'cover_sticker_id',
|
||||
'cover_sticker',
|
||||
'description',
|
||||
'_banner',
|
||||
)
|
||||
|
||||
def __init__(self, *, state: ConnectionState, data: StickerPackPayload) -> None:
|
||||
self._state: ConnectionState = state
|
||||
self._from_data(data)
|
||||
|
||||
def _from_data(self, data: StickerPackPayload) -> None:
|
||||
self.id: int = int(data['id'])
|
||||
stickers = data['stickers']
|
||||
self.stickers: List[StandardSticker] = [StandardSticker(state=self._state, data=sticker) for sticker in stickers]
|
||||
self.name: str = data['name']
|
||||
self.sku_id: int = int(data['sku_id'])
|
||||
self.cover_sticker_id: int = int(data['cover_sticker_id'])
|
||||
self.cover_sticker: StandardSticker = get(self.stickers, id=self.cover_sticker_id) # type: ignore
|
||||
self.description: str = data['description']
|
||||
self._banner: int = int(data['banner_asset_id'])
|
||||
|
||||
@property
|
||||
def banner(self) -> Asset:
|
||||
""":class:`Asset`: The banner asset of the sticker pack."""
|
||||
return Asset._from_sticker_banner(self._state, self._banner)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<StickerPack id={self.id} name={self.name!r} description={self.description!r}>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
class _StickerTag(Hashable, AssetMixin):
|
||||
__slots__ = ()
|
||||
|
||||
id: int
|
||||
format: StickerFormatType
|
||||
|
||||
async def read(self) -> bytes:
|
||||
"""|coro|
|
||||
|
||||
Retrieves the content of this sticker as a :class:`bytes` object.
|
||||
|
||||
.. note::
|
||||
|
||||
Stickers that use the :attr:`StickerFormatType.lottie` format cannot be read.
|
||||
|
||||
Raises
|
||||
------
|
||||
HTTPException
|
||||
Downloading the asset failed.
|
||||
NotFound
|
||||
The asset was deleted.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`bytes`
|
||||
The content of the asset.
|
||||
"""
|
||||
if self.format is StickerFormatType.lottie:
|
||||
raise TypeError('Cannot read stickers of format "lottie".')
|
||||
return await super().read()
|
||||
|
||||
|
||||
class StickerItem(_StickerTag):
|
||||
"""Represents a sticker item.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. container:: operations
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the name of the sticker item.
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if the sticker item is equal to another sticker item.
|
||||
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if the sticker item is not equal to another sticker item.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The sticker's name.
|
||||
id: :class:`int`
|
||||
The id of the sticker.
|
||||
format: :class:`StickerFormatType`
|
||||
The format for the sticker's image.
|
||||
url: :class:`str`
|
||||
The URL for the sticker's image.
|
||||
"""
|
||||
|
||||
__slots__ = ('_state', 'name', 'id', 'format', 'url')
|
||||
|
||||
def __init__(self, *, state: ConnectionState, data: StickerItemPayload):
|
||||
self._state: ConnectionState = state
|
||||
self.name: str = data['name']
|
||||
self.id: int = int(data['id'])
|
||||
self.format: StickerFormatType = try_enum(StickerFormatType, data['format_type'])
|
||||
self.url: str = f'{Asset.BASE}/stickers/{self.id}.{self.format.file_extension}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<StickerItem id={self.id} name={self.name!r} format={self.format}>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
async def fetch(self) -> Union[Sticker, StandardSticker, GuildSticker]:
|
||||
"""|coro|
|
||||
|
||||
Attempts to retrieve the full sticker data of the sticker item.
|
||||
|
||||
Raises
|
||||
--------
|
||||
HTTPException
|
||||
Retrieving the sticker failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
Union[:class:`StandardSticker`, :class:`GuildSticker`]
|
||||
The retrieved sticker.
|
||||
"""
|
||||
data: StickerPayload = await self._state.http.get_sticker(self.id)
|
||||
cls, _ = _sticker_factory(data['type']) # type: ignore
|
||||
return cls(state=self._state, data=data)
|
||||
|
||||
|
||||
class Sticker(_StickerTag):
|
||||
"""Represents a sticker.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
@ -69,30 +258,27 @@ class Sticker(Hashable):
|
||||
The description of the sticker.
|
||||
pack_id: :class:`int`
|
||||
The id of the sticker's pack.
|
||||
format: :class:`StickerType`
|
||||
format: :class:`StickerFormatType`
|
||||
The format for the sticker's image.
|
||||
tags: List[:class:`str`]
|
||||
A list of tags for the sticker.
|
||||
url: :class:`str`
|
||||
The URL for the sticker's image.
|
||||
"""
|
||||
|
||||
__slots__ = ('_state', 'id', 'name', 'description', 'pack_id', 'format', '_image', 'tags')
|
||||
__slots__ = ('_state', 'id', 'name', 'description', 'format', 'url')
|
||||
|
||||
def __init__(self, *, state: ConnectionState, data: StickerPayload):
|
||||
def __init__(self, *, state: ConnectionState, data: StickerPayload) -> None:
|
||||
self._state: ConnectionState = state
|
||||
self._from_data(data)
|
||||
|
||||
def _from_data(self, data: StickerPayload) -> None:
|
||||
self.id: int = int(data['id'])
|
||||
self.name: str = data['name']
|
||||
self.description: str = data['description']
|
||||
self.pack_id: int = int(data.get('pack_id', 0))
|
||||
self.format: StickerType = try_enum(StickerType, data['format_type'])
|
||||
self._image: str = data['asset']
|
||||
|
||||
try:
|
||||
self.tags: List[str] = [tag.strip() for tag in data['tags'].split(',')]
|
||||
except KeyError:
|
||||
self.tags = []
|
||||
self.format: StickerFormatType = try_enum(StickerFormatType, data['format_type'])
|
||||
self.url: str = f'{Asset.BASE}/stickers/{self.id}.{self.format.file_extension}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>'
|
||||
return f'<Sticker id={self.id} name={self.name!r}>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
@ -102,19 +288,229 @@ class Sticker(Hashable):
|
||||
""":class:`datetime.datetime`: Returns the sticker's creation time in UTC."""
|
||||
return snowflake_time(self.id)
|
||||
|
||||
@property
|
||||
def image(self) -> Optional[Asset]:
|
||||
"""Returns an :class:`Asset` for the sticker's image.
|
||||
|
||||
.. note::
|
||||
This will return ``None`` if the format is ``StickerType.lottie``.
|
||||
class StandardSticker(Sticker):
|
||||
"""Represents a sticker that is found in a standard sticker pack.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. container:: operations
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the name of the sticker.
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if the sticker is equal to another sticker.
|
||||
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if the sticker is not equal to another sticker.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name: :class:`str`
|
||||
The sticker's name.
|
||||
id: :class:`int`
|
||||
The id of the sticker.
|
||||
description: :class:`str`
|
||||
The description of the sticker.
|
||||
pack_id: :class:`int`
|
||||
The id of the sticker's pack.
|
||||
format: :class:`StickerFormatType`
|
||||
The format for the sticker's image.
|
||||
tags: List[:class:`str`]
|
||||
A list of tags for the sticker.
|
||||
sort_value: :class:`int`
|
||||
The sticker's sort order within its pack.
|
||||
"""
|
||||
|
||||
__slots__ = ('sort_value', 'pack_id', 'type', 'tags')
|
||||
|
||||
def _from_data(self, data: StandardStickerPayload) -> None:
|
||||
super()._from_data(data)
|
||||
self.sort_value: int = data['sort_value']
|
||||
self.pack_id: int = int(data['pack_id'])
|
||||
self.type: StickerType = StickerType.standard
|
||||
|
||||
try:
|
||||
self.tags: List[str] = [tag.strip() for tag in data['tags'].split(',')]
|
||||
except KeyError:
|
||||
self.tags = []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<StandardSticker id={self.id} name={self.name!r} pack_id={self.pack_id}>'
|
||||
|
||||
async def pack(self) -> StickerPack:
|
||||
"""|coro|
|
||||
|
||||
Retrieves the sticker pack that this sticker belongs to.
|
||||
|
||||
Raises
|
||||
--------
|
||||
InvalidData
|
||||
The corresponding sticker pack was not found.
|
||||
HTTPException
|
||||
Retrieving the sticker pack failed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Optional[:class:`Asset`]
|
||||
The resulting CDN asset.
|
||||
--------
|
||||
:class:`StickerPack`
|
||||
The retrieved sticker pack.
|
||||
"""
|
||||
if self.format is StickerType.lottie:
|
||||
return None
|
||||
data: ListNitroStickerPacksPayload = await self._state.http.list_nitro_sticker_packs()
|
||||
packs = data['sticker_packs']
|
||||
pack = find(lambda d: int(d['id']) == self.pack_id, packs)
|
||||
|
||||
return Asset._from_sticker(self._state, self.id, self._image)
|
||||
if pack:
|
||||
return StickerPack(state=self._state, data=pack)
|
||||
raise InvalidData(f'Could not find corresponding sticker pack for {self!r}')
|
||||
|
||||
|
||||
class GuildSticker(Sticker):
|
||||
"""Represents a sticker that belongs to a guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. container:: operations
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the name of the sticker.
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if the sticker is equal to another sticker.
|
||||
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if the sticker is not equal to another sticker.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name: :class:`str`
|
||||
The sticker's name.
|
||||
id: :class:`int`
|
||||
The id of the sticker.
|
||||
description: :class:`str`
|
||||
The description of the sticker.
|
||||
format: :class:`StickerFormatType`
|
||||
The format for the sticker's image.
|
||||
available: :class:`bool`
|
||||
Whether this sticker is available for use.
|
||||
guild_id: :class:`int`
|
||||
The ID of the guild that this sticker is from.
|
||||
user: Optional[:class:`User`]
|
||||
The user that created this sticker. This can only be retrieved using :meth:`Guild.fetch_sticker` and
|
||||
having the :attr:`~Permissions.manage_emojis_and_stickers` permission.
|
||||
emoji: :class:`str`
|
||||
The name of a unicode emoji that represents this sticker.
|
||||
"""
|
||||
|
||||
__slots__ = ('available', 'guild_id', 'user', 'emoji', 'type', '_cs_guild')
|
||||
|
||||
def _from_data(self, data: GuildStickerPayload) -> None:
|
||||
super()._from_data(data)
|
||||
self.available: bool = data['available']
|
||||
self.guild_id: int = int(data['guild_id'])
|
||||
user = data.get('user')
|
||||
self.user: Optional[User] = self._state.store_user(user) if user else None
|
||||
self.emoji: str = data['tags']
|
||||
self.type: StickerType = StickerType.guild
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<GuildSticker name={self.name!r} id={self.id} guild_id={self.guild_id} user={self.user!r}>'
|
||||
|
||||
@cached_slot_property('_cs_guild')
|
||||
def guild(self) -> Optional[Guild]:
|
||||
"""Optional[:class:`Guild`]: The guild that this sticker is from.
|
||||
Could be ``None`` if the bot is not in the guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._state._get_guild(self.guild_id)
|
||||
|
||||
async def edit(
|
||||
self,
|
||||
*,
|
||||
name: str = MISSING,
|
||||
description: str = MISSING,
|
||||
emoji: str = MISSING,
|
||||
reason: Optional[str] = None,
|
||||
) -> None:
|
||||
"""|coro|
|
||||
|
||||
Edits a :class:`Sticker` for the guild.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The sticker's new name. Must be at least 2 characters.
|
||||
description: Optional[:class:`str`]
|
||||
The sticker's new description. Can be ``None``.
|
||||
emoji: :class:`str`
|
||||
The name of a unicode emoji that represents the sticker's expression.
|
||||
reason: :class:`str`
|
||||
The reason for editing this sticker. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
Forbidden
|
||||
You are not allowed to edit stickers.
|
||||
HTTPException
|
||||
An error occurred editing the sticker.
|
||||
"""
|
||||
payload = {}
|
||||
|
||||
if name is not MISSING:
|
||||
payload['name'] = name
|
||||
|
||||
if description is not MISSING:
|
||||
payload['description'] = description
|
||||
|
||||
if emoji is not MISSING:
|
||||
try:
|
||||
emoji = unicodedata.name(emoji)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
emoji = emoji.replace(' ', '_')
|
||||
|
||||
payload['tags'] = emoji
|
||||
|
||||
data: GuildStickerPayload = await self._state.http.modify_guild_sticker(self.guild_id, self.id, payload, reason)
|
||||
|
||||
self._from_data(data)
|
||||
|
||||
async def delete(self, *, reason: Optional[str] = None) -> None:
|
||||
"""|coro|
|
||||
|
||||
Deletes the custom :class:`Sticker` from the guild.
|
||||
|
||||
You must have :attr:`~Permissions.manage_emojis_and_stickers` permission to
|
||||
do this.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for deleting this sticker. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
Forbidden
|
||||
You are not allowed to delete stickers.
|
||||
HTTPException
|
||||
An error occurred deleting the sticker.
|
||||
"""
|
||||
await self._state.http.delete_guild_sticker(self.guild_id, self.id, reason)
|
||||
|
||||
|
||||
def _sticker_factory(sticker_type: Literal[1, 2]) -> Tuple[Type[Union[StandardSticker, GuildSticker, Sticker]], StickerType]:
|
||||
value = try_enum(StickerType, sticker_type)
|
||||
if value == StickerType.standard:
|
||||
return StandardSticker, value
|
||||
elif value == StickerType.guild:
|
||||
return GuildSticker, value
|
||||
else:
|
||||
return Sticker, value
|
||||
|
@ -73,6 +73,9 @@ AuditLogEvent = Literal[
|
||||
83,
|
||||
84,
|
||||
85,
|
||||
90,
|
||||
91,
|
||||
92,
|
||||
110,
|
||||
111,
|
||||
112,
|
||||
@ -81,14 +84,14 @@ AuditLogEvent = Literal[
|
||||
|
||||
class _AuditLogChange_Str(TypedDict):
|
||||
key: Literal[
|
||||
'name', 'description', 'preferred_locale', 'vanity_url_code', 'topic', 'code', 'allow', 'deny', 'permissions'
|
||||
'name', 'description', 'preferred_locale', 'vanity_url_code', 'topic', 'code', 'allow', 'deny', 'permissions', 'tags'
|
||||
]
|
||||
new_value: str
|
||||
old_value: str
|
||||
|
||||
|
||||
class _AuditLogChange_AssetHash(TypedDict):
|
||||
key: Literal['icon_hash', 'splash_hash', 'discovery_splash_hash', 'banner_hash', 'avatar_hash']
|
||||
key: Literal['icon_hash', 'splash_hash', 'discovery_splash_hash', 'banner_hash', 'avatar_hash', 'asset']
|
||||
new_value: str
|
||||
old_value: str
|
||||
|
||||
@ -105,6 +108,7 @@ class _AuditLogChange_Snowflake(TypedDict):
|
||||
'application_id',
|
||||
'channel_id',
|
||||
'inviter_id',
|
||||
'guild_id',
|
||||
]
|
||||
new_value: Snowflake
|
||||
old_value: Snowflake
|
||||
@ -123,6 +127,7 @@ class _AuditLogChange_Bool(TypedDict):
|
||||
'enabled_emoticons',
|
||||
'region',
|
||||
'rtc_region',
|
||||
'available',
|
||||
'archived',
|
||||
'locked',
|
||||
]
|
||||
|
@ -33,6 +33,7 @@ from .embed import Embed
|
||||
from .channel import ChannelType
|
||||
from .components import Component
|
||||
from .interactions import MessageInteraction
|
||||
from .sticker import StickerItem
|
||||
|
||||
|
||||
class ChannelMention(TypedDict):
|
||||
@ -89,22 +90,6 @@ class MessageReference(TypedDict, total=False):
|
||||
fail_if_not_exists: bool
|
||||
|
||||
|
||||
class _StickerOptional(TypedDict, total=False):
|
||||
tags: str
|
||||
|
||||
|
||||
StickerFormatType = Literal[1, 2, 3]
|
||||
|
||||
|
||||
class Sticker(_StickerOptional):
|
||||
id: Snowflake
|
||||
pack_id: Snowflake
|
||||
name: str
|
||||
description: str
|
||||
asset: str
|
||||
format_type: StickerFormatType
|
||||
|
||||
|
||||
class _MessageOptional(TypedDict, total=False):
|
||||
guild_id: Snowflake
|
||||
member: Member
|
||||
@ -117,7 +102,7 @@ class _MessageOptional(TypedDict, total=False):
|
||||
application_id: Snowflake
|
||||
message_reference: MessageReference
|
||||
flags: int
|
||||
stickers: List[Sticker]
|
||||
sticker_items: List[StickerItem]
|
||||
referenced_message: Optional[Message]
|
||||
interaction: MessageInteraction
|
||||
components: List[Component]
|
||||
|
93
discord/types/sticker.py
Normal file
93
discord/types/sticker.py
Normal file
@ -0,0 +1,93 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Rapptz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Literal, TypedDict, Union
|
||||
from .snowflake import Snowflake
|
||||
from .user import User
|
||||
|
||||
StickerFormatType = Literal[1, 2, 3]
|
||||
|
||||
|
||||
class StickerItem(TypedDict):
|
||||
id: Snowflake
|
||||
name: str
|
||||
format_type: StickerFormatType
|
||||
|
||||
|
||||
class BaseSticker(TypedDict):
|
||||
id: Snowflake
|
||||
name: str
|
||||
description: str
|
||||
tags: str
|
||||
format_type: StickerFormatType
|
||||
|
||||
|
||||
class StandardSticker(BaseSticker):
|
||||
type: Literal[1]
|
||||
sort_value: int
|
||||
pack_id: Snowflake
|
||||
|
||||
|
||||
class _GuildStickerOptional(TypedDict, total=False):
|
||||
user: User
|
||||
|
||||
|
||||
class GuildSticker(BaseSticker, _GuildStickerOptional):
|
||||
type: Literal[2]
|
||||
available: bool
|
||||
guild_id: Snowflake
|
||||
|
||||
|
||||
Sticker = Union[BaseSticker, StandardSticker, GuildSticker]
|
||||
|
||||
|
||||
class StickerPack(TypedDict):
|
||||
id: Snowflake
|
||||
stickers: List[StandardSticker]
|
||||
name: str
|
||||
sku_id: Snowflake
|
||||
cover_sticker_id: Snowflake
|
||||
description: str
|
||||
banner_asset_id: Snowflake
|
||||
|
||||
|
||||
class _CreateGuildStickerOptional(TypedDict, total=False):
|
||||
description: str
|
||||
|
||||
|
||||
class CreateGuildSticker(_CreateGuildStickerOptional):
|
||||
name: str
|
||||
tags: str
|
||||
|
||||
|
||||
class EditGuildSticker(TypedDict, total=False):
|
||||
name: str
|
||||
tags: str
|
||||
description: str
|
||||
|
||||
|
||||
class ListNitroStickerPacks(TypedDict):
|
||||
sticker_packs: List[StickerPack]
|
162
docs/api.rst
162
docs/api.rst
@ -926,7 +926,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||
|
||||
Called when a :class:`Guild` adds or removes :class:`Emoji`.
|
||||
|
||||
This requires :attr:`Intents.emojis` to be enabled.
|
||||
This requires :attr:`Intents.emojis_and_stickers` to be enabled.
|
||||
|
||||
:param guild: The guild who got their emojis updated.
|
||||
:type guild: :class:`Guild`
|
||||
@ -935,6 +935,21 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||
:param after: A list of emojis after the update.
|
||||
:type after: Sequence[:class:`Emoji`]
|
||||
|
||||
.. function:: on_guild_stickers_update(guild, before, after)
|
||||
|
||||
Called when a :class:`Guild` updates its stickers.
|
||||
|
||||
This requires :attr:`Intents.emojis_and_stickers` to be enabled.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param guild: The guild who got their stickers updated.
|
||||
:type guild: :class:`Guild`
|
||||
:param before: A list of stickers before the update.
|
||||
:type before: Sequence[:class:`GuildSticker`]
|
||||
:param after: A list of stickers after the update.
|
||||
:type after: Sequence[:class:`GuildSticker`]
|
||||
|
||||
.. function:: on_guild_available(guild)
|
||||
on_guild_unavailable(guild)
|
||||
|
||||
@ -2205,6 +2220,63 @@ of :class:`enum.Enum`.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: sticker_create
|
||||
|
||||
A sticker was created.
|
||||
|
||||
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||
the :class:`GuildSticker` or :class:`Object` with the ID of the sticker
|
||||
which was updated.
|
||||
|
||||
Possible attributes for :class:`AuditLogDiff`:
|
||||
|
||||
- :attr:`~AuditLogDiff.name`
|
||||
- :attr:`~AuditLogDiff.emoji`
|
||||
- :attr:`~AuditLogDiff.type`
|
||||
- :attr:`~AuditLogDiff.format_type`
|
||||
- :attr:`~AuditLogDiff.description`
|
||||
- :attr:`~AuditLogDiff.available`
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: sticker_update
|
||||
|
||||
A sticker was updated.
|
||||
|
||||
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||
the :class:`GuildSticker` or :class:`Object` with the ID of the sticker
|
||||
which was updated.
|
||||
|
||||
Possible attributes for :class:`AuditLogDiff`:
|
||||
|
||||
- :attr:`~AuditLogDiff.name`
|
||||
- :attr:`~AuditLogDiff.emoji`
|
||||
- :attr:`~AuditLogDiff.type`
|
||||
- :attr:`~AuditLogDiff.format_type`
|
||||
- :attr:`~AuditLogDiff.description`
|
||||
- :attr:`~AuditLogDiff.available`
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: sticker_delete
|
||||
|
||||
A sticker was deleted.
|
||||
|
||||
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||
the :class:`GuildSticker` or :class:`Object` with the ID of the sticker
|
||||
which was updated.
|
||||
|
||||
Possible attributes for :class:`AuditLogDiff`:
|
||||
|
||||
- :attr:`~AuditLogDiff.name`
|
||||
- :attr:`~AuditLogDiff.emoji`
|
||||
- :attr:`~AuditLogDiff.type`
|
||||
- :attr:`~AuditLogDiff.format_type`
|
||||
- :attr:`~AuditLogDiff.description`
|
||||
- :attr:`~AuditLogDiff.available`
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: thread_create
|
||||
|
||||
A thread was created.
|
||||
@ -2356,6 +2428,20 @@ of :class:`enum.Enum`.
|
||||
|
||||
.. class:: StickerType
|
||||
|
||||
Represents the type of sticker.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: standard
|
||||
|
||||
Represents a standard sticker that all Nitro users can use.
|
||||
|
||||
.. attribute:: guild
|
||||
|
||||
Represents a custom sticker created in a guild.
|
||||
|
||||
.. class:: StickerFormatType
|
||||
|
||||
Represents the type of sticker images.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
@ -2825,15 +2911,9 @@ AuditLogDiff
|
||||
|
||||
.. attribute:: type
|
||||
|
||||
The type of channel or channel permission overwrite.
|
||||
The type of channel or sticker.
|
||||
|
||||
If the type is an :class:`int`, then it is a type of channel which can be either
|
||||
``0`` to indicate a text channel or ``1`` to indicate a voice channel.
|
||||
|
||||
If the type is a :class:`str`, then it is a type of permission overwrite which
|
||||
can be either ``'role'`` or ``'member'``.
|
||||
|
||||
:type: Union[:class:`int`, :class:`str`]
|
||||
:type: Union[:class:`ChannelType`, :class:`StickerType`]
|
||||
|
||||
.. attribute:: topic
|
||||
|
||||
@ -3040,6 +3120,38 @@ AuditLogDiff
|
||||
|
||||
:type: :class:`VideoQualityMode`
|
||||
|
||||
.. attribute:: format_type
|
||||
|
||||
The format type of a sticker being changed.
|
||||
|
||||
See also :attr:`GuildSticker.format_type`
|
||||
|
||||
:type: :class:`StickerFormatType`
|
||||
|
||||
.. attribute:: emoji
|
||||
|
||||
The name of the emoji that represents a sticker being changed.
|
||||
|
||||
See also :attr:`GuildSticker.emoji`
|
||||
|
||||
:type: :class:`str`
|
||||
|
||||
.. attribute:: description
|
||||
|
||||
The description of a sticker being changed.
|
||||
|
||||
See also :attr:`GuildSticker.description`
|
||||
|
||||
:type: :class:`str`
|
||||
|
||||
.. attribute:: available
|
||||
|
||||
The availability of a sticker being changed.
|
||||
|
||||
See also :attr:`GuildSticker.available`
|
||||
|
||||
:type: :class:`bool`
|
||||
|
||||
.. attribute:: archived
|
||||
|
||||
The thread is now archived.
|
||||
@ -3620,6 +3732,22 @@ Widget
|
||||
.. autoclass:: Widget()
|
||||
:members:
|
||||
|
||||
StickerPack
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: StickerPack
|
||||
|
||||
.. autoclass:: StickerPack()
|
||||
:members:
|
||||
|
||||
StickerItem
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: StickerItem
|
||||
|
||||
.. autoclass:: StickerItem()
|
||||
:members:
|
||||
|
||||
Sticker
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@ -3628,6 +3756,22 @@ Sticker
|
||||
.. autoclass:: Sticker()
|
||||
:members:
|
||||
|
||||
StandardSticker
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: StandardSticker
|
||||
|
||||
.. autoclass:: StandardSticker()
|
||||
:members:
|
||||
|
||||
GuildSticker
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: GuildSticker
|
||||
|
||||
.. autoclass:: GuildSticker()
|
||||
:members:
|
||||
|
||||
RawMessageDeleteEvent
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user