Browse Source

Fix tracking settings, add settings.edit

pull/10109/head
dolfies 4 years ago
parent
commit
54c9c3588c
  1. 116
      discord/settings.py
  2. 121
      discord/tracking.py
  3. 118
      discord/user.py

116
discord/settings.py

@ -27,10 +27,10 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, TYPE_CHECKING
from .activity import create_activity
from .activity import create_settings_activity
from .enums import FriendFlags, NotificationLevel, Status, StickerAnimationOptions, Theme, UserContentFilter, try_enum
from .guild_folder import GuildFolder
from .utils import MISSING, parse_time, utcnow
from .utils import copy_doc, MISSING, parse_time, utcnow
if TYPE_CHECKING:
from .abc import GuildChannel
@ -134,6 +134,9 @@ class UserSettings:
def __repr__(self) -> str:
return '<Settings>'
def _get_guild(self, id: int) -> Optional[Guild]:
return self._state._get_guild(int(id))
def _update(self, data: Dict[str, Any]) -> None:
RAW_VALUES = {
'afk_timeout',
@ -166,6 +169,110 @@ class UserSettings:
else:
setattr(self, '_' + key, value)
async def edit(self, **kwargs) -> UserSettings:
"""|coro|
Edits the client user's settings.
.. versionchanged:: 2.0
The edit is no longer in-place, instead the newly edited settings are returned.
Parameters
----------
afk_timeout: :class:`int`
How long (in seconds) the user needs to be AFK until Discord
sends push notifications to your mobile device.
allow_accessibility_detection: :class:`bool`
Whether or not to allow Discord to track screen reader usage.
animate_emojis: :class:`bool`
Whether or not to animate emojis in the chat.
animate_stickers: :class:`StickerAnimationOptions`
Whether or not to animate stickers in the chat.
contact_sync_enabled: :class:`bool`
Whether or not to enable the contact sync on Discord mobile.
convert_emoticons: :class:`bool`
Whether or not to automatically convert emoticons into emojis.
e.g. :-) -> 😃
default_guilds_restricted: :class:`bool`
Whether or not to automatically disable DMs between you and
members of new guilds you join.
detect_platform_accounts: :class:`bool`
Whether or not to automatically detect accounts from services
like Steam and Blizzard when you open the Discord client.
developer_mode: :class:`bool`
Whether or not to enable developer mode.
disable_games_tab: :class:`bool`
Whether or not to disable the showing of the Games tab.
enable_tts_command: :class:`bool`
Whether or not to allow tts messages to be played/sent.
explicit_content_filter: :class:`UserContentFilter`
The filter for explicit content in all messages.
friend_source_flags: :class:`FriendFlags`
Who can add you as a friend.
gif_auto_play: :class:`bool`
Whether or not to automatically play gifs that are in the chat.
guild_positions: List[:class:`abc.Snowflake`]
A list of guilds in order of the guild/guild icons that are on
the left hand side of the UI.
inline_attachment_media: :class:`bool`
Whether or not to display attachments when they are uploaded in chat.
inline_embed_media: :class:`bool`
Whether or not to display videos and images from links posted in chat.
locale: :class:`str`
The :rfc:`3066` language identifier of the locale to use for the language
of the Discord client.
message_display_compact: :class:`bool`
Whether or not to use the compact Discord display mode.
native_phone_integration_enabled: :class:`bool`
Whether or not to enable the new Discord mobile phone number friend
requesting features.
render_embeds: :class:`bool`
Whether or not to render embeds that are sent in the chat.
render_reactions: :class:`bool`
Whether or not to render reactions that are added to messages.
restricted_guilds: List[:class:`abc.Snowflake`]
A list of guilds that you will not receive DMs from.
show_current_game: :class:`bool`
Whether or not to display the game that you are currently playing.
stream_notifications_enabled: :class:`bool`
Unknown.
theme: :class:`Theme`
The theme of the Discord UI.
timezone_offset: :class:`int`
The timezone offset to use.
view_nsfw_guilds: :class:`bool`
Whether or not to show NSFW guilds on iOS.
Raises
-------
HTTPException
Editing the settings failed.
Returns
-------
:class:`.UserSettings`
The client user's updated settings.
"""
return await self._state.user.edit_settings(**kwargs) # type: ignore
async def fetch_tracking(self) -> Tracking:
"""|coro|
Retrieves your :class:`Tracking` settings.
Raises
------
HTTPException
Retrieving the tracking settings failed.
Returns
-------
:class:`Tracking`
The tracking settings.
"""
data = await self._state.http.get_tracking()
return Tracking(state=self._state, data=data)
@property
def tracking(self) -> Optional[Tracking]:
"""Optional[:class:`Tracking`]: Returns your tracking settings if available."""
@ -179,7 +286,7 @@ class UserSettings:
@property
def custom_activity(self) -> Optional[CustomActivity]:
"""Optional[:class:`CustomActivity]: The custom activity you have set."""
return create_activity(getattr(self, '_custom_status', None))
return create_settings_activity(data=getattr(self, '_custom_status', None), state=self._state)
@property
def explicit_content_filter(self) -> UserContentFilter:
@ -221,9 +328,6 @@ class UserSettings:
""":class:`Theme`: The theme of the Discord UI."""
return try_enum(Theme, getattr(self, '_theme', 'dark')) # Sane default :)
def _get_guild(self, id: int) -> Optional[Guild]:
return self._state._get_guild(int(id))
class MuteConfig:
def __init__(self, muted: bool, config: Dict[str, str]) -> None:

121
discord/tracking.py

@ -27,9 +27,14 @@ from __future__ import annotations
from base64 import b64encode
import json
from typing import Any, Dict, Optional
from typing import Any, Dict, overload, Optional, TYPE_CHECKING
from .types.snowflake import Snowflake
from .utils import MISSING
if TYPE_CHECKING:
from .enums import ChannelType
from .types.snowflake import Snowflake
from .state import ConnectionState
__all__ = (
'ContextProperties',
@ -46,7 +51,7 @@ class ContextProperties: # Thank you Discord-S.C.U.M
__slots__ = ('_data', 'value')
def __init__(self, data) -> None:
self._data: Dict[str, any] = data
self._data: Dict[str, Snowflake] = data
self.value: str = self._encode_data(data)
def _encode_data(self, data) -> str:
@ -139,13 +144,6 @@ class ContextProperties: # Thank you Discord-S.C.U.M
}
return cls(data)
@classmethod
def _from_accept_invite_page_blank(cls) -> ContextProperties:
data = {
'location': 'Accept Invite Page'
}
return cls(data)
@classmethod
def _from_app(cls) -> ContextProperties:
data = {
@ -176,44 +174,63 @@ class ContextProperties: # Thank you Discord-S.C.U.M
@classmethod
def _from_accept_invite_page(
cls, *, guild_id: Snowflake, channel_id: Snowflake, channel_type: int
cls,
*,
guild_id: Snowflake = MISSING,
channel_id: Snowflake = MISSING,
channel_type: ChannelType = MISSING,
) -> ContextProperties:
data = {
data: Dict[str, Snowflake] = {
'location': 'Accept Invite Page',
'location_guild_id': str(guild_id),
'location_channel_id': str(channel_id),
'location_channel_type': int(channel_type)
}
if guild_id is not MISSING:
data['location_guild_id'] = str(guild_id)
if channel_id is not MISSING:
data['location_channel_id'] = str(channel_id)
if channel_type is not MISSING:
data['location_channel_type'] = int(channel_type)
return cls(data)
@classmethod
def _from_join_guild_popup(
cls, *, guild_id: Snowflake, channel_id: Snowflake, channel_type: int
cls,
*,
guild_id: Snowflake = MISSING,
channel_id: Snowflake = MISSING,
channel_type: ChannelType = MISSING,
) -> ContextProperties:
data = {
data: Dict[str, Snowflake] = {
'location': 'Join Guild',
'location_guild_id': str(guild_id),
'location_channel_id': str(channel_id),
'location_channel_type': int(channel_type)
}
if guild_id is not MISSING:
data['location_guild_id'] = str(guild_id)
if channel_id is not MISSING:
data['location_channel_id'] = str(channel_id)
if channel_type is not MISSING:
data['location_channel_type'] = int(channel_type)
return cls(data)
@classmethod
def _from_invite_embed(
cls, *, guild_id: Snowflake, channel_id: Snowflake, message_id: Snowflake, channel_type: int
cls,
*,
guild_id: Optional[Snowflake],
channel_id: Snowflake,
message_id: Snowflake,
channel_type: Optional[ChannelType],
) -> ContextProperties:
data = {
'location': 'Invite Button Embed',
'location_guild_id': str(guild_id),
'location_guild_id': str(guild_id) if guild_id else None,
'location_channel_id': str(channel_id),
'location_channel_type': int(channel_type),
'location_message_id': str(message_id)
'location_channel_type': int(channel_type) if channel_type else None,
'location_message_id': str(message_id),
}
return cls(data)
@property
def location(self) -> Optional[str]:
return self._data.get('location')
return self._data.get('location') # type: ignore
@property
def guild_id(self) -> Optional[int]:
@ -229,9 +246,7 @@ class ContextProperties: # Thank you Discord-S.C.U.M
@property
def channel_type(self) -> Optional[int]:
data = self._data.get('location_channel_type')
if data is not None:
return data
return self._data.get('location_channel_type') # type: ignore
@property
def message_id(self) -> Optional[int]:
@ -243,10 +258,10 @@ class ContextProperties: # Thank you Discord-S.C.U.M
return self.value is not None
def __str__(self) -> str:
return self._data.get('location', 'None')
return self._data.get('location', 'None') # type: ignore
def __repr__(self) -> str:
return '<ContextProperties location={0.location}>'.format(self)
return f'<ContextProperties location={self.location}>'
def __eq__(self, other) -> bool:
return isinstance(other, ContextProperties) and self.value == other.value
@ -262,7 +277,51 @@ class Tracking:
----------
personalization: :class:`bool`
Whether you have consented to your data being used for personalization.
usage_statistics: :class:`bool`
Whether you have consented to your data being used for usage statistics.
"""
def __init__(self, data: Dict[str, Any]): # TODO: rest of the values
__slots__ = ('_state', 'personalization', 'usage_statistics')
def __init__(self, *, data: Dict[str, Dict[str, bool]], state: ConnectionState) -> None:
self._state = state
self._update(data)
def __bool__(self) -> bool:
return any({self.personalization, self.usage_statistics})
def _update(self, data: Dict[str, Dict[str, bool]]):
self.personalization = data.get('personalization', {}).get('consented', False)
self.usage_statistics = data.get('usage_statistics', {}).get('consented', False)
@overload
async def edit(self) -> None:
...
@overload
async def edit(
self,
*,
personalization: bool = ...,
usage_statistics: bool = ...,
) -> None:
...
async def edit(self, **kwargs) -> None:
"""|coro|
Edits your tracking settings.
Parameters
----------
personalization: :class:`bool`
Whether you have consented to your data being used for personalization.
usage_statistics: :class:`bool`
Whether you have consented to your data being used for usage statistics.
"""
payload = {
'grant': [k for k, v in kwargs.items() if v is True],
'revoke': [k for k, v in kwargs.items() if v is False],
}
data = await self._state.http.edit_tracking(payload)
self._update(data)

118
discord/user.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from typing import Any, Dict, List, Optional, Type, TypeVar, TYPE_CHECKING, Union
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, TYPE_CHECKING, Union
import discord.abc
from .asset import Asset
@ -36,7 +36,7 @@ from .iterators import FakeCommandIterator
from .object import Object
from .relationship import Relationship
from .settings import UserSettings
from .utils import _bytes_to_base64_data, _get_as_snowflake, cached_slot_property, parse_time, snowflake_time, MISSING
from .utils import _bytes_to_base64_data, _get_as_snowflake, cached_slot_property, copy_doc, snowflake_time, MISSING
if TYPE_CHECKING:
from datetime import datetime
@ -478,6 +478,10 @@ class ClientUser(BaseUser):
Specifies the type of premium a user has (i.e. Nitro or Nitro Classic). Could be None if the user is not premium.
note: :class:`Note`
The user's note. Not pre-fetched.
nsfw_allowed: :class:`bool`
Specifies if the user should be allowed to access NSFW content.
.. versionadded:: 2.0
"""
__slots__ = (
@ -491,6 +495,7 @@ class ClientUser(BaseUser):
'note',
'premium',
'bio',
'nsfw_allowed',
)
if TYPE_CHECKING:
@ -503,6 +508,7 @@ class ClientUser(BaseUser):
premium: bool
premium_type: Optional[PremiumType]
bio: Optional[str]
nsfw_allowed: bool
def __init__(self, *, state: ConnectionState, data: UserPayload) -> None:
super().__init__(state=state, data=data)
@ -525,11 +531,7 @@ class ClientUser(BaseUser):
self.premium = data.get('premium', False)
self.premium_type = try_enum(PremiumType, data.get('premium_type', None))
self.bio = data.get('bio') or None
@property
def connected_accounts(self) -> Optional[List[dict]]:
"""Optional[List[:class:`dict`]]: Returns a list of all linked accounts for this user if available."""
return self._state._connected_accounts
self.nsfw_allowed = data.get('nsfw_allowed', False)
def get_relationship(self, user_id: int) -> Relationship:
"""Retrieves the :class:`Relationship` if applicable.
@ -581,7 +583,7 @@ class ClientUser(BaseUser):
accent_color: Colour = MISSING,
bio: Optional[str] = MISSING,
date_of_birth: datetime = MISSING,
) -> ClientUser:
) -> ClientUser:
"""|coro|
Edits the current profile of the client.
@ -642,7 +644,7 @@ class ClientUser(BaseUser):
:class:`ClientUser`
The newly edited client user.
"""
args: Dict[str, any] = {}
args: Dict[str, Any] = {}
if any(x is not MISSING for x in ('new_password', 'email', 'username', 'discriminator')):
if password is MISSING:
@ -700,7 +702,7 @@ class ClientUser(BaseUser):
else:
await http.change_hypesquad_house(house.value)
data = await http.edit_profile(**args)
data = await http.edit_profile(args)
try:
http._token(data['token'])
except KeyError:
@ -730,90 +732,8 @@ class ClientUser(BaseUser):
data = await self._state.http.get_settings()
return UserSettings(data=data, state=self._state)
@copy_doc(UserSettings.edit)
async def edit_settings(self, **kwargs) -> UserSettings: # TODO: I really wish I didn't have to do this...
"""|coro|
Edits the client user's settings.
.. versionchanged:: 2.0
The edit is no longer in-place, instead the newly edited settings are returned.
Parameters
----------
afk_timeout: :class:`int`
How long (in seconds) the user needs to be AFK until Discord
sends push notifications to your mobile device.
allow_accessibility_detection: :class:`bool`
Whether or not to allow Discord to track screen reader usage.
animate_emojis: :class:`bool`
Whether or not to animate emojis in the chat.
animate_stickers: :class:`StickerAnimationOptions`
Whether or not to animate stickers in the chat.
contact_sync_enabled: :class:`bool`
Whether or not to enable the contact sync on Discord mobile.
convert_emoticons: :class:`bool`
Whether or not to automatically convert emoticons into emojis.
e.g. :-) -> 😃
default_guilds_restricted: :class:`bool`
Whether or not to automatically disable DMs between you and
members of new guilds you join.
detect_platform_accounts: :class:`bool`
Whether or not to automatically detect accounts from services
like Steam and Blizzard when you open the Discord client.
developer_mode: :class:`bool`
Whether or not to enable developer mode.
disable_games_tab: :class:`bool`
Whether or not to disable the showing of the Games tab.
enable_tts_command: :class:`bool`
Whether or not to allow tts messages to be played/sent.
explicit_content_filter: :class:`UserContentFilter`
The filter for explicit content in all messages.
friend_source_flags: :class:`FriendFlags`
Who can add you as a friend.
gif_auto_play: :class:`bool`
Whether or not to automatically play gifs that are in the chat.
guild_positions: List[:class:`abc.Snowflake`]
A list of guilds in order of the guild/guild icons that are on
the left hand side of the UI.
inline_attachment_media: :class:`bool`
Whether or not to display attachments when they are uploaded in chat.
inline_embed_media: :class:`bool`
Whether or not to display videos and images from links posted in chat.
locale: :class:`str`
The :rfc:`3066` language identifier of the locale to use for the language
of the Discord client.
message_display_compact: :class:`bool`
Whether or not to use the compact Discord display mode.
native_phone_integration_enabled: :class:`bool`
Whether or not to enable the new Discord mobile phone number friend
requesting features.
render_embeds: :class:`bool`
Whether or not to render embeds that are sent in the chat.
render_reactions: :class:`bool`
Whether or not to render reactions that are added to messages.
restricted_guilds: List[:class:`abc.Snowflake`]
A list of guilds that you will not receive DMs from.
show_current_game: :class:`bool`
Whether or not to display the game that you are currently playing.
stream_notifications_enabled: :class:`bool`
Unknown.
theme: :class:`Theme`
The theme of the Discord UI.
timezone_offset: :class:`int`
The timezone offset to use.
view_nsfw_guilds: :class:`bool`
Whether or not to show NSFW guilds on iOS.
Raises
-------
HTTPException
Editing the settings failed.
Returns
-------
:class:`.UserSettings`
The client user's updated settings.
"""
payload = {}
content_filter = kwargs.pop('explicit_content_filter', None)
@ -842,6 +762,10 @@ class ClientUser(BaseUser):
if status:
payload['status'] = status.value
custom_activity = kwargs.pop('custom_activity', MISSING)
if custom_activity is not MISSING:
payload['custom_status'] = custom_activity and custom_activity.to_settings_dict()
theme = kwargs.pop('theme', None)
if theme:
payload['theme'] = theme.value
@ -895,7 +819,7 @@ class User(BaseUser, discord.abc.Connectable, discord.abc.Messageable):
self._stored: bool = False
def __repr__(self) -> str:
return f'<User id={self.id} name={self.name!r} discriminator={self.discriminator!r} bot={self.bot} system={self.system}>'
return f'<{self.__class__.__name__} id={self.id} name={self.name!r} discriminator={self.discriminator!r} bot={self.bot} system={self.system}>'
def __del__(self) -> None:
try:
@ -910,10 +834,10 @@ class User(BaseUser, discord.abc.Connectable, discord.abc.Messageable):
self._stored = False
return self
def _get_voice_client_key(self) -> Union[int, str]:
def _get_voice_client_key(self) -> Tuple[int, str]:
return self._state.self_id, 'self_id'
def _get_voice_state_pair(self) -> Union[int, int]:
def _get_voice_state_pair(self) -> Tuple[int, int]:
return self._state.self_id, self.dm_channel.id
async def _get_channel(self) -> DMChannel:
@ -974,7 +898,7 @@ class User(BaseUser, discord.abc.Connectable, discord.abc.Messageable):
*,
limit: Optional[int] = None,
command_ids: Optional[List[int]] = [],
**kwargs,
**_,
):
"""Returns an iterator that allows you to see what user commands are available to use on this user.

Loading…
Cancel
Save