diff options
| author | Nadir Chowdhury <[email protected]> | 2021-04-10 07:55:10 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-04-10 02:55:10 -0400 |
| commit | 1efdef3ac34ebed98d643a6a1c273f3b176e8837 (patch) | |
| tree | e835ade2564fe61dead5dee5ad67d67cca569c18 | |
| parent | Add typings for audit logs, integrations, and webhooks (diff) | |
| download | discord.py-1efdef3ac34ebed98d643a6a1c273f3b176e8837.tar.xz discord.py-1efdef3ac34ebed98d643a6a1c273f3b176e8837.zip | |
Add typings for invites, templates, and bans
| -rw-r--r-- | discord/client.py | 9 | ||||
| -rw-r--r-- | discord/guild.py | 14 | ||||
| -rw-r--r-- | discord/invite.py | 57 | ||||
| -rw-r--r-- | discord/template.py | 33 | ||||
| -rw-r--r-- | discord/types/guild.py | 6 | ||||
| -rw-r--r-- | discord/types/invite.py | 60 | ||||
| -rw-r--r-- | discord/types/template.py | 44 |
7 files changed, 185 insertions, 38 deletions
diff --git a/discord/client.py b/discord/client.py index 1e46be05..587137ef 100644 --- a/discord/client.py +++ b/discord/client.py @@ -27,6 +27,7 @@ import logging import signal import sys import traceback +from typing import Any, Optional, Union import aiohttp @@ -1070,7 +1071,7 @@ class Client: """ code = utils.resolve_template(code) data = await self.http.get_template(code) - return Template(data=data, state=self._connection) + return Template(data=data, state=self._connection) # type: ignore async def fetch_guild(self, guild_id): """|coro| @@ -1106,7 +1107,7 @@ class Client: data = await self.http.get_guild(guild_id) return Guild(data=data, state=self._connection) - async def create_guild(self, name, region=None, icon=None, *, code=None): + async def create_guild(self, name: str, region: Optional[VoiceRegion] = None, icon: Any = None, *, code: str = None): """|coro| Creates a :class:`.Guild`. @@ -1155,7 +1156,7 @@ class Client: # Invite management - async def fetch_invite(self, url, *, with_counts=True): + async def fetch_invite(self, url: Union[Invite, str], *, with_counts: bool = True) -> Invite: """|coro| Gets an :class:`.Invite` from a discord.gg URL or ID. @@ -1192,7 +1193,7 @@ class Client: data = await self.http.get_invite(invite_id, with_counts=with_counts) return Invite.from_incomplete(state=self._connection, data=data) - async def delete_invite(self, invite): + async def delete_invite(self, invite: Union[Invite, str]) -> None: """|coro| Revokes an :class:`.Invite`, URL, or ID to an invite. diff --git a/discord/guild.py b/discord/guild.py index a3343686..c5ba0d1a 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -24,6 +24,7 @@ DEALINGS IN THE SOFTWARE. import copy from collections import namedtuple +from typing import List, TYPE_CHECKING from . import utils from .role import Role @@ -48,6 +49,11 @@ __all__ = ( 'Guild', ) +if TYPE_CHECKING: + from .types.guild import ( + Ban as BanPayload + ) + BanEntry = namedtuple('BanEntry', 'reason user') _GuildLimit = namedtuple('_GuildLimit', 'emoji bitrate filesize') @@ -1429,7 +1435,7 @@ class Guild(Hashable): :class:`BanEntry` The :class:`BanEntry` object for the specified user. """ - data = await self._state.http.get_ban(user.id, self.id) + data: BanPayload = await self._state.http.get_ban(user.id, self.id) return BanEntry( user=User(state=self._state, data=data['user']), reason=data['reason'] @@ -1456,7 +1462,7 @@ class Guild(Hashable): A list of :class:`BanEntry` objects. """ - data = await self._state.http.get_bans(self.id) + data: List[BanPayload] = await self._state.http.get_bans(self.id) return [BanEntry(user=User(state=self._state, data=e['user']), reason=e['reason']) for e in data] @@ -1606,7 +1612,7 @@ class Guild(Hashable): data = await self._state.http.estimate_pruned_members(self.id, days, roles) return data['pruned'] - async def invites(self): + async def invites(self) -> List[Invite]: """|coro| Returns a list of all active instant invites from the guild. @@ -2056,7 +2062,7 @@ class Guild(Hashable): """ await self._state.http.unban(user.id, self.id, reason=reason) - async def vanity_invite(self): + async def vanity_invite(self) -> Invite: """|coro| Returns the guild's special vanity invite. diff --git a/discord/invite.py b/discord/invite.py index 3c061750..d8bd554f 100644 --- a/discord/invite.py +++ b/discord/invite.py @@ -22,6 +22,9 @@ 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 Optional, TYPE_CHECKING from .asset import Asset from .utils import parse_time, snowflake_time, _get_as_snowflake from .object import Object @@ -34,6 +37,16 @@ __all__ = ( 'Invite', ) +if TYPE_CHECKING: + from .types.invite import ( + Invite as InvitePayload, + InviteGuild as InviteGuildPayload, + ) + from .types.channel import ( + PartialChannel as PartialChannelPayload, + ) + import datetime + class PartialInviteChannel: """Represents a "partial" invite channel. @@ -72,7 +85,7 @@ class PartialInviteChannel: __slots__ = ('id', 'name', 'type') def __init__(self, **kwargs): - self.id = kwargs.pop('id') + self.id = int(kwargs.pop('id')) self.name = kwargs.pop('name') self.type = kwargs.pop('type') @@ -139,7 +152,7 @@ class PartialInviteGuild: __slots__ = ('_state', 'features', 'icon', 'banner', 'id', 'name', 'splash', 'verification_level', 'description') - def __init__(self, state, data, id): + def __init__(self, state, data: InviteGuildPayload, id: int): self._state = state self.id = id self.name = data['name'] @@ -150,33 +163,33 @@ class PartialInviteGuild: self.verification_level = try_enum(VerificationLevel, data.get('verification_level')) self.description = data.get('description') - def __str__(self): + def __str__(self) -> str: return self.name - def __repr__(self): + def __repr__(self) -> str: return ( f'<{self.__class__.__name__} id={self.id} name={self.name!r} features={self.features} ' f'description={self.description!r}>' ) @property - def created_at(self): + def created_at(self) -> datetime.datetime: """:class:`datetime.datetime`: Returns the guild's creation time in UTC.""" return snowflake_time(self.id) @property - def icon_url(self): + def icon_url(self) -> Asset: """:class:`Asset`: Returns the guild's icon asset.""" return self.icon_url_as() - def is_icon_animated(self): + def is_icon_animated(self) -> bool: """:class:`bool`: Returns ``True`` if the guild has an animated icon. .. versionadded:: 1.4 """ return bool(self.icon and self.icon.startswith('a_')) - def icon_url_as(self, *, format=None, static_format='webp', size=1024): + def icon_url_as(self, *, format=None, static_format='webp', size=1024) -> Asset: """The same operation as :meth:`Guild.icon_url_as`. Returns @@ -187,11 +200,11 @@ class PartialInviteGuild: return Asset._from_guild_icon(self._state, self, format=format, static_format=static_format, size=size) @property - def banner_url(self): + def banner_url(self) -> Asset: """:class:`Asset`: Returns the guild's banner asset.""" return self.banner_url_as() - def banner_url_as(self, *, format='webp', size=2048): + def banner_url_as(self, *, format='webp', size=2048) -> Asset: """The same operation as :meth:`Guild.banner_url_as`. Returns @@ -202,11 +215,11 @@ class PartialInviteGuild: return Asset._from_guild_image(self._state, self.id, self.banner, 'banners', format=format, size=size) @property - def splash_url(self): + def splash_url(self) -> Asset: """:class:`Asset`: Returns the guild's invite splash asset.""" return self.splash_url_as() - def splash_url_as(self, *, format='webp', size=2048): + def splash_url_as(self, *, format='webp', size=2048) -> Asset: """The same operation as :meth:`Guild.splash_url_as`. Returns @@ -313,13 +326,13 @@ class Invite(Hashable): BASE = 'https://discord.gg' - def __init__(self, *, state, data): + def __init__(self, *, state, data: InvitePayload): self._state = state self.max_age = data.get('max_age') - self.code = data.get('code') + self.code = data['code'] self.guild = data.get('guild') self.revoked = data.get('revoked') - self.created_at = parse_time(data.get('created_at')) + self.created_at: Optional[datetime.datetime] = parse_time(data.get('created_at')) # type: ignore self.temporary = data.get('temporary') self.uses = data.get('uses') self.max_uses = data.get('max_uses') @@ -346,7 +359,7 @@ class Invite(Hashable): # As far as I know, invites always need a channel # So this should never raise. - channel_data = data['channel'] + channel_data: PartialChannelPayload = data['channel'] channel_id = int(channel_data['id']) channel_type = try_enum(ChannelType, channel_data['type']) channel = PartialInviteChannel(id=channel_id, name=channel_data['name'], type=channel_type) @@ -373,30 +386,30 @@ class Invite(Hashable): data['channel'] = channel return cls(state=state, data=data) - def __str__(self): + def __str__(self) -> str: return self.url - def __repr__(self): + def __repr__(self) -> str: return ( f'<Invite code={self.code!r} guild={self.guild!r} ' f'online={self.approximate_presence_count} ' f'members={self.approximate_member_count}>' ) - def __hash__(self): + def __hash__(self) -> int: return hash(self.code) @property - def id(self): + def id(self) -> str: """:class:`str`: Returns the proper code portion of the invite.""" return self.code @property - def url(self): + def url(self) -> str: """:class:`str`: A property that retrieves the invite URL.""" return self.BASE + '/' + self.code - async def delete(self, *, reason=None): + async def delete(self, *, reason: Optional[str] = None): """|coro| Revokes the instant invite. diff --git a/discord/template.py b/discord/template.py index af2d0dc4..5b9ad871 100644 --- a/discord/template.py +++ b/discord/template.py @@ -22,6 +22,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from typing import Any, Optional, TYPE_CHECKING, overload from .utils import parse_time, _get_as_snowflake, _bytes_to_base64_data from .enums import VoiceRegion from .guild import Guild @@ -30,6 +31,9 @@ __all__ = ( 'Template', ) +if TYPE_CHECKING: + from .types.template import Template as TemplatePayload + class _FriendlyHttpAttributeErrorHelper: __slots__ = () @@ -101,11 +105,11 @@ class Template: The source guild. """ - def __init__(self, *, state, data): + def __init__(self, *, state, data: TemplatePayload): self._state = state self._store(data) - def _store(self, data): + def _store(self, data: TemplatePayload): self.code = data['code'] self.uses = data['usage_count'] self.name = data['name'] @@ -120,7 +124,7 @@ class Template: guild = self._state._get_guild(id) - if guild is None: + if guild is None and id: source_serialised = data['serialized_source_guild'] source_serialised['id'] = id state = _PartialTemplateState(state=self._state) @@ -128,13 +132,13 @@ class Template: self.source_guild = guild - def __repr__(self): + def __repr__(self) -> str: return ( f'<Template code={self.code!r} uses={self.uses} name={self.name!r}' f' creator={self.creator!r} source_guild={self.source_guild!r}>' ) - async def create_guild(self, name, region=None, icon=None): + async def create_guild(self, name: str, region: Optional[VoiceRegion] = None, icon: Any = None): """|coro| Creates a :class:`.Guild` using the template. @@ -174,7 +178,7 @@ class Template: data = await self._state.http.create_from_template(self.code, name, region_value, icon) return Guild(data=data, state=self._state) - async def sync(self): + async def sync(self) -> None: """|coro| Sync the template to the guild's current state. @@ -197,7 +201,20 @@ class Template: data = await self._state.http.sync_template(self.source_guild.id, self.code) self._store(data) - async def edit(self, **kwargs): + @overload + async def edit( + self, + *, + name: Optional[str] = ..., + description: Optional[str] = ..., + ) -> None: + ... + + @overload + async def edit(self) -> None: + ... + + async def edit(self, **kwargs) -> None: """|coro| Edit the template metadata. @@ -226,7 +243,7 @@ class Template: data = await self._state.http.edit_template(self.source_guild.id, self.code, kwargs) self._store(data) - async def delete(self): + async def delete(self) -> None: """|coro| Delete the template. diff --git a/discord/types/guild.py b/discord/types/guild.py index d78cb8a3..83c69916 100644 --- a/discord/types/guild.py +++ b/discord/types/guild.py @@ -31,6 +31,12 @@ from .activity import PartialPresenceUpdate from .role import Role from .member import Member from .emoji import Emoji +from .user import User + + +class Ban(TypedDict): + reason: Optional[str] + user: User class _UnavailableGuildOptional(TypedDict, total=False): diff --git a/discord/types/invite.py b/discord/types/invite.py new file mode 100644 index 00000000..25466158 --- /dev/null +++ b/discord/types/invite.py @@ -0,0 +1,60 @@ +""" +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 Literal, TypedDict + +from .guild import InviteGuild, _GuildPreviewUnique +from .channel import PartialChannel +from .user import PartialUser + +TargetUserType = Literal[1] + + +class _InviteOptional(TypedDict, total=False): + guild: InviteGuild + inviter: PartialUser + target_user: PartialUser + target_user_type: TargetUserType + + +class _InviteMetadata(TypedDict, total=False): + uses: int + max_uses: int + temporary: bool + created_at: str + + +class IncompleteInvite(_InviteMetadata): + code: str + channel: PartialChannel + + +class Invite(IncompleteInvite, _InviteOptional): + ... + + +class InviteWithCounts(Invite, _GuildPreviewUnique): + ... diff --git a/discord/types/template.py b/discord/types/template.py new file mode 100644 index 00000000..eaf5e756 --- /dev/null +++ b/discord/types/template.py @@ -0,0 +1,44 @@ +""" +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 Optional, TypedDict +from .snowflake import Snowflake +from .user import User +from .guild import Guild + + +class Template(TypedDict): + code: str + name: str + description: Optional[str] + usage_count: int + creator_id: Snowflake + creator: User + created_at: str + updated_at: str + source_guild_id: Snowflake + serialized_source_guild: Guild + is_dirty: Optional[bool] |