You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

942 lines
34 KiB

"""
The MIT License (MIT)
Copyright (c) 2021-present Dolfies
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 __future__ import annotations
from datetime import datetime
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, overload
from .activity import create_settings_activity
from .enums import (
FriendFlags,
HighlightLevel,
Locale,
NotificationLevel,
Status,
StickerAnimationOptions,
Theme,
UserContentFilter,
try_enum,
)
from .guild_folder import GuildFolder
from .object import Object
from .utils import MISSING, _get_as_snowflake, parse_time, utcnow, find
if TYPE_CHECKING:
from .abc import GuildChannel, PrivateChannel
from .activity import CustomActivity
from .guild import Guild
from .state import ConnectionState
from .user import ClientUser
__all__ = (
'ChannelSettings',
'GuildSettings',
'UserSettings',
'TrackingSettings',
'EmailSettings',
'MuteConfig',
)
class UserSettings:
"""Represents the Discord client settings.
.. versionadded:: 1.9
Attributes
----------
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.
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.
gif_auto_play: :class:`bool`
Whether or not to automatically play gifs that are in the chat.
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.
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.
show_current_game: :class:`bool`
Whether or not to display the game that you are currently playing.
stream_notifications_enabled: :class:`bool`
Whether stream notifications for friends will be received.
timezone_offset: :class:`int`
The timezone offset to use.
view_nsfw_commands: :class:`bool`
Whether or not to show NSFW application commands.
.. versionadded:: 2.0
view_nsfw_guilds: :class:`bool`
Whether or not to show NSFW guilds on iOS.
"""
if TYPE_CHECKING: # Fuck me
afk_timeout: int
allow_accessibility_detection: bool
animate_emojis: bool
contact_sync_enabled: bool
convert_emoticons: bool
default_guilds_restricted: bool
detect_platform_accounts: bool
developer_mode: bool
disable_games_tab: bool
enable_tts_command: bool
gif_auto_play: bool
inline_attachment_media: bool
inline_embed_media: bool
message_display_compact: bool
native_phone_integration_enabled: bool
render_embeds: bool
render_reactions: bool
show_current_game: bool
stream_notifications_enabled: bool
timezone_offset: int
view_nsfw_commands: bool
view_nsfw_guilds: bool
def __init__(self, *, data, state: ConnectionState) -> None:
self._state = state
self._update(data)
def __repr__(self) -> str:
return '<Settings>'
def _get_guild(self, id: int) -> Guild:
return self._state._get_guild(int(id)) or Object(id=int(id)) # type: ignore # Lying for better developer UX
def _update(self, data: Dict[str, Any]) -> None:
RAW_VALUES = {
'afk_timeout',
'allow_accessibility_detection',
'animate_emojis',
'contact_sync_enabled',
'convert_emoticons',
'default_guilds_restricted',
'detect_platform_accounts',
'developer_mode',
'disable_games_tab',
'enable_tts_command',
'gif_auto_play',
'inline_attachment_media',
'inline_embed_media',
'message_display_compact',
'native_phone_integration_enabled',
'render_embeds',
'render_reactions',
'show_current_game',
'stream_notifications_enabled',
'timezone_offset',
'view_nsfw_commands',
'view_nsfw_guilds',
}
for key, value in data.items():
if key in RAW_VALUES:
setattr(self, key, value)
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
----------
activity_restricted_guilds: List[:class:`abc.Snowflake`]
A list of guilds that your current activity will not be shown in.
.. versionadded:: 2.0
activity_joining_restricted_guilds: List[:class:`abc.Snowflake`]
A list of guilds that will not be able to join your current activity.
.. versionadded:: 2.0
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:`Locale`
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.
passwordless: :class:`bool`
Whether the account is passwordless.
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`
Whether stream notifications for friends will be received.
theme: :class:`Theme`
The theme of the Discord UI.
timezone_offset: :class:`int`
The timezone offset to use.
view_nsfw_commands: :class:`bool`
Whether or not to show NSFW application commands.
.. versionadded:: 2.0
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 email_settings(self) -> EmailSettings:
"""|coro|
Retrieves your :class:`EmailSettings`.
.. versionadded:: 2.0
Raises
-------
HTTPException
Getting the email settings failed.
Returns
-------
:class:`.EmailSettings`
The email settings.
"""
data = await self._state.http.get_email_settings()
return EmailSettings(data=data, state=self._state)
async def fetch_tracking_settings(self) -> TrackingSettings:
"""|coro|
Retrieves your :class:`TrackingSettings`.
.. versionadded:: 2.0
Raises
------
HTTPException
Retrieving the tracking settings failed.
Returns
-------
:class:`Tracking`
The tracking settings.
"""
data = await self._state.http.get_tracking()
return TrackingSettings(state=self._state, data=data)
@property
def tracking_settings(self) -> Optional[TrackingSettings]:
"""Optional[:class:`TrackingSettings`]: Returns your tracking settings if available.
.. versionadded:: 2.0
"""
return self._state.consents
@property
def activity_restricted_guilds(self) -> List[Guild]:
"""List[:class:`abc.Snowflake`]: A list of guilds that your current activity will not be shown in.
.. versionadded:: 2.0
"""
return list(map(self._get_guild, getattr(self, '_activity_restricted_guild_ids', [])))
@property
def activity_joining_restricted_guilds(self) -> List[Guild]:
"""List[:class:`abc.Snowflake`]: A list of guilds that will not be able to join your current activity.
.. versionadded:: 2.0
"""
return list(map(self._get_guild, getattr(self, '_activity_joining_restricted_guild_ids', [])))
@property
def animate_stickers(self) -> StickerAnimationOptions:
""":class:`StickerAnimationOptions`: Whether or not to animate stickers in the chat."""
return try_enum(StickerAnimationOptions, getattr(self, '_animate_stickers', 0))
@property
def custom_activity(self) -> Optional[CustomActivity]:
"""Optional[:class:`CustomActivity`]: The set custom activity."""
return create_settings_activity(data=getattr(self, '_custom_status', None), state=self._state)
@property
def explicit_content_filter(self) -> UserContentFilter:
""":class:`UserContentFilter`: The filter for explicit content in all messages."""
return try_enum(UserContentFilter, getattr(self, '_explicit_content_filter', 0))
@property
def friend_source_flags(self) -> FriendFlags:
""":class:`FriendFlags`: Who can add you as a friend."""
return FriendFlags._from_dict(getattr(self, '_friend_source_flags', {'all': True}))
@property
def guild_folders(self) -> List[GuildFolder]:
"""List[:class:`GuildFolder`]: A list of guild folders."""
state = self._state
return [GuildFolder(data=folder, state=state) for folder in getattr(self, '_guild_folders', [])]
@property
def guild_positions(self) -> List[Guild]:
"""List[:class:`Guild`]: A list of guilds in order of the guild/guild icons that are on the left hand side of the UI."""
return list(map(self._get_guild, getattr(self, '_guild_positions', [])))
@property
def locale(self) -> Locale:
""":class:`Locale`: The :rfc:`3066` language identifier
of the locale to use for the language of the Discord client.
.. versionchanged:: 2.0
This now returns a :class:`Locale` object instead of a string.
"""
return try_enum(Locale, getattr(self, '_locale', 'en-US'))
@property
def passwordless(self) -> bool:
""":class:`bool`: Whether the account is passwordless."""
return getattr(self, '_passwordless', False)
@property
def restricted_guilds(self) -> List[Guild]:
"""List[:class:`Guild`]: A list of guilds that you will not receive DMs from."""
return list(filter(None, map(self._get_guild, getattr(self, '_restricted_guilds', []))))
@property
def status(self) -> Status:
"""Optional[:class:`Status`]: The configured status."""
return try_enum(Status, getattr(self, '_status', 'online'))
@property
def theme(self) -> Theme:
""":class:`Theme`: The theme of the Discord UI."""
return try_enum(Theme, getattr(self, '_theme', 'dark')) # Sane default :)
class MuteConfig:
"""An object representing an object's mute status.
.. versionadded:: 2.0
.. container:: operations
.. describe:: x == y
Checks if two items are muted.
.. describe:: x != y
Checks if two items are not muted.
.. describe:: str(x)
Returns the mute status as a string.
.. describe:: int(x)
Returns the mute status as an int.
Attributes
----------
muted: :class:`bool`
Indicates if the object is muted.
until: Optional[:class:`datetime.datetime`]
When the mute will expire.
"""
def __init__(self, muted: bool, config: Dict[str, str]) -> None:
until = parse_time(config.get('end_time'))
if until is not None:
if until <= utcnow():
muted = False
until = None
self.muted: bool = muted
self.until: Optional[datetime] = until
def __repr__(self) -> str:
return str(self.muted)
def __int__(self) -> int:
return int(self.muted)
def __bool__(self) -> bool:
return self.muted
def __eq__(self, other: object) -> bool:
return self.muted == bool(other)
def __ne__(self, other: object) -> bool:
return not self.muted == bool(other)
class ChannelSettings:
"""Represents a channel's notification settings.
.. versionadded:: 2.0
Attributes
----------
level: :class:`NotificationLevel`
The notification level for the channel.
muted: :class:`MuteConfig`
The mute configuration for the channel.
collapsed: :class:`bool`
Whether the channel is collapsed.
Only applicable to channels of type :attr:`ChannelType.category`.
"""
if TYPE_CHECKING:
_channel_id: int
level: NotificationLevel
muted: MuteConfig
collapsed: bool
def __init__(self, guild_id: Optional[int] = None, *, data: Dict[str, Any], state: ConnectionState) -> None:
self._guild_id = guild_id
self._state = state
self._update(data)
def __repr__(self) -> str:
return f'<ChannelSettings channel={self.channel} level={self.level} muted={self.muted} collapsed={self.collapsed}>'
def _update(self, data: Dict[str, Any]) -> None:
# We consider everything optional because this class can be constructed with no data
# to represent the default settings
self._channel_id = int(data['channel_id'])
self.collapsed = data.get('collapsed', False)
self.level = try_enum(NotificationLevel, data.get('message_notifications', 3))
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config') or {})
@property
def channel(self) -> Union[GuildChannel, PrivateChannel]:
"""Union[:class:`.abc.GuildChannel`, :class:`.abc.PrivateChannel`]: Returns the channel these settings are for."""
guild = self._state._get_guild(self._guild_id)
if guild:
channel = guild.get_channel(self._channel_id)
else:
channel = self._state._get_private_channel(self._channel_id)
if not channel:
channel = Object(id=self._channel_id)
return channel # type: ignore # Lying for better developer UX
async def edit(
self,
*,
muted_until: Optional[Union[bool, datetime]] = MISSING,
collapsed: bool = MISSING,
level: NotificationLevel = MISSING,
) -> ChannelSettings:
"""|coro|
Edits the channel's notification settings.
All parameters are optional.
Parameters
-----------
muted_until: Optional[Union[:class:`datetime.datetime`, :class:`bool`]]
The date this channel's mute should expire.
This can be ``True`` to mute indefinitely, or ``False``/``None`` to unmute.
This must be a timezone-aware datetime object. Consider using :func:`utils.utcnow`.
collapsed: :class:`bool`
Indicates if the channel should be collapsed or not.
Only applicable to channels of type :attr:`ChannelType.category`.
level: :class:`NotificationLevel`
Determines what level of notifications you receive for the channel.
Raises
-------
HTTPException
Editing the settings failed.
Returns
--------
:class:`ChannelSettings`
The new notification settings.
"""
state = self._state
guild_id = self._guild_id
channel_id = self._channel_id
payload = {}
if muted_until is not MISSING:
if not muted_until:
payload['muted'] = False
else:
payload['muted'] = True
if muted_until is True:
payload['mute_config'] = {'selected_time_window': -1, 'end_time': None}
else:
if muted_until.tzinfo is None:
raise TypeError(
'muted_until must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
)
mute_config = {
'selected_time_window': (muted_until - utcnow()).total_seconds(),
'end_time': muted_until.isoformat(),
}
payload['mute_config'] = mute_config
if collapsed is not MISSING:
payload['collapsed'] = collapsed
if level is not MISSING:
payload['message_notifications'] = level.value
fields = {'channel_overrides': {str(channel_id): payload}}
data = await state.http.edit_guild_settings(guild_id or '@me', fields)
override = find(lambda x: x.get('channel_id') == str(channel_id), data['channel_overrides']) or {
'channel_id': channel_id
}
return ChannelSettings(guild_id, data=override, state=state)
class GuildSettings:
"""Represents a guild's notification settings.
.. versionadded:: 2.0
Attributes
----------
level: :class:`NotificationLevel`
The notification level for the guild.
muted: :class:`MuteConfig`
The mute configuration for the guild.
suppress_everyone: :class:`bool`
Whether to suppress @everyone/@here notifications.
suppress_roles: :class:`bool`
Whether to suppress role notifications.
hide_muted_channels: :class:`bool`
Whether to hide muted channels.
mobile_push: :class:`bool`
Whether to enable mobile push notifications.
mute_scheduled_events: :class:`bool`
Whether to mute scheduled events.
notify_highlights: :class:`HighlightLevel`
Whether to include highlights in notifications.
version: :class:`int`
The version of the guild's settings.
"""
if TYPE_CHECKING:
_channel_overrides: Dict[int, ChannelSettings]
_guild_id: Optional[int]
level: NotificationLevel
muted: MuteConfig
suppress_everyone: bool
suppress_roles: bool
hide_muted_channels: bool
mobile_push: bool
mute_scheduled_events: bool
notify_highlights: HighlightLevel
version: int
def __init__(self, *, data: Dict[str, Any], state: ConnectionState) -> None:
self._state = state
self._update(data)
def __repr__(self) -> str:
return f'<GuildSettings guild={self.guild!r} level={self.level} muted={self.muted} suppress_everyone={self.suppress_everyone} suppress_roles={self.suppress_roles}>'
def _update(self, data: Dict[str, Any]) -> None:
# We consider everything optional because this class can be constructed with no data
# to represent the default settings
self._guild_id = guild_id = _get_as_snowflake(data, 'guild_id')
self.level = try_enum(NotificationLevel, data.get('message_notifications', 3))
self.suppress_everyone = data.get('suppress_everyone', False)
self.suppress_roles = data.get('suppress_roles', False)
self.hide_muted_channels = data.get('hide_muted_channels', False)
self.mobile_push = data.get('mobile_push', True)
self.mute_scheduled_events = data.get('mute_scheduled_events', False)
self.notify_highlights = try_enum(HighlightLevel, data.get('notify_highlights', 0))
self.version = data.get('version', -1) # Overriden by real data
self.muted = MuteConfig(data.get('muted', False), data.get('mute_config') or {})
self._channel_overrides = overrides = {}
state = self._state
for override in data.get('channel_overrides', []):
channel_id = int(override['channel_id'])
overrides[channel_id] = ChannelSettings(guild_id, data=override, state=state)
@property
def guild(self) -> Union[Guild, ClientUser]:
"""Union[:class:`Guild`, :class:`ClientUser`]: Returns the guild that these settings are for.
If the returned value is a :class:`ClientUser` then the settings are for the user's private channels.
"""
if self._guild_id:
return self._state._get_guild(self._guild_id) or Object(id=self._guild_id) # type: ignore # Lying for better developer UX
return self._state.user # type: ignore # Should always be present here
@property
def channel_overrides(self) -> List[ChannelSettings]:
"""List[:class:`ChannelSettings`: Returns a list of all the overrided channel notification settings."""
return list(self._channel_overrides.values())
async def edit(
self,
muted_until: Optional[Union[bool, datetime]] = MISSING,
level: NotificationLevel = MISSING,
suppress_everyone: bool = MISSING,
suppress_roles: bool = MISSING,
mobile_push: bool = MISSING,
hide_muted_channels: bool = MISSING,
mute_scheduled_events: bool = MISSING,
notify_highlights: HighlightLevel = MISSING,
) -> Optional[GuildSettings]:
"""|coro|
Edits the guild's notification settings.
All parameters are optional.
Parameters
-----------
muted_until: Optional[Union[:class:`datetime.datetime`, :class:`bool`]]
The date this guild's mute should expire.
This can be ``True`` to mute indefinitely, or ``False``/``None`` to unmute.
This must be a timezone-aware datetime object. Consider using :func:`utils.utcnow`.
level: :class:`NotificationLevel`
Determines what level of notifications you receive for the guild.
suppress_everyone: :class:`bool`
Indicates if @everyone mentions should be suppressed for the guild.
suppress_roles: :class:`bool`
Indicates if role mentions should be suppressed for the guild.
mobile_push: :class:`bool`
Indicates if push notifications should be sent to mobile devices for this guild.
hide_muted_channels: :class:`bool`
Indicates if channels that are muted should be hidden from the sidebar.
mute_scheduled_events: :class:`bool`
Indicates if scheduled events should be muted.
notify_highlights: :class:`HighlightLevel`
Indicates if highlights should be included in notifications.
Raises
-------
HTTPException
Editing the settings failed.
Returns
--------
:class:`GuildSettings`
The new notification settings.
"""
payload = {}
if muted_until is not MISSING:
if not muted_until:
payload['muted'] = False
else:
payload['muted'] = True
if muted_until is True:
payload['mute_config'] = {'selected_time_window': -1, 'end_time': None}
else:
if muted_until.tzinfo is None:
raise TypeError(
'muted_until must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
)
mute_config = {
'selected_time_window': (muted_until - utcnow()).total_seconds(),
'end_time': muted_until.isoformat(),
}
payload['mute_config'] = mute_config
if level is not MISSING:
payload['message_notifications'] = level.value
if suppress_everyone is not MISSING:
payload['suppress_everyone'] = suppress_everyone
if suppress_roles is not MISSING:
payload['suppress_roles'] = suppress_roles
if mobile_push is not MISSING:
payload['mobile_push'] = mobile_push
if hide_muted_channels is not MISSING:
payload['hide_muted_channels'] = hide_muted_channels
if mute_scheduled_events is not MISSING:
payload['mute_scheduled_events'] = mute_scheduled_events
if notify_highlights is not MISSING:
payload['notify_highlights'] = notify_highlights.value
data = await self._state.http.edit_guild_settings(self._guild_id or '@me', payload)
return GuildSettings(data=data, state=self._state)
class TrackingSettings:
"""Represents your Discord tracking settings.
.. versionadded:: 2.0
.. container:: operations
.. describe:: bool(x)
Checks if any tracking settings are enabled.
Attributes
----------
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.
"""
__slots__ = ('_state', 'personalization', 'usage_statistics')
def __init__(self, *, data: Dict[str, Dict[str, bool]], state: ConnectionState) -> None:
self._state = state
self._update(data)
def __repr__(self) -> str:
return f'<TrackingSettings personalization={self.personalization} usage_statistics={self.usage_statistics}>'
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)
class EmailSettings:
"""Represents email communication preferences.
.. versionadded:: 2.0
Attributes
----------
initialized: :class:`bool`
Whether the email communication preferences have been initialized.
communication: :class:`bool`
Whether you want to receive emails for missed calls/messages.
social: :class:`bool`
Whether you want to receive emails for friend requests/suggestions or events.
recommendations_and_events: :class:`bool`
Whether you want to receive emails for recommended servers and events.
tips: :class:`bool`
Whether you want to receive emails for advice and tricks.
updates_and_announcements: :class:`bool`
Whether you want to receive emails for updates and new features.
"""
__slots__ = (
'_state',
'initialized',
'communication',
'social',
'recommendations_and_events',
'tips',
'updates_and_announcements',
)
def __init__(self, *, data: dict, state: ConnectionState):
self._state = state
self._update(data)
def __repr__(self) -> str:
return f'<EmailSettings initialized={self.initialized}>'
def _update(self, data: dict):
self.initialized = data.get('initialized', False)
categories = data.get('categories', {})
self.communication = categories.get('communication', False)
self.social = categories.get('social', False)
self.recommendations_and_events = categories.get('recommendations_and_events', False)
self.tips = categories.get('tips', False)
self.updates_and_announcements = categories.get('updates_and_announcements', False)
@overload
async def edit(self) -> None:
...
@overload
async def edit(
self,
*,
communication: bool = MISSING,
social: bool = MISSING,
recommendations_and_events: bool = MISSING,
tips: bool = MISSING,
updates_and_announcements: bool = MISSING,
) -> None:
...
async def edit(self, **kwargs) -> None:
"""|coro|
Edits the email settings.
All parameters are optional.
Parameters
-----------
communication: :class:`bool`
Indicates if you want to receive communication emails.
social: :class:`bool`
Indicates if you want to receive social emails.
recommendations_and_events: :class:`bool`
Indicates if you want to receive recommendations and events emails.
tips: :class:`bool`
Indicates if you want to receive tips emails.
updates_and_announcements: :class:`bool`
Indicates if you want to receive updates and announcements emails.
Raises
-------
HTTPException
Editing the settings failed.
"""
payload = {}
# It seems that initialized is settable, but it doesn't do anything
# So we support just in case but leave it undocumented
initialized = kwargs.pop('initialized', None)
if initialized is not None:
payload['initialized'] = initialized
if kwargs:
payload['categories'] = kwargs
data = await self._state.http.edit_email_settings(**payload)
self._update(data)