Browse Source

implement guild stickers

pull/7313/head
Nadir Chowdhury 4 years ago
committed by GitHub
parent
commit
60d82cf908
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      discord/abc.py
  2. 6
      discord/asset.py
  3. 27
      discord/audit_logs.py
  4. 69
      discord/client.py
  5. 25
      discord/enums.py
  6. 18
      discord/flags.py
  7. 174
      discord/guild.py
  8. 64
      discord/http.py
  9. 8
      discord/message.py
  10. 24
      discord/permissions.py
  11. 29
      discord/state.py
  12. 456
      discord/sticker.py
  13. 9
      discord/types/audit_log.py
  14. 19
      discord/types/message.py
  15. 93
      discord/types/sticker.py
  16. 162
      docs/api.rst

21
discord/abc.py

@ -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)

6
discord/asset.py

@ -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,
)

27
discord/audit_logs.py

@ -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)

69
discord/client.py

@ -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|

25
discord/enums.py

@ -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

18
discord/flags.py

@ -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

@ -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|

64
discord/http.py

@ -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))

8
discord/message.py

@ -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:

24
discord/permissions.py

@ -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):

29
discord/state.py

@ -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

456
discord/sticker.py

@ -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
--------
:class:`StickerPack`
The retrieved sticker pack.
"""
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)
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
-------
Optional[:class:`Asset`]
The resulting CDN asset.
Forbidden
You are not allowed to delete stickers.
HTTPException
An error occurred deleting the sticker.
"""
if self.format is StickerType.lottie:
return None
await self._state.http.delete_guild_sticker(self.guild_id, self.id, reason)
return Asset._from_sticker(self._state, self.id, self._image)
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

9
discord/types/audit_log.py

@ -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',
]

19
discord/types/message.py

@ -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

@ -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

@ -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…
Cancel
Save