aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRapptz <[email protected]>2015-12-04 22:13:17 -0500
committerRapptz <[email protected]>2015-12-04 22:13:17 -0500
commit79bdf2a72174a634aad0a752cad60d96ee8336f6 (patch)
tree866d7d4ac7cf19071c7190ab4c9a749eedb2c851
parentAdd channel management functions (diff)
downloaddiscord.py-79bdf2a72174a634aad0a752cad60d96ee8336f6.tar.xz
discord.py-79bdf2a72174a634aad0a752cad60d96ee8336f6.zip
Add enumerators instead of strings.
Changes channel type, status and server region into 3.4 enums.
-rw-r--r--discord/channel.py89
-rw-r--r--discord/client.py13
-rw-r--r--discord/enums.py54
-rw-r--r--discord/member.py57
-rw-r--r--discord/server.py60
-rw-r--r--discord/state.py5
-rw-r--r--docs/api.rst61
7 files changed, 231 insertions, 108 deletions
diff --git a/discord/channel.py b/discord/channel.py
index 32b98fda..7204d31f 100644
--- a/discord/channel.py
+++ b/discord/channel.py
@@ -26,44 +26,37 @@ DEALINGS IN THE SOFTWARE.
from copy import deepcopy
from . import utils
from .permissions import Permissions
+from .enums import ChannelType
from collections import namedtuple
Overwrites = namedtuple('Overwrites', 'id allow deny type')
-class Channel(object):
+class Channel:
"""Represents a Discord server channel.
- Instance attributes:
-
- .. attribute:: name
-
+ Attributes
+ -----------
+ name : str
The channel name.
- .. attribute:: server
-
- The :class:`Server` the channel belongs to.
- .. attribute:: id
-
+ server : :class:`Server`
+ The server the channel belongs to.
+ id : str
The channel ID.
- .. attribute:: topic
-
+ topic : Optional[str]
The channel's topic. None if it doesn't exist.
- .. attribute:: is_private
-
+ is_private : bool
``True`` if the channel is a private channel (i.e. PM). ``False`` in this case.
- .. attribute:: position
-
+ position : int
The position in the channel list.
- .. attribute:: type
-
- The channel type. Usually ``'voice'`` or ``'text'``.
- .. attribute:: changed_roles
-
+ type : :class:`ChannelType`
+ The channel type. There is a chance that the type will be ``str`` if
+ the channel type is not within the ones recognised by the enumerator.
+ changed_roles
A list of :class:`Roles` that have been overridden from their default
values in the :attr:`Server.roles` attribute.
- .. attribute:: voice_members
-
+ voice_members
A list of :class:`Members` that are currently inside this voice channel.
- If :attr:`type` is not ``'voice'`` then this is always an empty array.
+ If :attr:`type` is not :attr:`ChannelType.voice` then this is always an empty array.
"""
def __init__(self, **kwargs):
@@ -78,6 +71,11 @@ class Channel(object):
self.is_private = False
self.position = kwargs.get('position')
self.type = kwargs.get('type')
+ try:
+ self.type = ChannelType(self.type)
+ except:
+ pass
+
self.changed_roles = []
self._permission_overwrites = []
for overridden in kwargs.get('permission_overwrites', []):
@@ -98,11 +96,11 @@ class Channel(object):
self.changed_roles.append(override)
def is_default_channel(self):
- """Checks if this is the default channel for the :class:`Server` it belongs to."""
+ """bool : Indicates if this is the default channel for the :class:`Server` it belongs to."""
return self.server.id == self.id
def mention(self):
- """Returns a string that allows you to mention the channel."""
+ """str : The string that allows you to mention the channel."""
return '<#{0.id}>'.format(self)
def permissions_for(self, member):
@@ -116,8 +114,15 @@ class Channel(object):
- Member overrides
- Whether the channel is the default channel.
- :param member: The :class:`Member` to resolve permissions for.
- :return: The resolved :class:`Permissions` for the :class:`Member`.
+ Parameters
+ ----------
+ member : :class:`Member`
+ The member to resolve permissions for.
+
+ Returns
+ -------
+ :class:`Permissions`
+ The resolved permissions for the member.
"""
# The current cases can be explained as:
@@ -173,19 +178,16 @@ class Channel(object):
return base
-class PrivateChannel(object):
+class PrivateChannel:
"""Represents a Discord private channel.
- Instance attributes:
-
- .. attribute:: user
-
- The :class:`User` in the private channel.
- .. attribute:: id
-
+ Attributes
+ ----------
+ user : :class:`User`
+ The user you are participating with in the private channel.
+ id : str
The private channel ID.
- .. attribute:: is_private
-
+ is_private : bool
``True`` if the channel is a private channel (i.e. PM). ``True`` in this case.
"""
@@ -207,8 +209,15 @@ class PrivateChannel(object):
- can_manage_messages: You cannot delete others messages in a PM.
- can_mention_everyone: There is no one to mention in a PM.
- :param user: The :class:`User` to check permissions for.
- :return: A :class:`Permission` with the resolved permission value.
+ Parameters
+ -----------
+ user : :class:`User`
+ The user to check permissions for.
+
+ Returns
+ --------
+ :class:`Permission`
+ The resolved permissions for the user.
"""
base = Permissions.TEXT
diff --git a/discord/client.py b/discord/client.py
index 9f675ed3..881bab6b 100644
--- a/discord/client.py
+++ b/discord/client.py
@@ -34,6 +34,7 @@ from .object import Object
from .errors import *
from .state import ConnectionState
from . import utils
+from .enums import ChannelType, ServerRegion
import asyncio
import aiohttp
@@ -1085,7 +1086,7 @@ class Client:
log.debug(request_success_log.format(response=r, json=payload, data=data))
@asyncio.coroutine
- def create_channel(self, server, name, type='text'):
+ def create_channel(self, server, name, type=None):
"""|coro|
Creates a :class:`Channel` in the specified :class:`Server`.
@@ -1098,8 +1099,8 @@ class Client:
The server to create the channel in.
name : str
The channel's name.
- type : str
- The type of channel to create. 'text' or 'voice'.
+ type : :class:`ChannelType`
+ The type of channel to create. Defaults to :attr:`ChannelType.text`.
Raises
-------
@@ -1117,9 +1118,12 @@ class Client:
different than the one that will be added in cache.
"""
+ if type is None:
+ type = ChannelType.text
+
payload = {
'name': name,
- 'type': type
+ 'type': str(type)
}
url = '{0}/{1.id}/channels'.format(endpoints.SERVERS, server)
@@ -1160,4 +1164,3 @@ class Client:
response = yield from self.session.delete(url, headers=self.headers)
log.debug(request_logging_format.format(method='DELETE', response=response))
yield from utils._verify_successful_response(response)
-
diff --git a/discord/enums.py b/discord/enums.py
new file mode 100644
index 00000000..0d2d9390
--- /dev/null
+++ b/discord/enums.py
@@ -0,0 +1,54 @@
+# -*- 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 enum import Enum
+
+class ChannelType(Enum):
+ text = 'text'
+ voice = 'voice'
+
+ def __str__(self):
+ return self.value
+
+class ServerRegion(Enum):
+ us_west = 'us-west'
+ us_east = 'us-east'
+ singapore = 'singapore'
+ london = 'london'
+ sydney = 'sydney'
+ amsterdam = 'amsterdam'
+ frankfurt = 'frankfurt'
+
+ def __str__(self):
+ return self.value
+
+class Status(Enum):
+ online = 'online'
+ offline = 'offline'
+ idle = 'idle'
+
+ def __str__(self):
+ return self.value
diff --git a/discord/member.py b/discord/member.py
index 2fadc664..5c151e9f 100644
--- a/discord/member.py
+++ b/discord/member.py
@@ -26,6 +26,7 @@ DEALINGS IN THE SOFTWARE.
from .user import User
from .utils import parse_time
+from .enums import Status
class Member(User):
"""Represents a Discord member to a :class:`Server`.
@@ -33,44 +34,34 @@ class Member(User):
This is a subclass of :class:`User` that extends more functionality
that server members have such as roles and permissions.
- Instance attributes:
-
- .. attribute:: deaf
-
- A boolean that specifies if the member is currently deafened by the server.
- .. attribute:: mute
-
- A boolean that specifies if the member is currently muted by the server.
- .. attribute:: self_mute
-
- A boolean that specifies if the member is currently muted by their own accord.
- .. attribute:: self_deaf
-
- A boolean that specifies if the member is currently deafened by their own accord.
- .. attribute:: is_afk
-
- A boolean that specifies if the member is currently in the AFK channel in the server.
- .. attribute:: voice_channel
-
- A voice :class:`Channel` that the member is currently connected to. None if the member
+ Attributes
+ ----------
+ deaf : bool
+ Indicates if the member is currently deafened by the server.
+ mute : bool
+ Indicates if the member is currently muted by the server.
+ self_mute : bool
+ Indicates if the member is currently muted by their own accord.
+ self_deaf : bool
+ Indicates if the member is currently deafened by their own accord.
+ is_afk : bool
+ Indicates if the member is currently in the AFK channel in the server.
+ voice_channel : :class:`Channel`
+ The voice channel that the member is currently connected to. None if the member
is not currently in a voice channel.
- .. attribute:: roles
-
+ roles
A list of :class:`Role` that the member belongs to. Note that the first element of this
list is always the default '@everyone' role.
- .. attribute:: joined_at
-
+ joined_at : `datetime.datetime`
A datetime object that specifies the date and time in UTC that the member joined the server for
the first time.
- .. attribute:: status
-
- A string that denotes the user's status. Can be 'online', 'offline' or 'idle'.
- .. attribute:: game_id
-
+ status : :class:`Status`
+ The member's status. There is a chance that the status will be a ``str``
+ if it is a value that is not recognised by the enumerator.
+ game_id : int
The game ID that the user is currently playing. Could be None if no game is being played.
- .. attribute:: server
-
- The :class:`Server` that the member belongs to.
+ server : :class:`Server`
+ The server that the member belongs to.
"""
def __init__(self, deaf, joined_at, user, roles, mute, **kwargs):
@@ -79,7 +70,7 @@ class Member(User):
self.mute = mute
self.joined_at = parse_time(joined_at)
self.roles = roles
- self.status = 'offline'
+ self.status = Status.offline
self.game_id = kwargs.get('game_id', None)
self.server = kwargs.get('server', None)
self.update_voice_state(mute=mute, deaf=deaf)
diff --git a/discord/server.py b/discord/server.py
index 0c2eca5a..8309f052 100644
--- a/discord/server.py
+++ b/discord/server.py
@@ -28,45 +28,36 @@ from . import utils
from .role import Role
from .member import Member
from .channel import Channel
+from .enums import ServerRegion, Status
-class Server(object):
+class Server:
"""Represents a Discord server.
- Instance attributes:
-
- .. attribute:: name
-
+ Attributes
+ ----------
+ name : str
The server name.
- .. attribute:: roles
-
+ roles
A list of :class:`Role` that the server has available.
- .. attribute:: region
-
- The region the server belongs on.
- .. attribute:: afk_timeout
-
+ region : :class:`ServerRegion`
+ The region the server belongs on. There is a chance that the region
+ will be a ``str`` if the value is not recognised by the enumerator.
+ afk_timeout : int
The timeout to get sent to the AFK channel.
- .. attribute:: afk_channel
-
- The :class:`Channel` that denotes the AFK channel. None if it doesn't exist.
- .. attribute:: members
-
+ afk_channel : :class:`Channel`
+ The channel that denotes the AFK channel. None if it doesn't exist.
+ members
A list of :class:`Member` that are currently on the server.
- .. attribute:: channels
-
+ channels
A list of :class:`Channel` that are currently on the server.
- .. attribute:: icon
-
+ icon : str
The server's icon.
- .. attribute:: id
-
+ id : str
The server's ID.
- .. attribute:: owner
-
- The :class:`Member` who owns the server.
- .. attribute:: unavailable
-
- A boolean indicating if the server is unavailable. If this is ``True`` then the
+ owner : :class:`Member`
+ The member who owns the server.
+ unavailable : bool
+ Indicates if the server is unavailable. If this is ``True`` then the
reliability of other attributes outside of :meth:`Server.id` is slim and they might
all be None. It is best to not do anything with the server if it is unavailable.
@@ -88,6 +79,11 @@ class Server(object):
def _from_data(self, guild):
self.name = guild.get('name')
self.region = guild.get('region')
+ try:
+ self.region = ServerRegion(self.region)
+ except:
+ pass
+
self.afk_timeout = guild.get('afk_timeout')
self.icon = guild.get('icon')
self.unavailable = guild.get('unavailable', False)
@@ -119,6 +115,10 @@ class Server(object):
member = utils.find(lambda m: m.id == user_id, self.members)
if member is not None:
member.status = presence['status']
+ try:
+ member.status = Status(member.status)
+ except:
+ pass
member.game_id = presence['game_id']
self.channels = [Channel(server=self, **c) for c in guild['channels']]
@@ -137,7 +137,7 @@ class Server(object):
return utils.find(lambda c: c.is_default_channel(), self.channels)
def icon_url(self):
- """Returns the URL version of the server's icon. Returns None if it has no icon."""
+ """Returns the URL version of the server's icon. Returns an empty string if it has no icon."""
if self.icon is None:
return ''
return 'https://cdn.discordapp.com/icons/{0.id}/{0.icon}.jpg'.format(self)
diff --git a/discord/state.py b/discord/state.py
index b62817a7..d8b38a43 100644
--- a/discord/state.py
+++ b/discord/state.py
@@ -31,6 +31,7 @@ from .channel import Channel, PrivateChannel
from .member import Member
from .role import Role
from . import utils
+from .enums import Status
from collections import deque
import copy
@@ -113,6 +114,10 @@ class ConnectionState:
if member is not None:
old_member = copy.copy(member)
member.status = data.get('status')
+ try:
+ member.status = Status(member.status)
+ except:
+ pass
member.game_id = data.get('game_id')
member.name = user.get('username', member.name)
member.avatar = user.get('avatar', member.avatar)
diff --git a/docs/api.rst b/docs/api.rst
index 59efac78..93b2c239 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -297,6 +297,67 @@ Utility Functions
.. autofunction:: discord.utils.find
+Enumerators
+------------
+
+The API provides some enumerators for certain types of strings to avoid the API
+from being stringly typed in case the strings change in the future.
+
+All enumerators are subclasses of `enum`_.
+
+.. _enum: https://docs.python.org/3/library/enum.html
+
+.. class:: ChannelType
+
+ Specifies the type of :class:`Channel`.
+
+ .. attribute:: text
+
+ A text channel.
+ .. attribute:: voice
+
+ A voice channel.
+
+.. class:: ServerRegion
+
+ Specifies the region a :class:`Server`'s voice server belongs to.
+
+ .. attribute:: us_west
+
+ The US West region.
+ .. attribute:: us_east
+
+ The US East region.
+ .. attribute:: singapore
+
+ The Singapore region.
+ .. attribute:: london
+
+ The London region.
+ .. attribute:: sydney
+
+ The Sydney region.
+ .. attribute:: amsterdam
+
+ The Amsterdam region.
+ .. attribute:: frankfurt
+
+ The Frankfurt region.
+
+.. class:: Status
+
+ Specifies a :class:`Member` 's status.
+
+ .. attribute:: online
+
+ The member is online.
+ .. attribute:: offline
+
+ The member is offline.
+ .. attribute:: idle
+
+ The member is idle.
+
Data Classes
--------------