From 80eb567520d2f20b383cf680f14b07c8f3728ac2 Mon Sep 17 00:00:00 2001 From: Rapptz Date: Fri, 4 Sep 2015 23:23:47 -0400 Subject: [PATCH] Add support for creating invites. --- discord/__init__.py | 1 + discord/client.py | 31 ++++++++++++++++ discord/invite.py | 86 +++++++++++++++++++++++++++++++++++++++++++++ docs/api.rst | 3 ++ 4 files changed, 121 insertions(+) create mode 100644 discord/invite.py diff --git a/discord/__init__.py b/discord/__init__.py index 07d7ea12c..55023f7bc 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -25,3 +25,4 @@ from .server import Server, Member, Permissions, Role from .message import Message from .errors import * from .permissions import Permissions +from .invite import Invite diff --git a/discord/client.py b/discord/client.py index 098f16f74..b55e9cbf3 100644 --- a/discord/client.py +++ b/discord/client.py @@ -31,6 +31,7 @@ from .channel import Channel, PrivateChannel from .server import Server, Member, Permissions, Role from .message import Message from .utils import parse_time +from .invite import Invite import requests import json, re, time, copy @@ -694,3 +695,33 @@ class Client(object): url = '{0}/{1.id}'.format(endpoints.SERVERS, server) requests.delete(url, headers=self.headers) + + def create_invite(self, destination, **options): + """Creates an invite for the destination which could be either a :class:`Server` or :class:`Channel`. + + The available options are: + + :param destination: The :class:`Server` or :class:`Channel` to create the invite to. + :param max_age: How long the invite should last. If it's 0 then the invite doesn't expire. Defaults to 0. + :param max_uses: How many uses the invite could be used for. If it's 0 then there are unlimited uses. Defaults to 0. + :param temporary: A boolean to denote that the invite grants temporary membership (i.e. they get kicked after they disconnect). Defaults to False. + :param xkcd: A boolean to indicate if the invite URL is human readable. Defaults to False. + :returns: The :class:`Invite` if creation is successful, None otherwise. + """ + + payload = { + 'max_age': options.get('max_age', 0), + 'max_uses': options.get('max_uses', 0), + 'temporary': options.get('temporary', False), + 'xkcdpass': options.get('xkcd', False) + } + + url = '{0}/{1.id}/invites'.format(endpoints.CHANNELS, destination) + response = requests.post(url, headers=self.headers, json=payload) + if response.status_code in (200, 201): + data = response.json() + data['server'] = self._get_server(data['guild']['id']) + data['channel'] = next((ch for ch in data['server'].channels if ch.id == data['channel']['id'])) + return Invite(**data) + + return None diff --git a/discord/invite.py b/discord/invite.py new file mode 100644 index 000000000..39897b444 --- /dev/null +++ b/discord/invite.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +""" +The MIT License (MIT) + +Copyright (c) 2015 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 .user import User +from .utils import parse_time + +class Invite(object): + """Represents a Discord :class:`Server` or :class:`Channel` invite. + + Instance attributes: + + .. attribute:: max_age + + How long the before the invite expires in seconds. A value of 0 indicates that it doesn't expire. + .. attribute:: code + + The URL fragment used for the invite. :attr:`xkcd` is also a possible fragment. + .. attribute:: server + + The :class:`Server` the invite is for. + .. attribute:: revoked + + A boolean indicating if the invite has been revoked. + .. attribute:: created_at + + A datetime object denoting the time the invite was created. + .. attribute:: temporary + + A boolean indicating that the invite grants temporary membership. If True, members who joined via this invite will be kicked upon disconnect. + .. attribute:: uses + + How many times the invite has been used. + .. attribute:: max_uses + + How many times the invite can be used. + .. attribute:: xkcd + + The URL fragment used for the invite if it is human readable. + .. attribute:: inviter + + The :class:`User` who created the invite. + .. attribute:: channel + + The :class:`Channel` the invite is for. + """ + + def __init__(self, **kwargs): + self.max_age = kwargs.get('max_age') + self.code = kwargs.get('code') + self.server = kwargs.get('server') + self.revoked = kwargs.get('revoked') + self.created_at = parse_time(kwargs.get('created_at')) + self.temporary = kwargs.get('temporary') + self.uses = kwargs.get('uses') + self.max_uses = kwargs.get('max_uses') + self.xkcd = kwargs.get('xkcdpass') + self.inviter = User(**kwargs.get('inviter', {})) + self.channel = kwargs.get('channel') + + @property + def url(self): + """A property that retrieves the invite URL.""" + return 'http://discord.gg/{}'.format(self.xkcd if self.xkcd else self.code) diff --git a/docs/api.rst b/docs/api.rst index c74ea20a5..23f1e458b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -120,6 +120,9 @@ Some classes are just there to be data containers, this lists them. It should be .. autoclass:: PrivateChannel :members: +.. autoclass:: Invite + :members: + Exceptions ------------