Browse Source

Add missing types

pull/10109/head
dolfies 2 years ago
parent
commit
fd240be395
  1. 8
      discord/abc.py
  2. 10
      discord/calls.py
  3. 127
      discord/commands.py
  4. 81
      discord/components.py
  5. 19
      discord/file.py
  6. 58
      discord/http.py
  7. 3
      discord/interactions.py
  8. 6
      discord/member.py
  9. 9
      discord/message.py
  10. 11
      discord/modal.py
  11. 81
      discord/settings.py
  12. 90
      discord/state.py
  13. 7
      discord/types/activity.py
  14. 4
      discord/types/channel.py
  15. 40
      discord/types/command.py
  16. 16
      discord/types/components.py
  17. 153
      discord/types/gateway.py
  18. 121
      discord/types/interactions.py
  19. 6
      discord/types/invite.py
  20. 6
      discord/types/member.py
  21. 11
      discord/types/message.py
  22. 65
      discord/types/user.py

8
discord/abc.py

@ -262,7 +262,7 @@ async def _handle_commands(
prev_cursor = cursor
cursor = data['cursor'].get('next')
cmds = data['application_commands']
apps: Dict[int, dict] = {int(app['id']): app for app in data.get('applications') or []}
apps = {int(app['id']): app for app in data.get('applications') or []}
for cmd in cmds:
# Handle faked parameters
@ -280,8 +280,10 @@ async def _handle_commands(
except ValueError:
pass
cmd['application'] = apps.get(int(cmd['application_id']))
yield cls(state=state, data=cmd, channel=channel, target=target)
application_data = apps.get(int(cmd['application_id']))
application = state.create_integration_application(application_data) if application_data else None
yield cls(state=state, data=cmd, channel=channel, target=target, application=application)
cmd_ids = None
if application_id or len(cmds) < min(limit if limit else 25, 25) or len(cmds) == limit == 25:

10
discord/calls.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
import datetime
from typing import Callable, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, Union
from . import utils
from .errors import ClientException
@ -39,6 +39,7 @@ if TYPE_CHECKING:
from .member import VoiceState
from .message import Message
from .state import ConnectionState
from .types.gateway import CallCreateEvent, CallUpdateEvent
from .user import BaseUser, User
_PrivateChannel = Union[abc.DMChannel, abc.GroupChannel]
@ -144,7 +145,7 @@ class PrivateCall:
def __init__(
self,
*,
data: dict,
data: Union[CallCreateEvent, CallUpdateEvent],
state: ConnectionState,
message: Optional[Message],
channel: abc.PrivateChannel,
@ -153,7 +154,6 @@ class PrivateCall:
self._cs_message = message
self.channel = channel # type: ignore # Will always be a DMChannel here
self._ended: bool = False
self._update(data)
def _delete(self) -> None:
@ -168,7 +168,7 @@ class PrivateCall:
state = self.voice_state_for(user)
return bool(state and state.channel and state.channel.id == self.channel.id)
def _update(self, data) -> None:
def _update(self, data: Union[CallCreateEvent, CallUpdateEvent]) -> None:
self._message_id = int(data['message_id'])
self.unavailable = data.get('unavailable', False)
try:
@ -179,7 +179,7 @@ class PrivateCall:
channel = self.channel
recipients = self._get_recipients()
lookup = {u.id: u for u in recipients}
self._ringing = tuple(filter(None, map(lookup.get, data.get('ringing', []))))
self._ringing = tuple(filter(None, map(lookup.get, [int(x) for x in data.get('ringing', [])])))
for vs in data.get('voice_states', []):
self._state._update_voice_state(vs, channel.id)

127
discord/commands.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Protocol, Tuple, Type, Union, runtime_checkable
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Protocol, Tuple, Type, Union, runtime_checkable
from .enums import ApplicationCommandOptionType, ApplicationCommandType, ChannelType, InteractionType, try_enum
from .interactions import _wrapped_interaction
@ -38,8 +38,23 @@ if TYPE_CHECKING:
from .file import _FileBase
from .guild import Guild
from .interactions import Interaction
from .message import Attachment, Message
from .message import Message
from .state import ConnectionState
from .types.command import (
ApplicationCommand as ApplicationCommandPayload,
ApplicationCommandOption,
ApplicationCommandOptionChoice as OptionChoicePayload,
SubCommand as SubCommandPayload,
_ValueApplicationCommandOption as OptionPayload,
)
from .types.interactions import (
ApplicationCommandInteractionData,
ChatInputCommandInteractionData,
MessageCommandInteractionData,
UserCommandInteractionData,
ApplicationCommandInteractionDataOption as InteractionDataOption,
)
from .types.message import PartialAttachment as PartialAttachmentPayload
__all__ = (
@ -113,7 +128,10 @@ class ApplicationCommand(Protocol):
return self.name
async def __call__(
self, data: dict, files: Optional[List[_FileBase]] = None, channel: Optional[Messageable] = None
self,
data: ApplicationCommandInteractionData,
files: Optional[List[_FileBase]] = None,
channel: Optional[Messageable] = None,
) -> Interaction:
channel = channel or self.target_channel
if channel is None:
@ -174,7 +192,6 @@ class BaseCommand(ApplicationCommand, Hashable):
'description',
'id',
'version',
'type',
'application',
'application_id',
'dm_permission',
@ -186,21 +203,27 @@ class BaseCommand(ApplicationCommand, Hashable):
'_default_member_permissions',
)
if TYPE_CHECKING:
type: ApplicationCommandType
def __init__(
self, *, state: ConnectionState, data: Dict[str, Any], channel: Optional[Messageable] = None, **kwargs
self,
*,
state: ConnectionState,
data: ApplicationCommandPayload,
channel: Optional[Messageable] = None,
application: Optional[IntegrationApplication] = None,
**kwargs,
) -> None:
self._state = state
self._data = data
self.application = application
self.name = data['name']
self.description = data['description']
self._channel = channel
self.application_id: int = int(data['application_id'])
self.id: int = int(data['id'])
self.version = int(data['version'])
self.type = try_enum(ApplicationCommandType, data['type'])
application = data.get('application')
self.application = state.create_integration_application(application) if application else None
self._default_member_permissions = _get_as_snowflake(data, 'default_member_permissions')
dm_permission = data.get('dm_permission') # Null means true?
@ -225,28 +248,29 @@ class SlashMixin(ApplicationCommand, Protocol):
async def __call__(
self,
options: List[dict],
options: List[InteractionDataOption],
files: Optional[List[_FileBase]],
attachments: List[Attachment],
attachments: List[PartialAttachmentPayload],
channel: Optional[Messageable] = None,
) -> Interaction:
obj = self._parent
command = obj._data
command['name_localized'] = command['name']
data = {
data: ChatInputCommandInteractionData = {
'application_command': command,
'attachments': attachments,
'id': str(obj.id),
'name': obj.name,
'options': options,
'type': obj.type.value,
'type': ApplicationCommandType.chat_input.value,
'version': str(obj.version),
}
if self.guild_id:
data['guild_id'] = str(self.guild_id)
return await super().__call__(data, files, channel)
def _parse_kwargs(self, kwargs: Dict[str, Any]) -> Tuple[List[Dict[str, Any]], List[_FileBase], List[Attachment]]:
def _parse_kwargs(
self, kwargs: Dict[str, Any]
) -> Tuple[List[InteractionDataOption], List[_FileBase], List[PartialAttachmentPayload]]:
possible_options = {o.name: o for o in self.options}
kwargs = {k: v for k, v in kwargs.items() if k in possible_options}
options = []
@ -286,18 +310,18 @@ class SlashMixin(ApplicationCommand, Protocol):
return options, files, attachments
def _unwrap_options(self, data: List[Dict[str, Any]]) -> None:
def _unwrap_options(self, data: List[ApplicationCommandOption]) -> None:
options = []
children = []
for option in data:
type = try_enum(ApplicationCommandOptionType, option['type'])
if type in {
if type in (
ApplicationCommandOptionType.sub_command,
ApplicationCommandOptionType.sub_command_group,
}:
children.append(SubCommand(parent=self, data=option))
):
children.append(SubCommand(parent=self, data=option)) # type: ignore
else:
options.append(Option(option))
options.append(Option(option)) # type: ignore
self.options = options
self.children = children
@ -340,8 +364,6 @@ class UserCommand(BaseCommand):
The command's name.
description: :class:`str`
The command's description, if any.
type: :class:`ApplicationCommandType`
The type of application command. This will always be :attr:`ApplicationCommandType.user`.
dm_permission: :class:`bool`
Whether the command is enabled in DMs.
nsfw: :class:`bool`
@ -386,18 +408,23 @@ class UserCommand(BaseCommand):
raise TypeError('__call__() missing 1 required positional argument: \'user\'')
command = self._data
data = {
data: UserCommandInteractionData = {
'application_command': command,
'attachments': [],
'id': str(self.id),
'name': self.name,
'options': [],
'target_id': str(user.id),
'type': self.type.value,
'type': ApplicationCommandType.user.value,
'version': str(self.version),
}
return await super().__call__(data, None, channel)
@property
def type(self) -> Literal[ApplicationCommandType.user]:
""":class:`ApplicationCommandType`: The type of application command. This is always :attr:`ApplicationCommandType.user`."""
return ApplicationCommandType.user
@property
def target_user(self) -> Optional[Snowflake]:
"""Optional[:class:`~abc.Snowflake`]: The user this application command will be used on.
@ -452,8 +479,6 @@ class MessageCommand(BaseCommand):
The command's name.
description: :class:`str`
The command's description, if any.
type: :class:`ApplicationCommandType`
The type of application command. This will always be :attr:`ApplicationCommandType.message`.
dm_permission: :class:`bool`
Whether the command is enabled in DMs.
nsfw: :class:`bool`
@ -498,8 +523,7 @@ class MessageCommand(BaseCommand):
raise TypeError('__call__() missing 1 required positional argument: \'message\'')
command = self._data
command['name_localized'] = command['name']
data = {
data: MessageCommandInteractionData = {
'application_command': command,
'attachments': [],
'id': str(self.id),
@ -511,6 +535,11 @@ class MessageCommand(BaseCommand):
}
return await super().__call__(data, None, channel)
@property
def type(self) -> Literal[ApplicationCommandType.message]:
""":class:`ApplicationCommandType`: The type of application command. This is always :attr:`ApplicationCommandType.message`."""
return ApplicationCommandType.message
@property
def target_message(self) -> Optional[Message]:
"""Optional[:class:`Message`]: The message this application command will be used on.
@ -565,8 +594,6 @@ class SlashCommand(BaseCommand, SlashMixin):
The command's name.
description: :class:`str`
The command's description, if any.
type: :class:`ApplicationCommandType`
The type of application command.
dm_permission: :class:`bool`
Whether the command is enabled in DMs.
nsfw: :class:`bool`
@ -587,7 +614,7 @@ class SlashCommand(BaseCommand, SlashMixin):
__slots__ = ('_parent', 'options', 'children')
def __init__(self, *, data: Dict[str, Any], **kwargs) -> None:
def __init__(self, *, data: ApplicationCommandPayload, **kwargs) -> None:
super().__init__(data=data, **kwargs)
self._parent = self
self._unwrap_options(data.get('options', []))
@ -629,6 +656,11 @@ class SlashCommand(BaseCommand, SlashMixin):
BASE += f' children={len(self.children)}'
return BASE + '>'
@property
def type(self) -> Literal[ApplicationCommandType.chat_input]:
""":class:`ApplicationCommandType`: The type of application command. This is always :attr:`ApplicationCommandType.chat_input`."""
return ApplicationCommandType.chat_input
def is_group(self) -> bool:
"""Query whether this command is a group.
@ -663,8 +695,6 @@ class SubCommand(SlashMixin):
The subcommand's name.
description: :class:`str`
The subcommand's description, if any.
type: :class:`ApplicationCommandType`
The type of application command. Always :attr:`ApplicationCommandType.chat_input`.
parent: Union[:class:`SlashCommand`, :class:`SubCommand`]
The parent command.
options: List[:class:`Option`]
@ -680,18 +710,20 @@ class SubCommand(SlashMixin):
'parent',
'options',
'children',
'type',
)
def __init__(self, *, parent, data):
def __init__(self, *, parent: Union[SlashCommand, SubCommand], data: SubCommandPayload):
self.name = data['name']
self.description = data.get('description')
self._state = parent._state
self.parent: Union[SlashCommand, SubCommand] = parent
self.parent = parent
self._parent: SlashCommand = getattr(parent, 'parent', parent) # type: ignore
self.type = ApplicationCommandType.chat_input # Avoid confusion I guess
self._type: ApplicationCommandOptionType = try_enum(ApplicationCommandOptionType, data['type'])
self._unwrap_options(data.get('options', []))
self._type: Literal[
ApplicationCommandOptionType.sub_command, ApplicationCommandOptionType.sub_command_group
] = try_enum(
ApplicationCommandOptionType, data['type']
) # type: ignore
self._unwrap_options(data.get('options', [])) # type: ignore # ???
def __str__(self) -> str:
return self.name
@ -733,8 +765,7 @@ class SubCommand(SlashMixin):
raise TypeError('Cannot use a group')
options, files, attachments = self._parse_kwargs(kwargs)
options = [
options: List[InteractionDataOption] = [
{
'type': self._type.value,
'name': self.name,
@ -742,7 +773,7 @@ class SubCommand(SlashMixin):
}
]
for parent in self._walk_parents():
options = [
options: List[InteractionDataOption] = [
{
'type': parent._type.value,
'name': parent.name,
@ -760,6 +791,12 @@ class SubCommand(SlashMixin):
BASE += f' children={len(self.children)}'
return BASE + '>'
@property
def type(self) -> Literal[ApplicationCommandType.chat_input]:
""":class:`ApplicationCommandType`: The type of application command. Always :attr:`ApplicationCommandType.chat_input`."""
# Avoid confusion I guess
return ApplicationCommandType.chat_input
@property
def qualified_name(self) -> str:
""":class:`str`: Returns the fully qualified command name.
@ -889,13 +926,13 @@ class Option:
'autocomplete',
)
def __init__(self, data):
def __init__(self, data: OptionPayload):
self.name: str = data['name']
self.description: str = data['description']
self.type: ApplicationCommandOptionType = try_enum(ApplicationCommandOptionType, data['type'])
self.required: bool = data.get('required', False)
self.min_value: Optional[Union[int, float]] = data.get('min_value')
self.max_value: Optional[int] = data.get('max_value')
self.max_value: Optional[Union[int, float]] = data.get('max_value')
self.choices = [OptionChoice(choice, self.type) for choice in data.get('choices', [])]
self.channel_types: List[ChannelType] = [try_enum(ChannelType, c) for c in data.get('channel_types', [])]
self.autocomplete: bool = data.get('autocomplete', False)
@ -934,7 +971,7 @@ class OptionChoice:
__slots__ = ('name', 'value')
def __init__(self, data: Dict[str, str], type: ApplicationCommandOptionType):
def __init__(self, data: OptionChoicePayload, type: ApplicationCommandOptionType):
self.name: str = data['name']
self.value: Union[str, int, float]
if type is ApplicationCommandOptionType.string:

81
discord/components.py

@ -24,30 +24,40 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import ClassVar, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload
from typing import TYPE_CHECKING, ClassVar, List, Literal, Optional, Tuple, Union, overload
from .enums import try_enum, ComponentType, ButtonStyle, TextStyle, InteractionType
from .enums import ButtonStyle, ComponentType, InteractionType, TextStyle, try_enum
from .interactions import _wrapped_interaction
from .utils import _generate_nonce, get_slots, MISSING
from .partial_emoji import PartialEmoji, _EmojiTag
from .utils import MISSING, _generate_nonce, get_slots
if TYPE_CHECKING:
from typing_extensions import Self
from .emoji import Emoji
from .interactions import Interaction
from .message import Message
from .types.components import (
ActionRow as ActionRowPayload,
Component as ComponentPayload,
ActionRowChildComponent,
ButtonComponent as ButtonComponentPayload,
Component as ComponentPayload,
MessageChildComponent,
ModalChildComponent,
SelectMenu as SelectMenuPayload,
SelectOption as SelectOptionPayload,
TextInput as TextInputPayload,
ActionRowChildComponent as ActionRowChildComponentPayload,
)
from .emoji import Emoji
from .interactions import Interaction
from .message import Message
from .types.interactions import (
ActionRowInteractionData,
ButtonInteractionData,
ComponentInteractionData,
SelectInteractionData,
TextInputInteractionData,
)
ActionRowChildComponentType = Union['Button', 'SelectMenu', 'TextInput']
MessageChildComponentType = Union['Button', 'SelectMenu']
ActionRowChildComponentType = Union[MessageChildComponentType, 'TextInput']
__all__ = (
@ -99,7 +109,7 @@ class Component:
setattr(self, slot, value)
return self
def to_dict(self) -> ComponentPayload:
def to_dict(self) -> Union[ActionRowInteractionData, ComponentInteractionData]:
raise NotImplementedError
@ -124,13 +134,12 @@ class ActionRow(Component):
__repr_info__: ClassVar[Tuple[str, ...]] = __slots__
def __init__(self, data: ComponentPayload, message: Message):
def __init__(self, data: ActionRowPayload, message: Message):
self.message = message
self.children: List[ActionRowChildComponentType] = []
for component_data in data.get('components', []):
component = _component_factory(component_data, message)
if component is not None:
self.children.append(component)
@ -139,12 +148,11 @@ class ActionRow(Component):
""":class:`ComponentType`: The type of component."""
return ComponentType.action_row
def to_dict(self) -> ActionRowPayload:
# NOTE: This will have to be changed for the inevitable selects in modals
def to_dict(self) -> ActionRowInteractionData:
return {
'type': self.type.value,
'components': [c.to_dict() for c in self.children], # type: ignore
}
'type': ComponentType.action_row.value,
'components': [c.to_dict() for c in self.children],
} # type: ignore
class Button(Component):
@ -202,10 +210,10 @@ class Button(Component):
""":class:`ComponentType`: The type of component."""
return ComponentType.button
def to_dict(self) -> dict:
def to_dict(self) -> ButtonInteractionData:
return {
'component_type': self.type.value,
'custom_id': self.custom_id,
'custom_id': self.custom_id or '',
}
async def click(self) -> Union[str, Interaction]:
@ -237,7 +245,7 @@ class Button(Component):
_generate_nonce(),
InteractionType.component,
None,
message.channel, # type: ignore # acc_channel is always correct here
message.channel, # type: ignore # channel is always correct here
self.to_dict(),
message=message,
)
@ -296,7 +304,7 @@ class SelectMenu(Component):
""":class:`ComponentType`: The type of component."""
return ComponentType.select
def to_dict(self, options: Optional[Tuple[SelectOption]] = None) -> dict:
def to_dict(self, options: Optional[Tuple[SelectOption]] = None) -> SelectInteractionData:
return {
'component_type': self.type.value,
'custom_id': self.custom_id,
@ -524,31 +532,40 @@ class TextInput(Component):
"""
self.value = value
def to_dict(self) -> dict:
def to_dict(self) -> TextInputInteractionData:
return {
'type': self.type.value,
'custom_id': self.custom_id,
'value': self.value,
'value': self.value or '',
}
@overload
def _component_factory(
data: ActionRowChildComponentPayload, message: Message = ...
) -> Optional[ActionRowChildComponentType]:
def _component_factory(data: ActionRowPayload, message: Message = ...) -> ActionRow:
...
@overload
def _component_factory(data: MessageChildComponent, message: Message = ...) -> Optional[MessageChildComponentType]:
...
@overload
def _component_factory(data: ModalChildComponent, message: Message = ...) -> Optional[TextInput]:
...
@overload
def _component_factory(data: ActionRowChildComponent, message: Message = ...) -> Optional[ActionRowChildComponentType]:
...
@overload
def _component_factory(
data: ComponentPayload, message: Message = ...
) -> Optional[Union[ActionRow, ActionRowChildComponentType]]:
def _component_factory(data: ComponentPayload, message: Message = ...) -> Optional[Component]:
...
def _component_factory(
data: ComponentPayload, message: Message = MISSING
) -> Optional[Union[ActionRow, ActionRowChildComponentType]]:
def _component_factory(data: ComponentPayload, message: Message = MISSING) -> Optional[Component]:
if data['type'] == 1:
return ActionRow(data, message)
elif data['type'] == 2:

19
discord/file.py

@ -24,12 +24,13 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from base64 import b64encode
from hashlib import md5
import io
import os
from base64 import b64encode
from hashlib import md5
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union
import yarl
from typing import Any, Dict, Optional, Tuple, Union, TYPE_CHECKING
from .utils import MISSING, cached_slot_property
@ -37,7 +38,11 @@ if TYPE_CHECKING:
from typing_extensions import Self
from .state import ConnectionState
from .types.message import CloudAttachment as CloudAttachmentPayload, UploadedAttachment as UploadedAttachmentPayload
from .types.message import (
CloudAttachment as CloudAttachmentPayload,
PartialAttachment as PartialAttachmentPayload,
UploadedAttachment as UploadedAttachmentPayload,
)
__all__ = (
'File',
@ -76,8 +81,8 @@ class _FileBase:
def filename(self, value: str) -> None:
self._filename, self.spoiler = _strip_spoiler(value)
def to_dict(self, index: int) -> Dict[str, Any]:
payload = {
def to_dict(self, index: int) -> PartialAttachmentPayload:
payload: PartialAttachmentPayload = {
'id': str(index),
'filename': self.filename,
}
@ -264,7 +269,7 @@ class CloudFile(_FileBase):
url = yarl.URL(self.url)
return url.query['upload_id']
def to_dict(self, index: int) -> Dict[str, Any]:
def to_dict(self, index: int) -> PartialAttachmentPayload:
payload = super().to_dict(index)
payload['uploaded_filename'] = self.upload_filename
return payload

58
discord/http.py

@ -96,6 +96,7 @@ if TYPE_CHECKING:
guild,
hub,
integration,
interactions,
invite,
library,
member,
@ -1179,7 +1180,6 @@ class HTTPClient:
flags: Optional[int] = None,
last_viewed: Optional[int] = None,
) -> None:
r = Route('POST', '/channels/{channel_id}/messages/{message_id}/ack', channel_id=channel_id, message_id=message_id)
payload = {}
if manual:
payload['manual'] = True
@ -1192,7 +1192,10 @@ class HTTPClient:
if last_viewed is not None:
payload['last_viewed'] = last_viewed
data: read_state.AcknowledgementToken = await self.request(r, json=payload)
data: read_state.AcknowledgementToken = await self.request(
Route('POST', '/channels/{channel_id}/messages/{message_id}/ack', channel_id=channel_id, message_id=message_id),
json=payload,
)
self.ack_token = data.get('token') if data else None
def ack_guild_feature(
@ -1699,8 +1702,8 @@ class HTTPClient:
params: MultipartParameters,
reason: Optional[str] = None,
) -> Response[threads.ForumThread]:
query = {'use_nested_fields': 1}
r = Route('POST', '/channels/{channel_id}/threads', channel_id=channel_id)
query = {'use_nested_fields': 'true'}
if params.files:
return self.request(r, files=params.files, form=params.multipart, params=query, reason=reason)
else:
@ -1925,8 +1928,8 @@ class HTTPClient:
payload = {'level': mfa_level}
return self.request(Route('POST', '/guilds/{guild_id}/mfa', guild_id=guild_id), json=payload, reason=reason)
def edit_guild_settings(self, guild_id: Snowflake, fields): # TODO: type
return self.request(Route('PATCH', '/users/@me/guilds/{guild_id}/settings', guild_id=guild_id), json=fields)
def edit_guild_settings(self, guild_id: Snowflake, payload: Dict[str, Any]) -> Response[user.UserGuildSettings]:
return self.request(Route('PATCH', '/users/@me/guilds/{guild_id}/settings', guild_id=guild_id), json=payload)
def get_template(self, code: str) -> Response[template.Template]:
return self.request(Route('GET', '/guilds/templates/{code}', code=code))
@ -2041,15 +2044,16 @@ class HTTPClient:
return self.request(Route('GET', '/sticker-packs'), params=params)
def get_sticker_pack(self, pack_id: Snowflake):
def get_sticker_pack(self, pack_id: Snowflake) -> Response[sticker.StickerPack]:
return self.request(Route('GET', '/sticker-packs/{pack_id}', pack_id=pack_id))
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]:
r = Route('GET', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id)
return self.request(r)
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: Dict[str, Any], file: File, reason: Optional[str]
@ -2285,7 +2289,7 @@ class HTTPClient:
channel_id: Snowflake = MISSING,
channel_type: ChannelType = MISSING,
message: Optional[Message] = None,
) -> Response[invite.PartialInvite]:
) -> Response[invite.AcceptedInvite]:
if message: # Invite Button Embed
props = ContextProperties.from_invite_button_embed(
guild_id=getattr(message.guild, 'id', None),
@ -2523,7 +2527,7 @@ class HTTPClient:
) -> Response[member.MemberWithUser]:
return self.edit_member(guild_id, user_id, channel_id=channel_id, reason=reason)
def get_ringability(self, channel_id: Snowflake):
def get_ringability(self, channel_id: Snowflake) -> Response[channel.CallEligibility]:
return self.request(Route('GET', '/channels/{channel_id}/call', channel_id=channel_id))
def ring(self, channel_id: Snowflake, *recipients: Snowflake) -> Response[None]:
@ -2534,7 +2538,7 @@ class HTTPClient:
payload = {'recipients': recipients}
return self.request(Route('POST', '/channels/{channel_id}/call/stop-ringing', channel_id=channel_id), json=payload)
def change_call_voice_region(self, channel_id: int, voice_region: str): # TODO: return type
def change_call_voice_region(self, channel_id: int, voice_region: str) -> Response[None]:
payload = {'region': voice_region}
return self.request(Route('PATCH', '/channels/{channel_id}/call', channel_id=channel_id), json=payload)
@ -2864,7 +2868,9 @@ class HTTPClient:
else:
props = ContextProperties.empty()
return self.request(Route('PUT', '/users/@me/relationships/{user_id}', user_id=user_id), context_properties=props, json=payload)
return self.request(
Route('PUT', '/users/@me/relationships/{user_id}', user_id=user_id), context_properties=props, json=payload
)
def send_friend_request(self, username: str, discriminator: Snowflake) -> Response[None]:
payload = {'username': username, 'discriminator': int(discriminator) or None}
@ -3165,7 +3171,7 @@ class HTTPClient:
super_properties_to_track=True,
)
def delete_app_whitelist(self, app_id: Snowflake, user_id: Snowflake):
def delete_app_whitelist(self, app_id: Snowflake, user_id: Snowflake) -> Response[None]:
return self.request(
Route('DELETE', '/oauth2/applications/{app_id}/allowlist/{user_id}', app_id=app_id, user_id=user_id),
super_properties_to_track=True,
@ -3223,7 +3229,7 @@ class HTTPClient:
super_properties_to_track=True,
)
def create_team(self, name: str):
def create_team(self, name: str) -> Response[team.Team]:
payload = {'name': name}
return self.request(Route('POST', '/teams'), json=payload, super_properties_to_track=True)
@ -3251,13 +3257,15 @@ class HTTPClient:
def get_team_members(self, team_id: Snowflake) -> Response[List[team.TeamMember]]:
return self.request(Route('GET', '/teams/{team_id}/members', team_id=team_id), super_properties_to_track=True)
def invite_team_member(self, team_id: Snowflake, username: str, discriminator: Snowflake):
def invite_team_member(
self, team_id: Snowflake, username: str, discriminator: Optional[Snowflake] = None
) -> Response[team.TeamMember]:
payload = {'username': username, 'discriminator': str(discriminator) or None}
return self.request(
Route('POST', '/teams/{team_id}/members', team_id=team_id), json=payload, super_properties_to_track=True
)
def remove_team_member(self, team_id: Snowflake, user_id: Snowflake):
def remove_team_member(self, team_id: Snowflake, user_id: Snowflake) -> Response[None]:
return self.request(
Route('DELETE', '/teams/{team_id}/members/{user_id}', team_id=team_id, user_id=user_id),
super_properties_to_track=True,
@ -4380,27 +4388,27 @@ class HTTPClient:
return self.request(Route('PATCH', '/users/@me/settings-proto/{type}', type=type), json=payload)
def get_settings(self): # TODO: return type
def get_settings(self):
return self.request(Route('GET', '/users/@me/settings'))
def edit_settings(self, **payload): # TODO: return type, is this cheating?
def edit_settings(self, **payload):
return self.request(Route('PATCH', '/users/@me/settings'), json=payload)
def get_tracking(self): # TODO: return type
def get_tracking(self) -> Response[user.ConsentSettings]:
return self.request(Route('GET', '/users/@me/consent'))
def edit_tracking(self, payload):
def edit_tracking(self, payload) -> Response[user.ConsentSettings]:
return self.request(Route('POST', '/users/@me/consent'), json=payload)
def get_email_settings(self):
def get_email_settings(self) -> Response[user.EmailSettings]:
return self.request(Route('GET', '/users/@me/email-settings'))
def edit_email_settings(self, **payload):
def edit_email_settings(self, **payload) -> Response[user.EmailSettings]:
return self.request(Route('PATCH', '/users/@me/email-settings'), json={'settings': payload})
def mobile_report( # Report v1
self, guild_id: Snowflake, channel_id: Snowflake, message_id: Snowflake, reason: str
): # TODO: return type
) -> Response[user.Report]:
payload = {'guild_id': guild_id, 'channel_id': channel_id, 'message_id': message_id, 'reason': reason}
return self.request(Route('POST', '/report'), json=payload)
@ -4418,7 +4426,7 @@ class HTTPClient:
command_ids: Optional[List[Snowflake]] = None,
application_id: Optional[Snowflake] = None,
include_applications: Optional[bool] = None,
):
) -> Response[command.ApplicationCommandSearch]:
params: Dict[str, Any] = {
'type': type,
}
@ -4442,7 +4450,7 @@ class HTTPClient:
def interact(
self,
type: InteractionType,
data: dict,
data: interactions.InteractionData,
channel: MessageableChannel,
message: Optional[Message] = None,
*,

3
discord/interactions.py

@ -39,6 +39,7 @@ if TYPE_CHECKING:
from .modal import Modal
from .state import ConnectionState
from .threads import Thread
from .types.interactions import InteractionData
from .types.snowflake import Snowflake
from .types.user import User as UserPayload
from .user import BaseUser, ClientUser
@ -193,7 +194,7 @@ async def _wrapped_interaction(
type: InteractionType,
name: Optional[str],
channel: MessageableChannel,
data: dict,
data: InteractionData,
**kwargs,
) -> Interaction:
state._interaction_cache[nonce] = (type.value, name, channel)

6
discord/member.py

@ -60,7 +60,7 @@ if TYPE_CHECKING:
from .guild import Guild
from .profile import MemberProfile
from .types.activity import (
PartialPresenceUpdate,
BasePresenceUpdate,
)
from .types.member import (
MemberWithUser as MemberWithUserPayload,
@ -389,7 +389,7 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
self._user = member._user
return self
def _update(self, data: GuildMemberUpdateEvent) -> Optional[Member]:
def _update(self, data: Union[GuildMemberUpdateEvent, MemberWithUserPayload]) -> Optional[Member]:
old = Member._copy(self)
# Some changes are optional
@ -416,7 +416,7 @@ class Member(discord.abc.Messageable, discord.abc.Connectable, _UserTag):
return old
def _presence_update(
self, data: PartialPresenceUpdate, user: Union[PartialUserPayload, Tuple[()]]
self, data: BasePresenceUpdate, user: Union[PartialUserPayload, Tuple[()]]
) -> Optional[Tuple[User, User]]:
self._presence = self._state.create_presence(data)
return self._user._update_self(user)

9
discord/message.py

@ -87,7 +87,7 @@ if TYPE_CHECKING:
from .types.interactions import MessageInteraction as MessageInteractionPayload
from .types.components import Component as ComponentPayload
from .types.components import MessageActionRow as ComponentPayload
from .types.threads import ThreadArchiveDuration
from .types.member import (
Member as MemberPayload,
@ -98,7 +98,7 @@ if TYPE_CHECKING:
from .types.gateway import MessageReactionRemoveEvent, MessageUpdateEvent
from .abc import Snowflake
from .abc import GuildChannel, MessageableChannel
from .components import ActionRow, ActionRowChildComponentType
from .components import ActionRow
from .file import _FileBase
from .state import ConnectionState
from .mentions import AllowedMentions
@ -107,7 +107,6 @@ if TYPE_CHECKING:
from .role import Role
EmojiInputType = Union[Emoji, PartialEmoji, str]
MessageComponentType = Union[ActionRow, ActionRowChildComponentType]
__all__ = (
@ -1573,7 +1572,7 @@ class Message(PartialMessage, Hashable):
mentions: List[Union[User, Member]]
author: Union[User, Member]
role_mentions: List[Role]
components: List[MessageComponentType]
components: List[ActionRow]
def __init__(
self,
@ -1865,10 +1864,8 @@ class Message(PartialMessage, Hashable):
def _handle_components(self, data: List[ComponentPayload]) -> None:
self.components = []
for component_data in data:
component = _component_factory(component_data, self)
if component is not None:
self.components.append(component)

11
discord/modal.py

@ -35,6 +35,7 @@ if TYPE_CHECKING:
from .application import IntegrationApplication
from .components import ActionRow
from .interactions import Interaction
from .types.interactions import Modal as ModalPayload, ModalSubmitInteractionData
# fmt: off
__all__ = (
@ -69,7 +70,7 @@ class Modal(Hashable):
Attributes
-----------
id: :class:`int`
The modal's ID. This is the same as the interaction ID.
The interaction ID.
nonce: Optional[Union[:class:`int`, :class:`str`]]
The modal's nonce. May not be present.
title: :class:`str`
@ -84,24 +85,24 @@ class Modal(Hashable):
__slots__ = ('_state', 'interaction', 'id', 'nonce', 'title', 'custom_id', 'components', 'application')
def __init__(self, *, data: dict, interaction: Interaction):
def __init__(self, *, data: ModalPayload, interaction: Interaction):
self._state = interaction._state
self.interaction = interaction
self.id = int(data['id'])
self.nonce: Optional[Union[int, str]] = data.get('nonce')
self.title: str = data.get('title', '')
self.custom_id: str = data.get('custom_id', '')
self.components: List[ActionRow] = [_component_factory(d) for d in data.get('components', [])] # type: ignore # Will always be rows here
self.components: List[ActionRow] = [_component_factory(d) for d in data.get('components', [])]
self.application: IntegrationApplication = interaction._state.create_integration_application(data['application'])
def __str__(self) -> str:
return self.title
def to_dict(self) -> dict:
def to_dict(self) -> ModalSubmitInteractionData:
return {
'id': str(self.id),
'custom_id': self.custom_id,
'components': [c.to_dict() for c in self.components],
'components': [c.to_dict() for c in self.components], # type: ignore
}
async def submit(self):

81
discord/settings.py

@ -25,13 +25,13 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
import base64
from datetime import datetime, timezone
import struct
import logging
import struct
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, Collection, Dict, List, Literal, Optional, Sequence, Tuple, Type, Union, overload
from google.protobuf.json_format import MessageToDict, ParseDict
from discord_protos import PreloadedUserSettings # , FrecencyUserSettings
from google.protobuf.json_format import MessageToDict, ParseDict
from .activity import CustomActivity
from .colour import Colour
@ -41,8 +41,8 @@ from .enums import (
InboxTab,
Locale,
NotificationLevel,
Status,
SpoilerRenderOptions,
Status,
StickerAnimationOptions,
StickerPickerSection,
Theme,
@ -51,7 +51,7 @@ from .enums import (
)
from .flags import FriendDiscoveryFlags, FriendSourceFlags, HubProgressFlags, OnboardingProgressFlags
from .object import Object
from .utils import MISSING, _get_as_snowflake, _ocast, parse_time, parse_timestamp, utcnow, find
from .utils import MISSING, _get_as_snowflake, _ocast, find, parse_time, parse_timestamp, utcnow
if TYPE_CHECKING:
from google.protobuf.message import Message
@ -61,6 +61,14 @@ if TYPE_CHECKING:
from .channel import DMChannel, GroupChannel
from .guild import Guild
from .state import ConnectionState
from .types.user import (
ConsentSettings as ConsentSettingsPayload,
EmailSettings as EmailSettingsPayload,
PartialConsentSettings as PartialConsentSettingsPayload,
UserGuildSettings as UserGuildSettingsPayload,
ChannelOverride as ChannelOverridePayload,
MuteConfig as MuteConfigPayload,
)
from .user import ClientUser, User
PrivateChannel = Union[DMChannel, GroupChannel]
@ -1954,8 +1962,8 @@ class MuteConfig:
When the mute will expire.
"""
def __init__(self, muted: bool, config: Dict[str, str]) -> None:
until = parse_time(config.get('end_time'))
def __init__(self, muted: bool, config: Optional[MuteConfigPayload] = None) -> None:
until = parse_time(config.get('end_time') if config else None)
if until is not None:
if until <= utcnow():
muted = False
@ -2002,7 +2010,7 @@ class ChannelSettings:
muted: MuteConfig
collapsed: bool
def __init__(self, guild_id: Optional[int] = None, *, data: Dict[str, Any], state: ConnectionState) -> None:
def __init__(self, guild_id: Optional[int] = None, *, data: ChannelOverridePayload, state: ConnectionState) -> None:
self._guild_id = guild_id
self._state = state
self._update(data)
@ -2010,14 +2018,14 @@ class ChannelSettings:
def __repr__(self) -> str:
return f'<ChannelSettings channel={self.channel} level={self.level} muted={self.muted} collapsed={self.collapsed}>'
def _update(self, data: Dict[str, Any]) -> None:
def _update(self, data: ChannelOverridePayload) -> None:
# We consider everything optional because this class can be constructed with no data
# to represent the default settings
self._channel_id = int(data['channel_id'])
self.collapsed = data.get('collapsed', False)
self.level = try_enum(NotificationLevel, data.get('message_notifications', 3))
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config') or {})
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config'))
@property
def channel(self) -> Union[GuildChannel, PrivateChannel]:
@ -2103,7 +2111,7 @@ class ChannelSettings:
override = find(lambda x: x.get('channel_id') == str(channel_id), data['channel_overrides']) or {
'channel_id': channel_id
}
return ChannelSettings(guild_id, data=override, state=state)
return ChannelSettings(guild_id, data=override, state=state) # type: ignore
class GuildSettings:
@ -2146,14 +2154,15 @@ class GuildSettings:
notify_highlights: HighlightLevel
version: int
def __init__(self, *, data: Dict[str, Any], state: ConnectionState) -> None:
def __init__(self, *, data: UserGuildSettingsPayload, state: ConnectionState) -> None:
self._state = state
self.version = -1 # Overriden by real data
self._update(data)
def __repr__(self) -> str:
return f'<GuildSettings guild={self.guild!r} level={self.level} muted={self.muted} suppress_everyone={self.suppress_everyone} suppress_roles={self.suppress_roles}>'
def _update(self, data: Dict[str, Any]) -> None:
def _update(self, data: UserGuildSettingsPayload) -> None:
# We consider everything optional because this class can be constructed with no data
# to represent the default settings
self._guild_id = guild_id = _get_as_snowflake(data, 'guild_id')
@ -2164,9 +2173,9 @@ class GuildSettings:
self.mobile_push = data.get('mobile_push', True)
self.mute_scheduled_events = data.get('mute_scheduled_events', False)
self.notify_highlights = try_enum(HighlightLevel, data.get('notify_highlights', 0))
self.version = data.get('version', -1) # Overriden by real data
self.version = data.get('version', self.version)
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config') or {})
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config'))
self._channel_overrides = overrides = {}
state = self._state
for override in data.get('channel_overrides', []):
@ -2244,9 +2253,7 @@ class GuildSettings:
payload['muted'] = False
else:
payload['muted'] = True
if muted_until is True:
payload['mute_config'] = {'selected_time_window': -1, 'end_time': None}
else:
if muted_until is not True:
if muted_until.tzinfo is None:
raise TypeError(
'muted_until must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
@ -2257,25 +2264,18 @@ class GuildSettings:
'end_time': muted_until.isoformat(),
}
payload['mute_config'] = mute_config
if level is not MISSING:
payload['message_notifications'] = level.value
if suppress_everyone is not MISSING:
payload['suppress_everyone'] = suppress_everyone
if suppress_roles is not MISSING:
payload['suppress_roles'] = suppress_roles
if mobile_push is not MISSING:
payload['mobile_push'] = mobile_push
if hide_muted_channels is not MISSING:
payload['hide_muted_channels'] = hide_muted_channels
if mute_scheduled_events is not MISSING:
payload['mute_scheduled_events'] = mute_scheduled_events
if notify_highlights is not MISSING:
payload['notify_highlights'] = notify_highlights.value
@ -2304,7 +2304,9 @@ class TrackingSettings:
__slots__ = ('_state', 'personalization', 'usage_statistics')
def __init__(self, *, data: Dict[str, Dict[str, bool]], state: ConnectionState) -> None:
def __init__(
self, *, data: Union[PartialConsentSettingsPayload, ConsentSettingsPayload], state: ConnectionState
) -> None:
self._state = state
self._update(data)
@ -2312,9 +2314,9 @@ class TrackingSettings:
return f'<TrackingSettings personalization={self.personalization} usage_statistics={self.usage_statistics}>'
def __bool__(self) -> bool:
return any({self.personalization, self.usage_statistics})
return any((self.personalization, self.usage_statistics))
def _update(self, data: Dict[str, Dict[str, bool]]):
def _update(self, data: Union[PartialConsentSettingsPayload, ConsentSettingsPayload]):
self.personalization = data.get('personalization', {}).get('consented', False)
self.usage_statistics = data.get('usage_statistics', {}).get('consented', False)
@ -2387,22 +2389,22 @@ class EmailSettings:
'family_center_digest',
)
def __init__(self, *, data: dict, state: ConnectionState):
def __init__(self, *, data: EmailSettingsPayload, state: ConnectionState):
self._state = state
self._update(data)
def __repr__(self) -> str:
return f'<EmailSettings initialized={self.initialized}>'
def _update(self, data: dict):
self.initialized = data.get('initialized', False)
def _update(self, data: EmailSettingsPayload):
self.initialized: bool = data.get('initialized', False)
categories = data.get('categories', {})
self.communication = categories.get('communication', False)
self.social = categories.get('social', False)
self.recommendations_and_events = categories.get('recommendations_and_events', False)
self.tips = categories.get('tips', False)
self.updates_and_announcements = categories.get('updates_and_announcements', False)
self.family_center_digest = categories.get('family_center_digest', False)
self.communication: bool = categories.get('communication', False)
self.social: bool = categories.get('social', False)
self.recommendations_and_events: bool = categories.get('recommendations_and_events', False)
self.tips: bool = categories.get('tips', False)
self.updates_and_announcements: bool = categories.get('updates_and_announcements', False)
self.family_center_digest: bool = categories.get('family_center_digest', False)
@overload
async def edit(self) -> None:
@ -2412,6 +2414,7 @@ class EmailSettings:
async def edit(
self,
*,
initialized: bool = MISSING,
communication: bool = MISSING,
social: bool = MISSING,
recommendations_and_events: bool = MISSING,
@ -2449,8 +2452,8 @@ class EmailSettings:
# It seems that initialized is settable, but it doesn't do anything
# So we support just in case but leave it undocumented
initialized = kwargs.pop('initialized', None)
if initialized is not None:
initialized = kwargs.pop('initialized', MISSING)
if initialized is not MISSING:
payload['initialized'] = initialized
if kwargs:
payload['categories'] = kwargs

90
discord/state.py

@ -128,7 +128,7 @@ if TYPE_CHECKING:
PartialMessage as PartialMessagePayload,
)
from .types import gateway as gw
from .types.voice import GuildVoiceState
from .types.voice import VoiceState as VoiceStatePayload
from .types.activity import ClientStatus as ClientStatusPayload
T = TypeVar('T')
@ -496,7 +496,7 @@ class ClientStatus:
class Presence:
__slots__ = ('client_status', 'activities', 'last_modified')
def __init__(self, data: gw.PresenceUpdateEvent, state: ConnectionState, /) -> None:
def __init__(self, data: gw.BasePresenceUpdate, state: ConnectionState, /) -> None:
self.client_status: ClientStatus = ClientStatus(data['status'], data.get('client_status'))
self.activities: Tuple[ActivityTypes, ...] = tuple(create_activity(d, state) for d in data['activities'])
self.last_modified: Optional[datetime.datetime] = utils.parse_timestamp(data.get('last_modified'))
@ -520,7 +520,7 @@ class Presence:
return True
return self.client_status != other.client_status or self.activities != other.activities
def _update(self, data: gw.PresenceUpdateEvent, state: ConnectionState, /) -> None:
def _update(self, data: gw.BasePresenceUpdate, state: ConnectionState, /) -> None:
self.client_status._update(data['status'], data.get('client_status'))
self.activities = tuple(create_activity(d, state) for d in data['activities'])
self.last_modified = utils.parse_timestamp(data.get('last_modified')) or utils.utcnow()
@ -653,7 +653,6 @@ class ConnectionState:
self.user: Optional[ClientUser] = None
self._users: weakref.WeakValueDictionary[int, User] = weakref.WeakValueDictionary()
self.settings: Optional[UserSettings] = None
self.guild_settings: Dict[Optional[int], GuildSettings] = {}
self.consents: Optional[TrackingSettings] = None
self.connections: Dict[str, Connection] = {}
self.pending_payments: Dict[int, Payment] = {}
@ -674,6 +673,9 @@ class ConnectionState:
self._read_states: Dict[int, Dict[int, ReadState]] = {}
self.read_state_version: int = 0
self.guild_settings: Dict[Optional[int], GuildSettings] = {}
self.guild_settings_version: int = 0
self._calls: Dict[int, Call] = {}
self._call_message_cache: Dict[int, Message] = {} # Hopefully this won't be a memory leak
self._voice_clients: Dict[int, VoiceProtocol] = {}
@ -756,7 +758,7 @@ class ConnectionState:
return list(self._voice_clients.values())
def _update_voice_state(
self, data: GuildVoiceState, channel_id: Optional[int]
self, data: VoiceStatePayload, channel_id: Optional[int]
) -> Tuple[Optional[User], VoiceState, VoiceState]:
user_id = int(data['user_id'])
user = self.get_user(user_id)
@ -1079,6 +1081,14 @@ class ConnectionState:
self.store_read_state(item)
self.read_state_version = read_states.get('version', 0)
# Guild settings parsing
guild_settings = data.get('user_guild_settings', {})
self.guild_settings = {
utils._get_as_snowflake(entry, 'guild_id'): GuildSettings(data=entry, state=self)
for entry in guild_settings.get('entries', [])
}
self.guild_settings_version = guild_settings.get('version', 0)
# Experiments
self.experiments = {exp[0]: UserExperiment(state=self, data=exp) for exp in data.get('experiments', [])}
self.guild_experiments = {exp[0]: GuildExperiment(state=self, data=exp) for exp in data.get('guild_experiments', [])}
@ -1087,10 +1097,6 @@ class ConnectionState:
self.analytics_token = data.get('analytics_token')
self.preferred_rtc_regions = data.get('geo_ordered_rtc_regions', ['us-central'])
self.settings = UserSettings(self, data.get('user_settings_proto', ''))
self.guild_settings = {
utils._get_as_snowflake(entry, 'guild_id'): GuildSettings(data=entry, state=self)
for entry in data.get('user_guild_settings', {}).get('entries', [])
}
self.consents = TrackingSettings(data=data.get('consents', {}), state=self)
self.country_code = data.get('country_code', 'US')
self.api_code_version = data.get('api_code_version', 1)
@ -1479,7 +1485,7 @@ class ConnectionState:
else:
_log.warning('Unknown user settings proto type: %s', type.value)
def parse_user_guild_settings_update(self, data) -> None:
def parse_user_guild_settings_update(self, data: gw.UserGuildSettingsEvent) -> None:
guild_id = utils._get_as_snowflake(data, 'guild_id')
settings = self.guild_settings.get(guild_id)
@ -1489,6 +1495,7 @@ class ConnectionState:
else:
old_settings = None
settings = GuildSettings(data=data, state=self)
self.guild_settings_version = data.get('version', self.guild_settings_version)
self.dispatch('guild_settings_update', old_settings, settings)
def parse_user_required_action_update(self, data: gw.RequiredActionEvent) -> None:
@ -1779,14 +1786,22 @@ class ConnectionState:
else:
self.dispatch('guild_channel_pins_ack', channel, last_pin)
def parse_channel_recipient_add(self, data) -> None:
def parse_channel_recipient_add(self, data: gw.ChannelRecipientEvent) -> None:
channel = self._get_private_channel(int(data['channel_id']))
if channel is None:
_log.debug('CHANNEL_RECIPIENT_ADD referencing an unknown channel ID: %s. Discarding.', data['channel_id'])
return
user = self.store_user(data['user'])
channel.recipients.append(user) # type: ignore
self.dispatch('group_join', channel, user)
def parse_channel_recipient_remove(self, data) -> None:
def parse_channel_recipient_remove(self, data: gw.ChannelRecipientEvent) -> None:
channel = self._get_private_channel(int(data['channel_id']))
if channel is None:
_log.debug('CHANNEL_RECIPIENT_REMOVE referencing an unknown channel ID: %s. Discarding.', data['channel_id'])
return
user = self.store_user(data['user'])
try:
channel.recipients.remove(user) # type: ignore
@ -2011,7 +2026,7 @@ class ConnectionState:
'I noticed you triggered a `GUILD_SYNC`.\nIf you want to share your secrets, please feel free to open an issue.'
)
def parse_guild_member_list_update(self, data) -> None:
def parse_guild_member_list_update(self, data: gw.GuildMemberListUpdateEvent) -> None:
self.dispatch('raw_member_list_update', data)
guild = self._get_guild(int(data['guild_id']))
if guild is None:
@ -2042,7 +2057,6 @@ class ConnectionState:
ops = data['ops']
for opdata in ops:
op = opdata['op']
# The OPs are as follows:
# SYNC: Provides member/presence data for a 100 member range of the member list
# UPDATE: Dispatched when a member is updated and stays in the same range
@ -2050,7 +2064,7 @@ class ConnectionState:
# DELETE: Dispatched when a member is removed from a range
# INVALIDATE: Sent when you're unsubscribed from a range
if op == 'SYNC':
if opdata['op'] == 'SYNC':
for item in opdata['items']:
if 'group' in item: # Hoisted role
guild._member_list.append(None) if should_parse else None # Insert blank so indexes don't fuck up
@ -2064,7 +2078,7 @@ class ConnectionState:
members.append(member)
guild._member_list.append(member) if should_parse else None
elif op == 'INSERT':
elif opdata['op'] == 'INSERT':
index = opdata['index']
item = opdata['item']
if 'group' in item: # Hoisted role
@ -2111,7 +2125,7 @@ class ConnectionState:
guild._member_list.insert(index, member) if should_parse else None
elif op == 'UPDATE' and should_parse:
elif opdata['op'] == 'UPDATE' and should_parse:
item = opdata['item']
if 'group' in item: # Hoisted role
continue
@ -2159,7 +2173,7 @@ class ConnectionState:
guild._member_list.insert(opdata['index'], member) # Race condition?
elif op == 'DELETE' and should_parse:
elif opdata['op'] == 'DELETE' and should_parse:
index = opdata['index']
try:
item = guild._member_list.pop(index)
@ -2213,7 +2227,6 @@ class ConnectionState:
before_emojis = guild.emojis
for emoji in before_emojis:
self._emojis.pop(emoji.id, None)
# guild won't be None here
guild.emojis = tuple(map(lambda d: self.store_emoji(guild, d), data['emojis']))
self.dispatch('guild_emojis_update', guild, before_emojis, guild.emojis)
@ -2226,7 +2239,6 @@ class ConnectionState:
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)
@ -2243,7 +2255,6 @@ class ConnectionState:
data=data,
guild=guild,
)
self.dispatch('audit_log_entry_create', entry)
def parse_auto_moderation_rule_create(self, data: AutoModerationRule) -> None:
@ -2753,17 +2764,26 @@ class ConnectionState:
else:
_log.debug('SCHEDULED_EVENT_USER_REMOVE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
def parse_call_create(self, data) -> None:
channel = self._get_private_channel(int(data['channel_id']))
def parse_call_create(self, data: gw.CallCreateEvent) -> None:
channel_id = int(data['channel_id'])
channel = self._get_private_channel(channel_id)
if channel is None:
_log.debug('CALL_CREATE referencing unknown channel ID: %s. Discarding.', data['channel_id'])
return
call = self._calls.get(channel_id)
if call is not None:
# Should only happen for unavailable calls
old_call = copy.copy(call)
call._update(data)
self.dispatch('call_update', old_call, call)
message = self._get_message(int(data['message_id']))
call = channel._add_call(data=data, state=self, message=message, channel=channel)
self._calls[channel.id] = call
self.dispatch('call_create', call)
def parse_call_update(self, data) -> None:
def parse_call_update(self, data: gw.CallUpdateEvent) -> None:
call = self._calls.get(int(data['channel_id']))
if call is None:
_log.debug('CALL_UPDATE referencing unknown call (channel ID: %s). Discarding.', data['channel_id'])
@ -2772,9 +2792,15 @@ class ConnectionState:
call._update(data)
self.dispatch('call_update', old_call, call)
def parse_call_delete(self, data) -> None:
def parse_call_delete(self, data: gw.CallDeleteEvent) -> None:
call = self._calls.pop(int(data['channel_id']), None)
if call is not None:
if data.get('unavailable'):
old_call = copy.copy(call)
call.unavailable = True
self.dispatch('call_update', old_call, call)
return
call._delete()
self._call_message_cache.pop(call._message_id, None)
self.dispatch('call_delete', call)
@ -2884,7 +2910,7 @@ class ConnectionState:
self.dispatch('friend_suggestion_remove', user)
self.dispatch('raw_friend_suggestion_remove', user_id)
def parse_interaction_create(self, data) -> None:
def parse_interaction_create(self, data: gw.InteractionEvent) -> None:
if 'nonce' not in data: # Sometimes interactions seem to be missing the nonce
return
@ -2893,7 +2919,7 @@ class ConnectionState:
self._interactions[i.id] = i
self.dispatch('interaction', i)
def parse_interaction_success(self, data) -> None:
def parse_interaction_success(self, data: gw.InteractionEvent) -> None:
id = int(data['id'])
i = self._interactions.get(id, None)
if i is None:
@ -2903,7 +2929,7 @@ class ConnectionState:
i.successful = True
self.dispatch('interaction_finish', i)
def parse_interaction_failed(self, data) -> None:
def parse_interaction_failed(self, data: gw.InteractionEvent) -> None:
id = int(data['id'])
i = self._interactions.pop(id, None)
if i is None:
@ -2913,7 +2939,7 @@ class ConnectionState:
i.successful = False
self.dispatch('interaction_finish', i)
def parse_interaction_modal_create(self, data) -> None:
def parse_interaction_modal_create(self, data: gw.InteractionModalCreateEvent) -> None:
id = int(data['id'])
interaction = self._interactions.pop(id, None)
if interaction is not None:
@ -3000,10 +3026,10 @@ class ConnectionState:
return IntegrationApplication(state=self, data=data)
def default_guild_settings(self, guild_id: Optional[int]) -> GuildSettings:
return GuildSettings(data={'guild_id': guild_id}, state=self)
return GuildSettings(data={'guild_id': guild_id}, state=self) # type: ignore
def default_channel_settings(self, guild_id: Optional[int], channel_id: int) -> ChannelSettings:
return ChannelSettings(guild_id, data={'channel_id': channel_id}, state=self)
return ChannelSettings(guild_id, data={'channel_id': channel_id}, state=self) # type: ignore
def create_implicit_relationship(self, user: User) -> Relationship:
relationship = self._relationships.get(user.id)
@ -3027,7 +3053,7 @@ class ConnectionState:
def client_presence(self) -> FakeClientPresence:
return FakeClientPresence(self)
def create_presence(self, data: gw.PresenceUpdateEvent) -> Presence:
def create_presence(self, data: gw.BasePresenceUpdate) -> Presence:
return Presence(data, self)
def create_offline_presence(self) -> Presence:

7
discord/types/activity.py

@ -33,14 +33,17 @@ from .snowflake import Snowflake
StatusType = Literal['idle', 'dnd', 'online', 'offline']
class PartialPresenceUpdate(TypedDict):
class BasePresenceUpdate(TypedDict):
user: PartialUser
guild_id: Optional[Snowflake]
status: StatusType
activities: List[Activity]
client_status: ClientStatus
class PartialPresenceUpdate(BasePresenceUpdate):
guild_id: NotRequired[Snowflake]
class ClientStatus(TypedDict, total=False):
desktop: StatusType
mobile: StatusType

4
discord/types/channel.py

@ -205,3 +205,7 @@ class InviteStageInstance(TypedDict):
participant_count: int
speaker_count: int
topic: str
class CallEligibility(TypedDict):
ringable: bool

40
discord/types/command.py

@ -24,9 +24,10 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import List, Literal, TypedDict, Union
from typing import List, Literal, Optional, TypedDict, Union
from typing_extensions import NotRequired, Required
from .application import IntegrationApplication
from .channel import ChannelType
from .snowflake import Snowflake
@ -109,6 +110,8 @@ class _NumberApplicationCommandOption(_BaseValueApplicationCommandOption, total=
autocomplete: bool
SubCommand = Union[_SubCommandCommandOption, _SubCommandGroupCommandOption]
_ValueApplicationCommandOption = Union[
_StringApplicationCommandOption,
_IntegerApplicationCommandOption,
@ -137,13 +140,10 @@ class _BaseApplicationCommand(TypedDict):
version: Snowflake
class _ChatInputApplicationCommand(_BaseApplicationCommand, total=False):
description: Required[str]
class _ChatInputApplicationCommand(_BaseApplicationCommand):
description: str
type: Literal[1]
options: Union[
List[_ValueApplicationCommandOption],
List[Union[_SubCommandCommandOption, _SubCommandGroupCommandOption]],
]
options: NotRequired[List[ApplicationCommandOption]]
class _BaseContextMenuApplicationCommand(_BaseApplicationCommand):
@ -177,13 +177,23 @@ class _GuildMessageApplicationCommand(_MessageApplicationCommand):
guild_id: Snowflake
ChatInputCommand = Union[
_ChatInputApplicationCommand,
_GuildChatInputApplicationCommand,
]
UserCommand = Union[
_UserApplicationCommand,
_GuildUserApplicationCommand,
]
MessageCommand = Union[
_MessageApplicationCommand,
_GuildMessageApplicationCommand,
]
GuildApplicationCommand = Union[
_GuildChatInputApplicationCommand,
_GuildUserApplicationCommand,
_GuildMessageApplicationCommand,
]
ApplicationCommand = Union[
GlobalApplicationCommand,
GuildApplicationCommand,
@ -204,3 +214,15 @@ class GuildApplicationCommandPermissions(TypedDict):
application_id: Snowflake
guild_id: Snowflake
permissions: List[ApplicationCommandPermissions]
class ApplicationCommandCursor(TypedDict):
next: Optional[str]
previous: Optional[str]
repaired: Optional[str]
class ApplicationCommandSearch(TypedDict):
application_commands: List[ApplicationCommand]
applications: Optional[List[IntegrationApplication]]
cursor: ApplicationCommandCursor

16
discord/types/components.py

@ -34,9 +34,17 @@ ButtonStyle = Literal[1, 2, 3, 4, 5]
TextStyle = Literal[1, 2]
class ActionRow(TypedDict):
class MessageActionRow(TypedDict):
type: Literal[1]
components: List[ActionRowChildComponent]
components: List[MessageChildComponent]
class ModalActionRow(TypedDict):
type: Literal[1]
components: List[ModalChildComponent]
ActionRow = Union[MessageActionRow, ModalActionRow]
class ButtonComponent(TypedDict):
@ -79,5 +87,7 @@ class TextInput(TypedDict):
max_length: NotRequired[int]
ActionRowChildComponent = Union[ButtonComponent, SelectMenu, TextInput]
MessageChildComponent = Union[ButtonComponent, SelectMenu]
ModalChildComponent = TextInput
ActionRowChildComponent = Union[MessageChildComponent, ModalChildComponent]
Component = Union[ActionRow, ActionRowChildComponent]

153
discord/types/gateway.py

@ -24,11 +24,10 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import List, Literal, Optional, TypedDict, Union
from typing import Generic, List, Literal, Optional, TypedDict, TypeVar, Union
from typing_extensions import NotRequired, Required
from .activity import Activity, ClientStatus, PartialPresenceUpdate, StatusType
from .activity import Activity, BasePresenceUpdate, PartialPresenceUpdate, StatusType
from .application import BaseAchievement
from .audit_log import AuditLogEntry
from .automod import AutoModerationAction, AutoModerationRuleTriggerType
@ -38,10 +37,10 @@ from .entitlements import Entitlement, GatewayGift
from .experiment import GuildExperiment, UserExperiment
from .guild import ApplicationCommandCounts, Guild, SupplementalGuild, UnavailableGuild
from .integration import BaseIntegration, IntegrationApplication
from .interactions import Interaction
from .interactions import Modal
from .invite import _InviteTargetType
from .library import LibraryApplication
from .member import MemberWithUser
from .member import MemberWithPresence, MemberWithUser
from .message import Message
from .payments import Payment
from .read_state import ReadState, ReadStateType
@ -51,15 +50,23 @@ from .snowflake import Snowflake
from .sticker import GuildSticker
from .subscriptions import PremiumGuildSubscriptionSlot
from .threads import Thread, ThreadMember
from .user import Connection, FriendSuggestion, PartialUser, ProtoSettingsType, Relationship, RelationshipType, User
from .voice import GuildVoiceState
class UserPresenceUpdateEvent(TypedDict):
user: PartialUser
status: StatusType
activities: List[Activity]
client_status: ClientStatus
from .user import (
Connection,
FriendSuggestion,
PartialConsentSettings,
PartialUser,
ProtoSettingsType,
Relationship,
RelationshipType,
User,
UserGuildSettings,
)
from .voice import GuildVoiceState, VoiceState
T = TypeVar('T')
class UserPresenceUpdateEvent(BasePresenceUpdate):
last_modified: int
@ -86,6 +93,7 @@ class ReadyEvent(ResumedEvent):
auth_session_id_hash: str
auth_token: NotRequired[str]
connected_accounts: List[Connection]
consents: PartialConsentSettings
country_code: str
experiments: List[UserExperiment]
friend_suggestion_count: int
@ -95,17 +103,17 @@ class ReadyEvent(ResumedEvent):
merged_members: List[List[MemberWithUser]]
pending_payments: NotRequired[List[Payment]]
private_channels: List[Union[DMChannel, GroupDMChannel]]
read_state: VersionedReadState
read_state: Versioned[ReadState]
relationships: List[Relationship]
resume_gateway_url: str
required_action: NotRequired[str]
sessions: List[Session]
session_id: str
session_type: str
session_type: Literal['normal']
shard: NotRequired[ShardInfo]
tutorial: Optional[Tutorial]
user: User
user_guild_settings: dict
user_guild_settings: Versioned[UserGuildSettings]
user_settings_proto: NotRequired[str]
users: List[PartialUser]
v: int
@ -138,8 +146,8 @@ class ReadySupplementalEvent(TypedDict):
disclose: List[str]
class VersionedReadState(TypedDict):
entries: List[ReadState]
class Versioned(TypedDict, Generic[T]):
entries: List[T]
version: int
partial: bool
@ -205,9 +213,6 @@ class MessageReactionRemoveEmojiEvent(TypedDict):
guild_id: NotRequired[Snowflake]
InteractionCreateEvent = Interaction
UserUpdateEvent = User
@ -240,6 +245,11 @@ class _ChannelEvent(TypedDict):
ChannelCreateEvent = ChannelUpdateEvent = ChannelDeleteEvent = _ChannelEvent
class ChannelRecipientEvent(TypedDict):
channel_id: Snowflake
user: PartialUser
class ChannelPinsUpdateEvent(TypedDict):
channel_id: Snowflake
guild_id: NotRequired[Snowflake]
@ -561,3 +571,102 @@ class GuildApplicationCommandIndexUpdateEvent(TypedDict):
class UserNoteUpdateEvent(TypedDict):
id: Snowflake
note: str
UserGuildSettingsEvent = UserGuildSettings
class InteractionEvent(TypedDict):
id: Snowflake
nonce: NotRequired[Snowflake]
InteractionModalCreateEvent = Modal
class CallCreateEvent(TypedDict):
channel_id: Snowflake
message_id: Snowflake
embedded_activities: List[dict]
region: str
ringing: List[Snowflake]
voice_states: List[VoiceState]
unavailable: NotRequired[bool]
class CallUpdateEvent(TypedDict):
channel_id: Snowflake
guild_id: Optional[Snowflake] # ???
message_id: Snowflake
region: str
ringing: List[Snowflake]
class CallDeleteEvent(TypedDict):
channel_id: Snowflake
unavailable: NotRequired[bool]
class _GuildMemberListGroup(TypedDict):
id: Union[Snowflake, Literal['online', 'offline']]
class GuildMemberListGroup(_GuildMemberListGroup):
count: int
class _GuildMemberListGroupItem(TypedDict):
group: _GuildMemberListGroup
class _GuildMemberListMemberItem(TypedDict):
member: MemberWithPresence
GuildMemberListItem = Union[_GuildMemberListGroupItem, _GuildMemberListMemberItem]
class GuildMemberListSyncOP(TypedDict):
op: Literal['SYNC']
range: tuple[int, int]
items: List[GuildMemberListItem]
class GuildMemberListUpdateOP(TypedDict):
op: Literal['UPDATE']
index: int
item: _GuildMemberListMemberItem
class GuildMemberListInsertOP(TypedDict):
op: Literal['INSERT']
index: int
item: _GuildMemberListMemberItem
class GuildMemberListDeleteOP(TypedDict):
op: Literal['DELETE']
index: int
class GuildMemberListInvalidateOP(TypedDict):
op: Literal['INVALIDATE']
range: tuple[int, int]
GuildMemberListOP = Union[
GuildMemberListSyncOP,
GuildMemberListUpdateOP,
GuildMemberListInsertOP,
GuildMemberListDeleteOP,
GuildMemberListInvalidateOP,
]
class GuildMemberListUpdateEvent(TypedDict):
id: Union[Snowflake, Literal['everyone']]
guild_id: Snowflake
member_count: int
online_count: int
groups: List[GuildMemberListGroup]
ops: List[GuildMemberListOP]

121
discord/types/interactions.py

@ -24,49 +24,20 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import TYPE_CHECKING, Dict, List, Literal, TypedDict, Union
from typing import List, Literal, TypedDict, Union
from typing_extensions import NotRequired
from .channel import ChannelTypeWithoutThread, ThreadMetadata
from .threads import ThreadType
from .application import IntegrationApplication
from .command import ApplicationCommand
from .components import ModalActionRow
from .member import Member
from .message import Attachment
from .role import Role
from .message import PartialAttachment
from .snowflake import Snowflake
from .user import User
if TYPE_CHECKING:
from .message import Message
InteractionType = Literal[1, 2, 3, 4, 5]
class _BasePartialChannel(TypedDict):
id: Snowflake
name: str
permissions: str
class PartialChannel(_BasePartialChannel):
type: ChannelTypeWithoutThread
class PartialThread(_BasePartialChannel):
type: ThreadType
thread_metadata: ThreadMetadata
parent_id: Snowflake
class ResolvedData(TypedDict, total=False):
users: Dict[str, User]
members: Dict[str, Member]
roles: Dict[str, Role]
channels: Dict[str, Union[PartialChannel, PartialThread]]
messages: Dict[str, Message]
attachments: Dict[str, Attachment]
class _BaseApplicationCommandInteractionDataOption(TypedDict):
name: str
@ -123,31 +94,33 @@ ApplicationCommandInteractionDataOption = Union[
class _BaseApplicationCommandInteractionData(TypedDict):
id: Snowflake
name: str
resolved: NotRequired[ResolvedData]
version: Snowflake
guild_id: NotRequired[Snowflake]
application_command: ApplicationCommand
attachments: List[PartialAttachment]
options: List[ApplicationCommandInteractionDataOption]
class ChatInputApplicationCommandInteractionData(_BaseApplicationCommandInteractionData, total=False):
class ChatInputCommandInteractionData(_BaseApplicationCommandInteractionData, total=False):
type: Literal[1]
options: List[ApplicationCommandInteractionDataOption]
class _BaseNonChatInputApplicationCommandInteractionData(_BaseApplicationCommandInteractionData):
target_id: Snowflake
class UserApplicationCommandInteractionData(_BaseNonChatInputApplicationCommandInteractionData):
class UserCommandInteractionData(_BaseNonChatInputApplicationCommandInteractionData):
type: Literal[2]
class MessageApplicationCommandInteractionData(_BaseNonChatInputApplicationCommandInteractionData):
class MessageCommandInteractionData(_BaseNonChatInputApplicationCommandInteractionData):
type: Literal[3]
ApplicationCommandInteractionData = Union[
ChatInputApplicationCommandInteractionData,
UserApplicationCommandInteractionData,
MessageApplicationCommandInteractionData,
ChatInputCommandInteractionData,
UserCommandInteractionData,
MessageCommandInteractionData,
]
@ -155,40 +128,45 @@ class _BaseMessageComponentInteractionData(TypedDict):
custom_id: str
class ButtonMessageComponentInteractionData(_BaseMessageComponentInteractionData):
class ButtonInteractionData(_BaseMessageComponentInteractionData):
component_type: Literal[2]
class SelectMessageComponentInteractionData(_BaseMessageComponentInteractionData):
class SelectInteractionData(_BaseMessageComponentInteractionData):
component_type: Literal[3]
values: List[str]
MessageComponentInteractionData = Union[ButtonMessageComponentInteractionData, SelectMessageComponentInteractionData]
MessageComponentInteractionData = Union[ButtonInteractionData, SelectInteractionData]
class ModalSubmitTextInputInteractionData(TypedDict):
class TextInputInteractionData(TypedDict):
type: Literal[4]
custom_id: str
value: str
ModalSubmitComponentItemInteractionData = ModalSubmitTextInputInteractionData
ModalSubmitComponentInteractionData = TextInputInteractionData
class ModalSubmitActionRowInteractionData(TypedDict):
class MessageActionRowData(TypedDict):
type: Literal[1]
components: List[ModalSubmitComponentItemInteractionData]
components: List[MessageComponentInteractionData]
ModalSubmitComponentInteractionData = Union[ModalSubmitActionRowInteractionData, ModalSubmitComponentItemInteractionData]
class ModalSubmitActionRowData(TypedDict):
type: Literal[1]
components: List[ModalSubmitComponentInteractionData]
class ModalSubmitInteractionData(TypedDict):
id: Snowflake
custom_id: str
components: List[ModalSubmitComponentInteractionData]
ActionRowInteractionData = Union[MessageActionRowData, ModalSubmitActionRowData]
ComponentInteractionData = Union[MessageComponentInteractionData, ModalSubmitComponentInteractionData]
InteractionData = Union[
ApplicationCommandInteractionData,
MessageComponentInteractionData,
@ -196,42 +174,19 @@ InteractionData = Union[
]
class _BaseInteraction(TypedDict):
id: Snowflake
application_id: Snowflake
token: str
version: Literal[1]
guild_id: NotRequired[Snowflake]
channel_id: NotRequired[Snowflake]
locale: NotRequired[str]
guild_locale: NotRequired[str]
class PingInteraction(_BaseInteraction):
type: Literal[1]
class ApplicationCommandInteraction(_BaseInteraction):
type: Literal[2, 4]
data: ApplicationCommandInteractionData
class MessageComponentInteraction(_BaseInteraction):
type: Literal[3]
data: MessageComponentInteractionData
class ModalSubmitInteraction(_BaseInteraction):
type: Literal[5]
data: ModalSubmitInteractionData
Interaction = Union[PingInteraction, ApplicationCommandInteraction, MessageComponentInteraction, ModalSubmitInteraction]
class MessageInteraction(TypedDict):
id: Snowflake
type: InteractionType
name: str
user: User
member: NotRequired[Member]
class Modal(TypedDict):
id: int
nonce: NotRequired[Snowflake]
channel_id: Snowflake
title: str
custom_id: str
application: IntegrationApplication
components: List[ModalActionRow]

6
discord/types/invite.py

@ -78,8 +78,10 @@ class InviteWithMetadata(PartialInvite, _InviteMetadata):
...
Invite = Union[PartialInvite, InviteWithCounts, InviteWithMetadata]
class AcceptedInvite(InviteWithCounts):
new_member: bool
show_verification_form: bool
Invite = Union[PartialInvite, InviteWithCounts, InviteWithMetadata, AcceptedInvite]
GatewayInvite = Union[InviteCreateEvent, InviteDeleteEvent]

6
discord/types/member.py

@ -23,6 +23,8 @@ DEALINGS IN THE SOFTWARE.
"""
from typing import Optional, TypedDict
from .activity import BasePresenceUpdate
from .snowflake import SnowflakeList
from .user import PartialUser
@ -60,6 +62,10 @@ class MemberWithUser(_OptionalMemberWithUser):
user: PartialUser
class MemberWithPresence(MemberWithUser):
presence: BasePresenceUpdate
class PrivateMember(MemberWithUser):
bio: str
banner: Optional[str]

11
discord/types/message.py

@ -33,7 +33,7 @@ from .user import User
from .emoji import PartialEmoji
from .embed import Embed
from .channel import ChannelType
from .components import Component
from .components import MessageActionRow
from .interactions import MessageInteraction
from .application import BaseApplication
from .sticker import StickerItem
@ -134,7 +134,7 @@ class Message(PartialMessage):
sticker_items: NotRequired[List[StickerItem]]
referenced_message: NotRequired[Optional[Message]]
interaction: NotRequired[MessageInteraction]
components: NotRequired[List[Component]]
components: NotRequired[List[MessageActionRow]]
position: NotRequired[int]
call: NotRequired[Call]
role_subscription_data: NotRequired[RoleSubscriptionData]
@ -190,6 +190,13 @@ MessageSearchSortType = Literal['timestamp', 'relevance']
MessageSearchSortOrder = Literal['desc', 'asc']
class PartialAttachment(TypedDict):
id: NotRequired[Snowflake]
filename: str
description: NotRequired[str]
uploaded_filename: NotRequired[str]
class UploadedAttachment(TypedDict):
id: NotRequired[Snowflake]
filename: str

65
discord/types/user.py

@ -136,11 +136,70 @@ class Relationship(TypedDict):
since: NotRequired[str]
ProtoSettingsType = Literal[1, 2, 3]
class ProtoSettings(TypedDict):
settings: str
ProtoSettingsType = Literal[1, 2, 3]
class _ConsentSettings(TypedDict):
consented: bool
class PartialConsentSettings(TypedDict):
personalization: _ConsentSettings
class ConsentSettings(PartialConsentSettings):
usage_statistics: _ConsentSettings
class _EmailSettingsCategories(TypedDict):
communication: bool
social: bool
recommendations_and_events: bool
tips: bool
updates_and_announcements: bool
family_center_digest: bool
class EmailSettings(TypedDict):
initialized: bool
categories: _EmailSettingsCategories
MessageNotificationLevel = Literal[0, 1, 2, 3]
HighlightLevel = Literal[0, 1, 2]
class MuteConfig(TypedDict):
end_time: Optional[str]
selected_time_window: Optional[int]
class ChannelOverride(TypedDict):
channel_id: Snowflake
collapsed: bool
message_notifications: MessageNotificationLevel
muted: bool
mute_config: Optional[MuteConfig]
class UserGuildSettings(TypedDict):
guild_id: Optional[Snowflake]
channel_overrides: List[ChannelOverride]
flags: int
message_notifications: MessageNotificationLevel
notify_highlights: HighlightLevel
hide_muted_channels: bool
mobile_push: bool
muted: bool
mute_config: Optional[MuteConfig]
mute_scheduled_events: bool
suppress_everyone: bool
suppress_roles: bool
version: int
class UserAffinity(TypedDict):
@ -177,3 +236,7 @@ class FriendSuggestionReason(TypedDict):
class FriendSuggestion(TypedDict):
suggested_user: PartialUser
reasons: List[FriendSuggestionReason]
class Report(TypedDict):
report_id: Snowflake

Loading…
Cancel
Save