|
|
@ -23,28 +23,25 @@ DEALINGS IN THE SOFTWARE. |
|
|
|
""" |
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
|
|
|
|
import os |
|
|
|
from typing import TYPE_CHECKING, Iterable, Optional, Set, List, Union |
|
|
|
|
|
|
|
from discord.utils import MISSING |
|
|
|
|
|
|
|
from . import utils |
|
|
|
from .mixins import Hashable |
|
|
|
from .enums import OnboardingMode, OnboardingPromptType, try_enum |
|
|
|
from .partial_emoji import PartialEmoji |
|
|
|
from .utils import cached_slot_property, MISSING |
|
|
|
from . import utils |
|
|
|
|
|
|
|
__all__ = ( |
|
|
|
'Onboarding', |
|
|
|
'PartialOnboardingPrompt', |
|
|
|
'OnboardingPrompt', |
|
|
|
'OnboardingPromptOption', |
|
|
|
'PartialOnboardingPromptOption', |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING: |
|
|
|
from .abc import GuildChannel |
|
|
|
from typing_extensions import Self |
|
|
|
|
|
|
|
from .abc import GuildChannel, Snowflake |
|
|
|
from .emoji import Emoji |
|
|
|
from .guild import Guild |
|
|
|
from .partial_emoji import PartialEmoji |
|
|
@ -53,19 +50,23 @@ if TYPE_CHECKING: |
|
|
|
from .types.onboarding import ( |
|
|
|
Prompt as PromptPayload, |
|
|
|
PromptOption as PromptOptionPayload, |
|
|
|
CreatePromptOption as CreatePromptOptionPayload, |
|
|
|
Onboarding as OnboardingPayload, |
|
|
|
) |
|
|
|
from .state import ConnectionState |
|
|
|
|
|
|
|
|
|
|
|
class PartialOnboardingPromptOption: |
|
|
|
"""Represents a partial onboarding prompt option, these are used in the creation |
|
|
|
of an :class:`OnboardingPrompt` via :meth:`Guild.edit_onboarding`. |
|
|
|
class OnboardingPromptOption(Hashable): |
|
|
|
"""Represents a onboarding prompt option. |
|
|
|
|
|
|
|
This can be manually created for :meth:`Guild.edit_onboarding`. |
|
|
|
|
|
|
|
.. versionadded:: 2.6 |
|
|
|
|
|
|
|
Attributes |
|
|
|
----------- |
|
|
|
id: :class:`int` |
|
|
|
The ID of this prompt option. If this was manually created then the ID will be ``0``. |
|
|
|
title: :class:`str` |
|
|
|
The title of this prompt option. |
|
|
|
description: Optional[:class:`str`] |
|
|
@ -73,126 +74,123 @@ class PartialOnboardingPromptOption: |
|
|
|
emoji: Optional[Union[:class:`Emoji`, :class:`PartialEmoji`, :class:`str`]] |
|
|
|
The emoji tied to this option. May be a custom emoji, or a unicode emoji. |
|
|
|
channel_ids: Set[:class:`int`] |
|
|
|
The IDs of the channels that will be made visible if this option is selected. |
|
|
|
The IDs of the channels the user will be added to if this option is selected. |
|
|
|
role_ids: Set[:class:`int`] |
|
|
|
The IDs of the roles given to the user if this option is selected. |
|
|
|
The IDs of the roles the user will be given if this option is selected. |
|
|
|
""" |
|
|
|
|
|
|
|
__slots__ = ( |
|
|
|
'title', |
|
|
|
'emoji', |
|
|
|
'description', |
|
|
|
'id', |
|
|
|
'channel_ids', |
|
|
|
'role_ids', |
|
|
|
'_guild', |
|
|
|
'_cs_channels', |
|
|
|
'_cs_roles', |
|
|
|
) |
|
|
|
|
|
|
|
def __init__( |
|
|
|
self, |
|
|
|
*, |
|
|
|
title: str, |
|
|
|
emoji: Union[Emoji, PartialEmoji, str] = MISSING, |
|
|
|
description: Optional[str] = None, |
|
|
|
channel_ids: Iterable[int] = MISSING, |
|
|
|
role_ids: Iterable[int] = MISSING, |
|
|
|
channels: Iterable[Union[Snowflake, int]] = MISSING, |
|
|
|
roles: Iterable[Union[Snowflake, int]] = MISSING, |
|
|
|
) -> None: |
|
|
|
self.id: int = 0 |
|
|
|
self.title: str = title |
|
|
|
self.description: Optional[str] = description |
|
|
|
self.emoji: Union[PartialEmoji, Emoji, str] = emoji |
|
|
|
self.channel_ids: Set[int] = set(channel_ids or []) |
|
|
|
self.role_ids: Set[int] = set(role_ids or []) |
|
|
|
|
|
|
|
def to_dict(self, *, id: int = MISSING) -> PromptOptionPayload: |
|
|
|
if self.emoji is not MISSING: |
|
|
|
if isinstance(self.emoji, str): |
|
|
|
emoji_payload = {"emoji_name": self.emoji} |
|
|
|
else: |
|
|
|
emoji_payload = { |
|
|
|
"emoji_id": self.emoji.id, |
|
|
|
"emoji_name": self.emoji.name, |
|
|
|
"emoji_animated": self.emoji.animated, |
|
|
|
} |
|
|
|
else: |
|
|
|
emoji_payload = {} |
|
|
|
|
|
|
|
return { |
|
|
|
'id': id or os.urandom(16).hex(), |
|
|
|
'title': self.title, |
|
|
|
'description': self.description, |
|
|
|
'channel_ids': list(self.channel_ids), |
|
|
|
'role_ids': list(self.role_ids), |
|
|
|
**emoji_payload, |
|
|
|
} # type: ignore |
|
|
|
|
|
|
|
|
|
|
|
class OnboardingPromptOption(PartialOnboardingPromptOption, Hashable): |
|
|
|
"""Represents an onboarding prompt option. |
|
|
|
|
|
|
|
.. versionadded:: 2.6 |
|
|
|
self.emoji: Optional[Union[Emoji, PartialEmoji]] = ( |
|
|
|
PartialEmoji.from_str(emoji) if isinstance(emoji, str) else emoji if emoji is not MISSING else None |
|
|
|
) |
|
|
|
|
|
|
|
Attributes |
|
|
|
----------- |
|
|
|
id: :class:`int` |
|
|
|
The ID of this prompt option. |
|
|
|
guild: :class:`Guild` |
|
|
|
The guild the onboarding prompt option is related to. |
|
|
|
title: :class:`str` |
|
|
|
The title of this prompt option. |
|
|
|
description: Optional[:class:`str`] |
|
|
|
The description of this prompt option. |
|
|
|
emoji: Optional[Union[:class:`Emoji`, :class:`PartialEmoji`, :class:`str`]] |
|
|
|
The emoji tied to this option. May be a custom emoji, or a unicode emoji. |
|
|
|
channel_ids: Set[:class:`int`] |
|
|
|
The IDs of the channels that will be made visible if this option is selected. |
|
|
|
role_ids: Set[:class:`int`] |
|
|
|
The IDs of the roles given to the user if this option is selected. |
|
|
|
""" |
|
|
|
self.channel_ids: Set[int] = ( |
|
|
|
{c.id if not isinstance(c, int) else c for c in channels} if channels is not MISSING else set() |
|
|
|
) |
|
|
|
self.role_ids: Set[int] = {c.id if not isinstance(c, int) else c for c in roles} if roles is not MISSING else set() |
|
|
|
self._guild: Optional[Guild] = None |
|
|
|
|
|
|
|
__slots__ = ( |
|
|
|
'_state', |
|
|
|
'_cs_channels', |
|
|
|
'_cs_roles', |
|
|
|
'guild', |
|
|
|
'id', |
|
|
|
) |
|
|
|
def __repr__(self) -> str: |
|
|
|
return f'<OnboardingPromptOption id={self.id} title={self.title}>' |
|
|
|
|
|
|
|
def __init__(self, *, data: PromptOptionPayload, state: ConnectionState, guild: Guild) -> None: |
|
|
|
self._state: ConnectionState = state |
|
|
|
self.guild: Guild = guild |
|
|
|
self.id: int = int(data['id']) |
|
|
|
super().__init__( |
|
|
|
@classmethod |
|
|
|
def from_dict(cls, *, data: PromptOptionPayload, state: ConnectionState, guild: Guild) -> Self: |
|
|
|
instance = cls( |
|
|
|
title=data['title'], |
|
|
|
description=data['description'], |
|
|
|
emoji=self._state.get_emoji_from_partial_payload(data['emoji']), |
|
|
|
channel_ids=[int(id) for id in data['channel_ids']], |
|
|
|
role_ids=[int(id) for id in data['role_ids']], |
|
|
|
emoji=state.get_emoji_from_partial_payload(data['emoji']) if 'emoji' in data else MISSING, |
|
|
|
channels=[int(id) for id in data['channel_ids']], |
|
|
|
roles=[int(id) for id in data['role_ids']], |
|
|
|
) |
|
|
|
instance._guild = guild |
|
|
|
instance.id = int(data['id']) |
|
|
|
return instance |
|
|
|
|
|
|
|
def __repr__(self) -> str: |
|
|
|
return f'<OnboardingPromptOption id={self.id} title={self.title}>' |
|
|
|
def to_dict( |
|
|
|
self, |
|
|
|
) -> CreatePromptOptionPayload: |
|
|
|
res: CreatePromptOptionPayload = { |
|
|
|
'title': self.title, |
|
|
|
'description': self.description, |
|
|
|
'channel_ids': list(self.channel_ids), |
|
|
|
'role_ids': list(self.role_ids), |
|
|
|
} |
|
|
|
if self.emoji: |
|
|
|
res.update((self.emoji._to_partial())._to_onboarding_prompt_option_payload()) # type: ignore |
|
|
|
return res |
|
|
|
|
|
|
|
@property |
|
|
|
def guild(self) -> Guild: |
|
|
|
""":class:`Guild`: The guild this prompt option is related to. |
|
|
|
|
|
|
|
Raises |
|
|
|
------- |
|
|
|
ValueError |
|
|
|
If this prompt option is manually created. |
|
|
|
""" |
|
|
|
if self._guild is None: |
|
|
|
raise ValueError('This prompt option is manually created therefore has no guild.') |
|
|
|
return self._guild |
|
|
|
|
|
|
|
@cached_slot_property('_cs_channels') |
|
|
|
def channels(self) -> List[Union[GuildChannel, Thread]]: |
|
|
|
"""List[Union[:class:`abc.GuildChannel`, :class:`Thread`]]: The list of channels which will be made visible if this option is selected.""" |
|
|
|
"""List[Union[:class:`abc.GuildChannel`, :class:`Thread`]]: The list of channels which will be made visible if this option is selected. |
|
|
|
|
|
|
|
Raises |
|
|
|
------- |
|
|
|
ValueError |
|
|
|
IF the prompt option is manually created, therefore has no guild. |
|
|
|
""" |
|
|
|
it = filter(None, map(self.guild._resolve_channel, self.channel_ids)) |
|
|
|
return utils._unique(it) |
|
|
|
|
|
|
|
@cached_slot_property('_cs_roles') |
|
|
|
def roles(self) -> List[Role]: |
|
|
|
"""List[:class:`Role`]: The list of roles given to the user if this option is selected.""" |
|
|
|
"""List[:class:`Role`]: The list of roles given to the user if this option is selected. |
|
|
|
|
|
|
|
Raises |
|
|
|
------- |
|
|
|
ValueError |
|
|
|
If the prompt option is manually created, therefore has no guild. |
|
|
|
""" |
|
|
|
it = filter(None, map(self.guild.get_role, self.role_ids)) |
|
|
|
return utils._unique(it) |
|
|
|
|
|
|
|
def to_dict(self) -> PromptOptionPayload: |
|
|
|
return super().to_dict(id=self.id) |
|
|
|
|
|
|
|
class OnboardingPrompt: |
|
|
|
"""Represents a onboarding prompt. |
|
|
|
|
|
|
|
class PartialOnboardingPrompt: |
|
|
|
"""Represents a partial onboarding prompt, these are used in the creation |
|
|
|
of an :class:`Onboarding` via :meth:`Guild.edit_onboarding`. |
|
|
|
This can be manually created for :meth:`Guild.edit_onboarding`. |
|
|
|
|
|
|
|
.. versionadded:: 2.6 |
|
|
|
|
|
|
|
Attributes |
|
|
|
----------- |
|
|
|
id: :class:`int` |
|
|
|
The ID of this prompt. If this was manually created then the ID will be ``0 |
|
|
|
type: :class:`OnboardingPromptType` |
|
|
|
The type of this prompt. |
|
|
|
title: :class:`str` |
|
|
@ -208,12 +206,14 @@ class PartialOnboardingPrompt: |
|
|
|
""" |
|
|
|
|
|
|
|
__slots__ = ( |
|
|
|
'id', |
|
|
|
'type', |
|
|
|
'title', |
|
|
|
'options', |
|
|
|
'single_select', |
|
|
|
'required', |
|
|
|
'in_onboarding', |
|
|
|
'_guild', |
|
|
|
) |
|
|
|
|
|
|
|
def __init__( |
|
|
@ -221,18 +221,40 @@ class PartialOnboardingPrompt: |
|
|
|
*, |
|
|
|
type: OnboardingPromptType, |
|
|
|
title: str, |
|
|
|
options: List[PartialOnboardingPromptOption], |
|
|
|
options: List[OnboardingPromptOption], |
|
|
|
single_select: bool = True, |
|
|
|
required: bool = True, |
|
|
|
in_onboarding: bool = True, |
|
|
|
) -> None: |
|
|
|
self.type: OnboardingPromptType = try_enum(OnboardingPromptType, type) |
|
|
|
self.id: int = 0 |
|
|
|
self.type: OnboardingPromptType = type |
|
|
|
self.title: str = title |
|
|
|
self.options: List[PartialOnboardingPromptOption] = options |
|
|
|
self.options: List[OnboardingPromptOption] = options |
|
|
|
self.single_select: bool = single_select |
|
|
|
self.required: bool = required |
|
|
|
self.in_onboarding: bool = in_onboarding |
|
|
|
|
|
|
|
self._guild: Optional[Guild] = None |
|
|
|
|
|
|
|
def __repr__(self) -> str: |
|
|
|
return f'<OnboardingPrompt id={self.id} title={self.title}, type={self.type}>' |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def from_dict(cls, *, data: PromptPayload, state: ConnectionState, guild: Guild) -> Self: |
|
|
|
instance = cls( |
|
|
|
type=try_enum(OnboardingPromptType, data['type']), |
|
|
|
title=data['title'], |
|
|
|
options=[ |
|
|
|
OnboardingPromptOption.from_dict(data=option_data, state=state, guild=guild) # type: ignore |
|
|
|
for option_data in data['options'] |
|
|
|
], |
|
|
|
single_select=data['single_select'], |
|
|
|
required=data['required'], |
|
|
|
in_onboarding=data['in_onboarding'], |
|
|
|
) |
|
|
|
instance.id = int(data['id']) |
|
|
|
return instance |
|
|
|
|
|
|
|
def to_dict(self, *, id: int) -> PromptPayload: |
|
|
|
return { |
|
|
|
'id': id, |
|
|
@ -244,61 +266,22 @@ class PartialOnboardingPrompt: |
|
|
|
'in_onboarding': self.in_onboarding, |
|
|
|
} |
|
|
|
|
|
|
|
@property |
|
|
|
def guild(self) -> Guild: |
|
|
|
""":class:`Guild`: The guild this prompt is related to. |
|
|
|
|
|
|
|
class OnboardingPrompt(PartialOnboardingPrompt, Hashable): |
|
|
|
"""Represents an onboarding prompt. |
|
|
|
|
|
|
|
.. versionadded:: 2.6 |
|
|
|
|
|
|
|
Attributes |
|
|
|
----------- |
|
|
|
id: :class:`int` |
|
|
|
The ID of this prompt. |
|
|
|
guild: :class:`Guild` |
|
|
|
The guild the onboarding prompt is related to. |
|
|
|
type: :class:`OnboardingPromptType` |
|
|
|
The type of onboarding prompt. |
|
|
|
title: :class:`str` |
|
|
|
The title of this prompt. |
|
|
|
options: List[:class:`OnboardingPromptOption`] |
|
|
|
The list of options the user can select from. |
|
|
|
single_select: :class:`bool` |
|
|
|
Whether only one option can be selected. |
|
|
|
required: :class:`bool` |
|
|
|
Whether this prompt is required to complete the onboarding flow. |
|
|
|
in_onboarding: :class:`bool` |
|
|
|
Whether this prompt is part of the onboarding flow. |
|
|
|
""" |
|
|
|
|
|
|
|
options: List[OnboardingPromptOption] |
|
|
|
|
|
|
|
__slots__ = ( |
|
|
|
'_state', |
|
|
|
'guild', |
|
|
|
'id', |
|
|
|
'title', |
|
|
|
'options', |
|
|
|
'single_select', |
|
|
|
'required', |
|
|
|
'in_onboarding', |
|
|
|
'type', |
|
|
|
) |
|
|
|
Raises |
|
|
|
------ |
|
|
|
ValueError |
|
|
|
If this prompt is manually created, therefore has no guild. |
|
|
|
""" |
|
|
|
if self._guild is None: |
|
|
|
raise ValueError('This prompt is manually created therefore has no guild.') |
|
|
|
return self._guild |
|
|
|
|
|
|
|
def __init__(self, *, data: PromptPayload, state: ConnectionState, guild: Guild): |
|
|
|
self._state: ConnectionState = state |
|
|
|
self.guild: Guild = guild |
|
|
|
self.id: int = int(data['id']) |
|
|
|
super().__init__( |
|
|
|
type=try_enum(OnboardingPromptType, data['type']), |
|
|
|
title=data['title'], |
|
|
|
options=[OnboardingPromptOption(data=option_data, state=state, guild=guild) for option_data in data['options']], |
|
|
|
single_select=data['single_select'], |
|
|
|
required=data['required'], |
|
|
|
in_onboarding=data['in_onboarding'], |
|
|
|
) |
|
|
|
|
|
|
|
def __repr__(self) -> str: |
|
|
|
return f'<OnboardingPrompt id={self.id} title={self.title}, type={self.type}>' |
|
|
|
def get_option(self, option_id: int, /) -> Optional[OnboardingPromptOption]: |
|
|
|
"""Optional[:class:`OnboardingPromptOption`]: The option with the given ID, if found.""" |
|
|
|
return next((option for option in self.options if option.id == option_id), None) |
|
|
|
|
|
|
|
|
|
|
|
class Onboarding: |
|
|
@ -335,7 +318,7 @@ class Onboarding: |
|
|
|
self.guild: Guild = guild |
|
|
|
self.default_channel_ids: Set[int] = {int(channel_id) for channel_id in data['default_channel_ids']} |
|
|
|
self.prompts: List[OnboardingPrompt] = [ |
|
|
|
OnboardingPrompt(data=prompt_data, state=state, guild=guild) for prompt_data in data['prompts'] |
|
|
|
OnboardingPrompt.from_dict(data=prompt_data, state=state, guild=guild) for prompt_data in data['prompts'] |
|
|
|
] |
|
|
|
self.enabled: bool = data['enabled'] |
|
|
|
self.mode: OnboardingMode = try_enum(OnboardingMode, data.get('mode', 0)) |
|
|
@ -348,3 +331,7 @@ class Onboarding: |
|
|
|
"""List[Union[:class:`abc.GuildChannel`, :class:`Thread`]]: The list of channels exposed to a new user by default.""" |
|
|
|
it = filter(None, map(self.guild._resolve_channel, self.default_channel_ids)) |
|
|
|
return utils._unique(it) |
|
|
|
|
|
|
|
def get_prompt(self, prompt_id: int, /) -> Optional[OnboardingPrompt]: |
|
|
|
"""Optional[:class:`OnboardingPrompt`]: The prompt with the given ID, if found.""" |
|
|
|
return next((prompt for prompt in self.prompts if prompt.id == prompt_id), None) |
|
|
|