aboutsummaryrefslogtreecommitdiff
path: root/discord
diff options
context:
space:
mode:
Diffstat (limited to 'discord')
-rw-r--r--discord/__init__.py1
-rw-r--r--discord/client.py38
-rw-r--r--discord/http.py11
-rw-r--r--discord/template.py140
-rw-r--r--discord/utils.py11
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 ('*', '`', '_', '~', '|'))