mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-19 15:36:02 +00:00
Add support for relationships.
This commit is contained in:
parent
e2acf7b2b9
commit
4c981ee631
@ -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)
|
||||
|
39
docs/api.rst
39
docs/api.rst
@ -409,6 +409,22 @@ to handle it, which defaults to print a traceback and ignore the exception.
|
||||
:param channel: The group that the user joined or left.
|
||||
:param user: The user that joined or left.
|
||||
|
||||
.. function:: on_relationship_add(relationship)
|
||||
on_relationship_remove(relationship)
|
||||
|
||||
Called when a :class:`Relationship` is added or removed from the
|
||||
:class:`ClientUser`.
|
||||
|
||||
:param relationship: The relationship that was added or removed.
|
||||
|
||||
.. function:: on_relationship_update(before, after)
|
||||
|
||||
Called when a :class:`Relationship` is updated, e.g. when you
|
||||
block a friend or a friendship is accepted.
|
||||
|
||||
:param before: The previous relationship status.
|
||||
:param after: The updated relationship status.
|
||||
|
||||
.. _discord-api-utils:
|
||||
|
||||
Utility Functions
|
||||
@ -607,6 +623,23 @@ All enumerations are subclasses of `enum`_.
|
||||
a presence a la :meth:`Client.change_presence`. When you receive a
|
||||
user's presence this will be :attr:`offline` instead.
|
||||
|
||||
.. class:: RelationshipType
|
||||
|
||||
Specifies the type of :class:`Relationship`
|
||||
|
||||
.. attribute:: friend
|
||||
|
||||
You are friends with this user.
|
||||
.. attribute:: blocked
|
||||
|
||||
You have blocked this user.
|
||||
.. attribute:: incoming_request
|
||||
|
||||
The user has sent you a friend request.
|
||||
.. attribute:: outgoing_request
|
||||
|
||||
You have sent a friend request to this user.
|
||||
|
||||
.. _discord_api_data:
|
||||
|
||||
Data Classes
|
||||
@ -652,6 +685,12 @@ ClientUser
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
Relationship
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: Relationship
|
||||
:members:
|
||||
|
||||
User
|
||||
~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user