diff options
Diffstat (limited to 'discord')
| -rw-r--r-- | discord/__init__.py | 1 | ||||
| -rw-r--r-- | discord/client.py | 38 | ||||
| -rw-r--r-- | discord/http.py | 11 | ||||
| -rw-r--r-- | discord/template.py | 140 | ||||
| -rw-r--r-- | discord/utils.py | 11 |
5 files changed, 199 insertions, 2 deletions
diff --git a/discord/__init__.py b/discord/__init__.py index f4846d45..589a1629 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -40,6 +40,7 @@ from .role import Role from .file import File from .colour import Color, Colour from .invite import Invite, PartialInviteChannel, PartialInviteGuild +from .template import Template from .widget import Widget, WidgetMember, WidgetChannel from .object import Object from .reaction import Reaction diff --git a/discord/client.py b/discord/client.py index 88cd09a1..b5ab12a9 100644 --- a/discord/client.py +++ b/discord/client.py @@ -37,6 +37,7 @@ import websockets from .user import User, Profile from .asset import Asset from .invite import Invite +from .template import Template from .widget import Widget from .guild import Guild from .channel import _channel_factory @@ -1019,6 +1020,32 @@ class Client: """ return GuildIterator(self, limit=limit, before=before, after=after) + async def fetch_template(self, code): + """|coro| + + Gets a :class:`.Template` from a discord.new URL or code. + + Parameters + ----------- + code: :class:`str` + The Discord Template Code or URL (must be a discord.new URL). + + Raises + ------- + :exc:`.NotFound` + The template is invalid. + :exc:`.HTTPException` + Getting the template failed. + + Returns + -------- + :class:`.Template` + The template from the URL/code. + """ + code = utils.resolve_template(code) + data = await self.http.get_template(code) + return Template(data=data, state=self._connection) + async def fetch_guild(self, guild_id): """|coro| @@ -1053,7 +1080,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): + async def create_guild(self, name, region=None, icon=None, *, code=None): """|coro| Creates a :class:`.Guild`. @@ -1070,6 +1097,10 @@ class Client: icon: :class:`bytes` The :term:`py:bytes-like object` representing the icon. See :meth:`.ClientUser.edit` for more details on what is expected. + code: Optional[:class:`str`] + The code for a template to create the guild with. + + .. versionadded:: 1.4 Raises ------ @@ -1092,7 +1123,10 @@ class Client: else: region = region.value - data = await self.http.create_guild(name, region, icon) + if code: + data = await self.http.create_from_template(code, name, region, icon) + else: + data = await self.http.create_guild(name, region, icon) return Guild(data=data, state=self._connection) # Invite management diff --git a/discord/http.py b/discord/http.py index dba174b6..c5650c24 100644 --- a/discord/http.py +++ b/discord/http.py @@ -630,6 +630,17 @@ class HTTPClient: return self.request(Route('PATCH', '/guilds/{guild_id}', guild_id=guild_id), json=payload, reason=reason) + def get_template(self, code): + return self.request(Route('GET', '/guilds/templates/{code}', code=code)) + + def create_from_template(self, code, name, region, icon): + payload = { + 'name': name, + 'icon': icon, + 'region': region + } + return self.request(Route('POST', '/guilds/templates/{code}', code=code), json=payload) + def get_bans(self, guild_id): return self.request(Route('GET', '/guilds/{guild_id}/bans', guild_id=guild_id)) diff --git a/discord/template.py b/discord/template.py new file mode 100644 index 00000000..905f5515 --- /dev/null +++ b/discord/template.py @@ -0,0 +1,140 @@ +from .utils import parse_time, _get_as_snowflake +from .enums import VoiceRegion +from .guild import Guild + +__all__ = ( + 'Template' +) + +class _FriendlyHttpAttributeErrorHelper: + __slots__ = () + + def __getattr__(self, attr): + raise AttributeError('PartialTemplateState does not support http methods.') + +class _PartialTemplateState: + def __init__(self, *, state): + self.__state = state + self.http = _FriendlyHttpAttributeErrorHelper() + + @property + def is_bot(self): + return self.__state.is_bot + + @property + def shard_count(self): + return self.__state.shard_count + + @property + def user(self): + return self.__state.user + + @property + def self_id(self): + return self.__state.user.id + + def store_emoji(self, guild, packet): + return None + + def _get_voice_client(self, id): + return None + + def _get_message(self, id): + return None + + async def query_members(self, **kwargs): + return [] + + def __getattr__(self, attr): + raise AttributeError('PartialTemplateState does not support {0!r}.'.format(attr)) + +class Template: + """Represents a Discord template. + + .. versionadded:: 1.4 + + Attributes + ----------- + code: :code:`str` + The template code. + uses: :class:`int` + How many time the template has been used. + name: :class:`str` + The name of the template. + description: :class:`str` + The description of the template. + creator: :class:`User` + The creator of the template. + created_at: :class:`datetime.datetime` + When the template was created. + updated_at: :class:`datetime.datetime` + When the template was last updated (referred to as "last synced" in the client). + source_guild: :class:`TemplateGuild` + The source guild. + """ + + def __init__(self, *, state, data): + self._state = state + + self.code = data['code'] + self.uses = data['usage_count'] + self.name = data['name'] + self.description = data['description'] + creator_data = data.get('creator') + self.creator = None if creator_data is None else self._state.store_user(creator_data) + + self.created_at = parse_time(data.get('created_at')) + self.updated_at = parse_time(data.get('updated_at')) + + id = _get_as_snowflake(data, 'source_guild_id') + source_serialised = data['serialized_source_guild'] + source_serialised['id'] = id + state = _PartialTemplateState(state=self._state) + + self.source_guild = Guild(data=source_serialised, state=state) + + def __repr__(self): + return '<Template code={0.code!r} uses={0.uses} name={0.name!r}' \ + ' creator={0.creator!r} source_guild={0.source_guild!r}>'.format(self) + + async def create_guild(self, name, region=None, icon=None): + """|coro| + + Creates a :class:`.Guild` using the template. + + Bot accounts in more than 10 guilds are not allowed to create guilds. + + Parameters + ---------- + name: :class:`str` + The name of the guild. + region: :class:`.VoiceRegion` + The region for the voice communication server. + Defaults to :attr:`.VoiceRegion.us_west`. + icon: :class:`bytes` + The :term:`py:bytes-like object` representing the icon. See :meth:`.ClientUser.edit` + for more details on what is expected. + + Raises + ------ + :exc:`.HTTPException` + Guild creation failed. + :exc:`.InvalidArgument` + Invalid icon image format given. Must be PNG or JPG. + + Returns + ------- + :class:`.Guild` + The guild created. This is not the same guild that is + added to cache. + """ + if icon is not None: + icon = _bytes_to_base64_data(icon) + + if region is None: + region = VoiceRegion.us_west.value + else: + region = region.value + + data = await self._state.http.create_from_template(self.code, name, region, icon) + return Guild(data=data, state=self._sate) diff --git a/discord/utils.py b/discord/utils.py index 118b4691..9c4bd43a 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -451,6 +451,17 @@ def resolve_invite(invite): return m.group(1) return invite +def resolve_template(code): + from .template import Template # circular import + if isinstance(code, (Template, Object)): + return template.id + else: + rx = r'(?:https?\:\/\/)?discord(?:\.new|(?:app)?\.com\/template)\/(.+)' + m = re.match(rx, code) + if m: + return m.group(1) + return code + _MARKDOWN_ESCAPE_SUBREGEX = '|'.join(r'\{0}(?=([\s\S]*((?<!\{0})\{0})))'.format(c) for c in ('*', '`', '_', '~', '|')) |