aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--discord/channel.py6
-rw-r--r--discord/emoji.py50
-rw-r--r--discord/guild.py78
-rw-r--r--discord/http.py5
-rw-r--r--discord/member.py201
-rw-r--r--discord/state.py5
6 files changed, 335 insertions, 10 deletions
diff --git a/discord/channel.py b/discord/channel.py
index a3ba99dc..70b0b0b3 100644
--- a/discord/channel.py
+++ b/discord/channel.py
@@ -355,7 +355,8 @@ class TextChannel(abc.MessageChannel, CommonGuildChannel):
Edits the channel.
- You must have the Manage Channel permission to use this.
+ You must have the :attr:`Permissions.manage_channel` permission to
+ use this.
Parameters
----------
@@ -446,7 +447,8 @@ class VoiceChannel(CommonGuildChannel):
Edits the channel.
- You must have the Manage Channel permission to use this.
+ You must have the :attr:`Permissions.manage_channel` permission to
+ use this.
Parameters
----------
diff --git a/discord/emoji.py b/discord/emoji.py
index 9e90bff6..1f06c7e4 100644
--- a/discord/emoji.py
+++ b/discord/emoji.py
@@ -24,6 +24,8 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
+import asyncio
+
from . import utils
from .mixins import Hashable
@@ -107,3 +109,51 @@ class Emoji(Hashable):
def url(self):
"""Returns a URL version of the emoji."""
return "https://discordapp.com/api/emojis/{0.id}.png".format(self)
+
+
+ @asyncio.coroutine
+ def delete(self):
+ """|coro|
+
+ Deletes the custom emoji.
+
+ You must have :attr:`Permissions.manage_emojis` permission to
+ do this.
+
+ Guild local emotes can only be deleted by user bots.
+
+ Raises
+ -------
+ Forbidden
+ You are not allowed to delete emojis.
+ HTTPException
+ An error occurred deleting the emoji.
+ """
+
+ yield from self._state.http.delete_custom_emoji(self.guild.id, self.id)
+
+ @asyncio.coroutine
+ def edit(self, *, name):
+ """|coro|
+
+ Edits the custom emoji.
+
+ You must have :attr:`Permissions.manage_emojis` permission to
+ do this.
+
+ Guild local emotes can only be edited by user bots.
+
+ Parameters
+ -----------
+ name: str
+ The new emoji name.
+
+ Raises
+ -------
+ Forbidden
+ You are not allowed to edit emojis.
+ HTTPException
+ An error occurred editing the emoji.
+ """
+
+ yield from self._state.http.edit_custom_emoji(self.guild.id, self.id, name=name)
diff --git a/discord/guild.py b/discord/guild.py
index 6e37c3ab..3ed07b20 100644
--- a/discord/guild.py
+++ b/discord/guild.py
@@ -680,3 +680,81 @@ class Guild(Hashable):
# TODO: add to cache
return role
+
+ @asyncio.coroutine
+ def kick(self, user):
+ """|coro|
+
+ Kicks a user from the guild.
+
+ The user must meet the :class:`abc.Snowflake` abc.
+
+ You must have :attr:`Permissions.kick_members` permissions to
+ do this.
+
+ Parameters
+ -----------
+ user: :class:`abc.Snowflake`
+ The user to kick from their guild.
+
+ Raises
+ -------
+ Forbidden
+ You do not have the proper permissions to kick.
+ HTTPException
+ Kicking failed.
+ """
+ yield from self._state.http.kick(user.id, self.id)
+
+ @asyncio.coroutine
+ def ban(self, user, *, delete_message_days=1):
+ """|coro|
+
+ Bans a user from the guild.
+
+ The user must meet the :class:`abc.Snowflake` abc.
+
+ You must have :attr:`Permissions.ban_members` permissions to
+ do this.
+
+ Parameters
+ -----------
+ user: :class:`abc.Snowflake`
+ The user to ban from their guild.
+ delete_message_days: int
+ The number of days worth of messages to delete from the user
+ in the guild. The minimum is 0 and the maximum is 7.
+
+ Raises
+ -------
+ Forbidden
+ You do not have the proper permissions to ban.
+ HTTPException
+ Banning failed.
+ """
+ yield from self._state.http.ban(user.id, self.id, delete_message_days)
+
+ @asyncio.coroutine
+ def unban(self, user):
+ """|coro|
+
+ Unbans a user from the guild.
+
+ The user must meet the :class:`abc.Snowflake` abc.
+
+ You must have :attr:`Permissions.ban_members` permissions to
+ do this.
+
+ Parameters
+ -----------
+ user: :class:`abc.Snowflake`
+ The user to unban.
+
+ Raises
+ -------
+ Forbidden
+ You do not have the proper permissions to unban.
+ HTTPException
+ Unbanning failed.
+ """
+ yield from self._state.http.unban(user.id, self.id)
diff --git a/discord/http.py b/discord/http.py
index 3b957a16..ecb9cd9f 100644
--- a/discord/http.py
+++ b/discord/http.py
@@ -383,6 +383,11 @@ class HTTPClient:
bucket = 'members:{}'.format(guild_id)
return self.patch(url, json=payload, bucket=bucket)
+ def edit_member(self, guild_id, user_id, **fields):
+ url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
+ bucket = 'members:%s' % guild_id
+ return self.patch(url, json=fields, bucket=bucket)
+
# Channel management
def edit_channel(self, channel_id, **options):
diff --git a/discord/member.py b/discord/member.py
index f9c5fa66..5584d0bc 100644
--- a/discord/member.py
+++ b/discord/member.py
@@ -24,6 +24,8 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
+import asyncio
+
from .user import User
from .game import Game
from .permissions import Permissions
@@ -161,16 +163,19 @@ class Member:
def __hash__(self):
return hash(self._user.id)
- def _update(self, data, user):
- self._user.name = user['username']
- self._user.discriminator = user['discriminator']
- self._user.avatar = user['avatar']
- self._user.bot = user.get('bot', False)
+ def _update(self, data, user=None):
+ if user:
+ self._user.name = user['username']
+ self._user.discriminator = user['discriminator']
+ self._user.avatar = user['avatar']
+ self._user.bot = user.get('bot', False)
# the nickname change is optional,
# if it isn't in the payload then it didn't change
- if 'nick' in data:
+ try:
self.nick = data['nick']
+ except KeyError:
+ pass
# update the roles
self.roles = [self.guild.default_role]
@@ -283,3 +288,187 @@ class Member:
def voice(self):
"""Optional[:class:`VoiceState`]: Returns the member's current voice state."""
return self.guild._voice_state_for(self._user.id)
+
+ @asyncio.coroutine
+ def ban(self):
+ """|coro|
+
+ Bans this member. Equivalent to :meth:`Guild.ban`
+ """
+ yield from self.guild.ban(self)
+
+ @asyncio.coroutine
+ def unban(self):
+ """|coro|
+
+ Unbans this member. Equivalent to :meth:`Guild.unban`
+ """
+ yield from self.guild.unban(self)
+
+ @asyncio.coroutine
+ def kick(self):
+ """|coro|
+
+ Kicks this member. Equivalent to :meth:`Guild.kick`
+ """
+ yield from self.guild.kick(self)
+
+ @asyncio.coroutine
+ def edit(self, **fields):
+ """|coro|
+
+ Edits the member's data.
+
+ Depending on the parameter passed, this requires different permissions listed below:
+
+ +---------------+--------------------------------------+
+ | Parameter | Permission |
+ +---------------+--------------------------------------+
+ | nick | :attr:`Permissions.manage_nicknames` |
+ +---------------+--------------------------------------+
+ | mute | :attr:`Permissions.mute_members` |
+ +---------------+--------------------------------------+
+ | deafen | :attr:`Permissions.deafen_members` |
+ +---------------+--------------------------------------+
+ | roles | :attr:`Permissions.manage_roles` |
+ +---------------+--------------------------------------+
+ | voice_channel | :attr:`Permissions.move_members` |
+ +---------------+--------------------------------------+
+
+ All parameters are optional.
+
+ Parameters
+ -----------
+ nick: str
+ The member's new nickname. Use ``None`` to remove the nickname.
+ mute: bool
+ Indicates if the member should be guild muted or un-muted.
+ deafen: bool
+ Indicates if the member should be guild deafened or un-deafened.
+ roles: List[:class:`Roles`]
+ The member's new list of roles. This *replaces* the roles.
+ voice_channel: :class:`VoiceChannel`
+ The voice channel to move the member to.
+
+ Raises
+ -------
+ Forbidden
+ You do not have the proper permissions to the action requested.
+ HTTPException
+ The operation failed.
+ """
+ http = self._state.http
+ guild_id = self.guild.id
+ payload = {}
+
+ try:
+ nick = fields['nick']
+ except KeyError:
+ # nick not present so...
+ pass
+ else:
+ nick = nick if nick else ''
+ if self._state.self_id == self.id:
+ yield from http.change_my_nickname(guild_id, nick)
+ else:
+ payload['nick'] = nick
+
+ deafen = fields.get('deafen')
+ if deafen is not None:
+ payload['deaf'] = deafen
+
+ mute = fields.get('mute')
+ if mute is not None:
+ payload['mute'] = mute
+
+ try:
+ vc = fields['voice_channel']
+ except KeyError:
+ pass
+ else:
+ payload['channel_id'] = vc.id
+
+ try:
+ roles = fields['roles']
+ except KeyError:
+ pass
+ else:
+ payload['roles'] = tuple(r.id for r in roles)
+
+ yield from http.edit_member(guild_id, self.id, **payload)
+
+ # TODO: wait for WS event for modify-in-place behaviour
+
+ @asyncio.coroutine
+ def move_to(self, channel):
+ """|coro|
+
+ Moves a member to a new voice channel (they must be connected first).
+
+ You must have the :attr:`Permissions.move_members` permission to
+ use this.
+
+ This raises the same exceptions as :meth:`edit`.
+
+ Parameters
+ -----------
+ channel: :class:`VoiceChannel`
+ The new voice channel to move the member to.
+ """
+ yield from self.edit(voice_channel=channel)
+
+ @asyncio.coroutine
+ def add_roles(self, *roles):
+ """|coro|
+
+ Gives the member a number of :class:`Role`\s.
+
+ You must have the :attr:`Permissions.manage_roles` permission to
+ use this.
+
+ Parameters
+ -----------
+ \*roles
+ An argument list of :class:`Role`\s to give the member.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permissions to add these roles.
+ HTTPException
+ Adding roles failed.
+ """
+
+ new_roles = utils._unique(r for s in (self.roles[1:], roles) for r in s)
+ yield from self.edit(roles=new_roles)
+
+ @asyncio.coroutine
+ def remove_roles(self, *roles):
+ """|coro|
+
+ Removes :class:`Role`\s from this member.
+
+ You must have the :attr:`Permissions.manage_roles` permission to
+ use this.
+
+ Parameters
+ -----------
+ \*roles
+ An argument list of :class:`Role`\s to remove from the member.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permissions to remove these roles.
+ HTTPException
+ Removing the roles failed.
+ """
+
+ new_roles = self.roles[1:] # remove @everyone
+ for role in roles:
+ try:
+ new_roles.remove(role)
+ except ValueError:
+ pass
+
+ yield from self.edit(roles=new_roles)
diff --git a/discord/state.py b/discord/state.py
index 0cf0e955..e6e551fd 100644
--- a/discord/state.py
+++ b/discord/state.py
@@ -47,7 +47,7 @@ class ListenerType(enum.Enum):
chunk = 0
Listener = namedtuple('Listener', ('type', 'future', 'predicate'))
-StateContext = namedtuple('StateContext', 'try_insert_user http')
+StateContext = namedtuple('StateContext', 'try_insert_user http self_id')
log = logging.getLogger(__name__)
ReadyState = namedtuple('ReadyState', ('launch', 'guilds'))
@@ -60,7 +60,7 @@ class ConnectionState:
self.syncer = syncer
self.is_bot = None
self._listeners = []
- self.ctx = StateContext(try_insert_user=self.try_insert_user, http=http)
+ self.ctx = StateContext(try_insert_user=self.try_insert_user, http=http, self_id=None)
self.clear()
def clear(self):
@@ -220,6 +220,7 @@ class ConnectionState:
def parse_ready(self, data):
self._ready_state = ReadyState(launch=asyncio.Event(), guilds=[])
self.user = self.try_insert_user(data['user'])
+ self.ctx.self_id = self.user.id
guilds = data.get('guilds')
guilds = self._ready_state.guilds