Add support for relationships.
This commit is contained in:
		| @@ -23,6 +23,7 @@ from .game import Game | ||||
| from .emoji import Emoji, PartialEmoji | ||||
| from .channel import * | ||||
| from .guild import Guild | ||||
| from .relationship import Relationship | ||||
| from .member import Member, VoiceState | ||||
| from .message import Message | ||||
| from .errors import * | ||||
|   | ||||
| @@ -96,6 +96,12 @@ class DefaultAvatar(Enum): | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
|  | ||||
| class RelationshipType(Enum): | ||||
|     friend           = 1 | ||||
|     blocked          = 2 | ||||
|     incoming_request = 3 | ||||
|     outgoing_request = 4 | ||||
|  | ||||
| def try_enum(cls, val): | ||||
|     """A function that tries to turn the value into enum ``cls``. | ||||
|  | ||||
|   | ||||
| @@ -618,6 +618,29 @@ class HTTPClient: | ||||
|     def move_member(self, user_id, guild_id, channel_id): | ||||
|         return self.edit_member(guild_id=guild_id, user_id=user_id, channel_id=channel_id) | ||||
|  | ||||
|  | ||||
|     # Relationship related | ||||
|  | ||||
|     def remove_relationship(self, user_id): | ||||
|         r = Route('DELETE', '/users/@me/relationships/{user_id}', user_id=user_id) | ||||
|         return self.request(r) | ||||
|  | ||||
|     def add_relationship(self, user_id, type=None): | ||||
|         r = Route('PUT', '/users/@me/relationships/{user_id}', user_id=user_id) | ||||
|         payload = {} | ||||
|         if type is not None: | ||||
|             payload['type'] = type | ||||
|  | ||||
|         return self.request(r, json=payload) | ||||
|  | ||||
|     def send_friend_request(self, username, discriminator): | ||||
|         r = Route('POST', '/users/@me/relationships') | ||||
|         payload = { | ||||
|             'username': username, | ||||
|             'discriminator': int(discriminator) | ||||
|         } | ||||
|         return self.request(r, json=payload) | ||||
|  | ||||
|     # Misc | ||||
|  | ||||
|     def application_info(self): | ||||
|   | ||||
| @@ -25,11 +25,12 @@ DEALINGS IN THE SOFTWARE. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import itertools | ||||
|  | ||||
| import discord.abc | ||||
|  | ||||
| from . import utils | ||||
| from .user import BaseUser | ||||
| from .user import BaseUser, User | ||||
| from .game import Game | ||||
| from .permissions import Permissions | ||||
| from .enums import Status, ChannelType, try_enum | ||||
| @@ -74,7 +75,7 @@ class VoiceState: | ||||
|         return '<VoiceState self_mute={0.self_mute} self_deaf={0.self_deaf} channel={0.channel!r}>'.format(self) | ||||
|  | ||||
| def flatten_user(cls): | ||||
|     for attr, value in BaseUser.__dict__.items(): | ||||
|     for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()): | ||||
|         # ignore private/special methods | ||||
|         if attr.startswith('_'): | ||||
|             continue | ||||
|   | ||||
							
								
								
									
										82
									
								
								discord/relationship.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								discord/relationship.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2015-2016 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 .enums import RelationshipType, try_enum | ||||
|  | ||||
| import asyncio | ||||
|  | ||||
| class Relationship: | ||||
|     """Represents a relationship in Discord. | ||||
|  | ||||
|     A relationship is like a friendship, a person who is blocked, etc. | ||||
|     Only non-bot accounts can have relationships. | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     user: :class:`User` | ||||
|         The user you have the relationship with. | ||||
|     type: :class:`RelationshipType` | ||||
|         The type of relationship you have. | ||||
|     """ | ||||
|  | ||||
|     __slots__ = ('type', 'user', '_state') | ||||
|  | ||||
|     def __init__(self, *, state, data): | ||||
|         self._state = state | ||||
|         self.type = try_enum(RelationshipType, data['type']) | ||||
|         self.user = state.store_user(data['user']) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<Relationship user={0.user!r} type={0.type!r}>'.format(self) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def delete(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Deletes the relationship. | ||||
|  | ||||
|         Raises | ||||
|         ------ | ||||
|         HTTPException | ||||
|             Deleting the relationship failed. | ||||
|         """ | ||||
|  | ||||
|         yield from self._state.http.remove_relationship(self.user.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def accept(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Accepts the relationship request. e.g. accepting a | ||||
|         friend request. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         HTTPException | ||||
|             Accepting the relationship failed. | ||||
|         """ | ||||
|  | ||||
|         yield from self._state.http.add_relationship(self.user.id) | ||||
| @@ -30,6 +30,7 @@ from .game import Game | ||||
| from .emoji import Emoji, PartialEmoji | ||||
| from .reaction import Reaction | ||||
| from .message import Message | ||||
| from .relationship import Relationship | ||||
| from .channel import * | ||||
| from .member import Member | ||||
| from .role import Role | ||||
| @@ -247,6 +248,14 @@ class ConnectionState: | ||||
|             if not self.is_bot or guild.large: | ||||
|                 guilds.append(guild) | ||||
|  | ||||
|         for relationship in data.get('relationships', []): | ||||
|             try: | ||||
|                 r_id = int(relationship['id']) | ||||
|             except KeyError: | ||||
|                 continue | ||||
|             else: | ||||
|                 self.user._relationships[r_id] = Relationship(state=self, data=relationship) | ||||
|  | ||||
|         for pm in data.get('private_channels', []): | ||||
|             factory, _ = _channel_factory(pm['type']) | ||||
|             self._add_private_channel(factory(me=self.user, data=pm, state=self)) | ||||
| @@ -663,6 +672,25 @@ class ConnectionState: | ||||
|         if call is not None: | ||||
|             self.dispatch('call_remove', call) | ||||
|  | ||||
|     def parse_relationship_add(self, data): | ||||
|         key = int(data['id']) | ||||
|         old = self.user.get_relationship(key) | ||||
|         new = Relationship(state=self, data=data) | ||||
|         self.user._relationships[key] = new | ||||
|         if old is not None: | ||||
|             self.dispatch('relationship_update', old, new) | ||||
|         else: | ||||
|             self.dispatch('relationship_add', new) | ||||
|  | ||||
|     def parse_relationship_remove(self, data): | ||||
|         key = int(data['id']) | ||||
|         try: | ||||
|             old = self.user._relationships.pop(key) | ||||
|         except KeyError: | ||||
|             pass | ||||
|         else: | ||||
|             self.dispatch('relationship_remove', old) | ||||
|  | ||||
|     def _get_reaction_user(self, channel, user_id): | ||||
|         if isinstance(channel, DMChannel) and user_id == channel.recipient.id: | ||||
|             return channel.recipient | ||||
| @@ -761,7 +789,7 @@ class AutoShardedConnectionState(ConnectionState): | ||||
|         if not hasattr(self, '_ready_state'): | ||||
|             self._ready_state = ReadyState(launch=asyncio.Event(), guilds=[]) | ||||
|  | ||||
|         self.user = self.store_user(data['user']) | ||||
|         self.user = ClientUser(state=self, data=data['user']) | ||||
|  | ||||
|         guilds = self._ready_state.guilds | ||||
|         for guild_data in data['guilds']: | ||||
|   | ||||
| @@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE. | ||||
| """ | ||||
|  | ||||
| from .utils import snowflake_time, _bytes_to_base64_data | ||||
| from .enums import DefaultAvatar | ||||
| from .enums import DefaultAvatar, RelationshipType | ||||
| from .errors import ClientException | ||||
|  | ||||
| import discord.abc | ||||
| @@ -174,7 +174,7 @@ class ClientUser(BaseUser): | ||||
|     premium: bool | ||||
|         Specifies if the user is a premium user (e.g. has Discord Nitro). | ||||
|     """ | ||||
|     __slots__ = ('email', 'verified', 'mfa_enabled', 'premium') | ||||
|     __slots__ = ('email', 'verified', 'mfa_enabled', 'premium', '_relationships') | ||||
|  | ||||
|     def __init__(self, *, state, data): | ||||
|         super().__init__(state=state, data=data) | ||||
| @@ -182,12 +182,33 @@ class ClientUser(BaseUser): | ||||
|         self.email = data.get('email') | ||||
|         self.mfa_enabled = data.get('mfa_enabled', False) | ||||
|         self.premium = data.get('premium', False) | ||||
|         self._relationships = {} | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<ClientUser id={0.id} name={0.name!r} discriminator={0.discriminator!r}' \ | ||||
|                ' bot={0.bot} verified={0.verified} mfa_enabled={0.mfa_enabled}>'.format(self) | ||||
|  | ||||
|  | ||||
|     def get_relationship(self, user_id): | ||||
|         """Retrieves the :class:`Relationship` if applicable. | ||||
|  | ||||
|         Parameters | ||||
|         ----------- | ||||
|         user_id: int | ||||
|             The user ID to check if we have a relationship with them. | ||||
|  | ||||
|         Returns | ||||
|         -------- | ||||
|         Optional[:class:`Relationship`] | ||||
|             The relationship if available or ``None`` | ||||
|         """ | ||||
|         return self._relationships.get(user_id) | ||||
|  | ||||
|     @property | ||||
|     def relationships(self): | ||||
|         """Returns a list of :class:`Relationship` that the user has.""" | ||||
|         return list(self._relationships.values()) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def edit(self, **fields): | ||||
|         """|coro| | ||||
| @@ -337,3 +358,69 @@ class User(BaseUser, discord.abc.Messageable): | ||||
|         state = self._state | ||||
|         data = yield from state.http.start_private_message(self.id) | ||||
|         return state.add_dm_channel(data) | ||||
|  | ||||
|     @property | ||||
|     def relationship(self): | ||||
|         """Returns the :class:`Relationship` with this user if applicable, ``None`` otherwise.""" | ||||
|         return self._state.user.get_relationship(self.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def block(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Blocks the user. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             Not allowed to block this user. | ||||
|         HTTPException | ||||
|             Blocking the user failed. | ||||
|         """ | ||||
|  | ||||
|         yield from self._state.http.add_relationship(self.id, type=RelationshipType.blocked.value) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def unblock(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Unblocks the user. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             Not allowed to unblock this user. | ||||
|         HTTPException | ||||
|             Unblocking the user failed. | ||||
|         """ | ||||
|         yield from self._state.http.remove_relationship(self.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def remove_friend(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Removes the user as a friend. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             Not allowed to remove this user as a friend. | ||||
|         HTTPException | ||||
|             Removing the user as a friend failed. | ||||
|         """ | ||||
|         yield from self._state.http.remove_relationship(self.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def send_friend_request(self): | ||||
|         """|coro| | ||||
|  | ||||
|         Sends the user a friend request. | ||||
|  | ||||
|         Raises | ||||
|         ------- | ||||
|         Forbidden | ||||
|             Not allowed to send a friend request to the user. | ||||
|         HTTPException | ||||
|             Sending the friend request failed. | ||||
|         """ | ||||
|         yield from self._state.http.send_friend_request(username=self.name, discriminator=self.discriminator) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user