From 024b8b73f7357bd2c2b7a94013d3f3641220ce27 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:10:21 +0000 Subject: [PATCH] feat: implement onboarding features (#2127) * initial onboarding impl. * Implement main edit function * implement shorthand edit * typo * better doc * misc * fix enums * fix repr * docs adjust * fix edit args * import fixes * docs clarify * final touches? * randint...? * style(pre-commit): auto fixes from pre-commit.com hooks * typefix * style(pre-commit): auto fixes from pre-commit.com hooks * docs updates * cleanup * style(pre-commit): auto fixes from pre-commit.com hooks * Update enum Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> * Update enums Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> * Replace _guild with guild Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> * Add new PromptOptions fields Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> * Update to_dict for emoji changes and exchange randint for generate_snowflake * style(pre-commit): auto fixes from pre-commit.com hooks * Final push * style(pre-commit): auto fixes from pre-commit.com hooks * remove debug lines * thanku doruk Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> * chore: minimal changes * chore: finalize * chore: promise this is the last --------- Signed-off-by: Lala Sabathil Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Lala Sabathil Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> --- CHANGELOG.md | 2 + discord/__init__.py | 1 + discord/enums.py | 32 +++ discord/guild.py | 83 +++++++ discord/http.py | 24 ++ discord/onboarding.py | 482 ++++++++++++++++++++++++++++++++++++ discord/types/onboarding.py | 64 +++++ docs/api/enums.rst | 28 +++ docs/api/models.rst | 18 ++ 9 files changed, 734 insertions(+) create mode 100644 discord/onboarding.py create mode 100644 discord/types/onboarding.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 44170862ff..c84e2b6b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2322](https://github.com/Pycord-Development/pycord/pull/2322)) - Added `User.avatar_decoration`. ([#2131](https://github.com/Pycord-Development/pycord/pull/2131)) +- Added support for guild onboarding related features. + ([#2127](https://github.com/Pycord-Development/pycord/pull/2127)) ### Changed diff --git a/discord/__init__.py b/discord/__init__.py index 1b73d1bafc..c49ae8902c 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -53,6 +53,7 @@ from .mentions import * from .message import * from .object import * +from .onboarding import * from .partial_emoji import * from .permissions import * from .player import * diff --git a/discord/enums.py b/discord/enums.py index dd9aa1c16d..6583b2bf5e 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -67,6 +67,8 @@ "AutoModActionType", "AutoModKeywordPresetType", "ApplicationRoleConnectionMetadataType", + "PromptType", + "OnboardingMode", "ReactionType", ) @@ -428,6 +430,11 @@ class AuditLogAction(Enum): auto_moderation_user_communication_disabled = 145 creator_monetization_request_created = 150 creator_monetization_terms_accepted = 151 + onboarding_question_create = 163 + onboarding_question_update = 164 + onboarding_update = 167 + server_guide_create = 190 + server_guide_update = 191 @property def category(self) -> AuditLogActionCategory | None: @@ -490,6 +497,11 @@ def category(self) -> AuditLogActionCategory | None: AuditLogAction.auto_moderation_user_communication_disabled: None, AuditLogAction.creator_monetization_request_created: None, AuditLogAction.creator_monetization_terms_accepted: None, + AuditLogAction.onboarding_question_create: AuditLogActionCategory.create, + AuditLogAction.onboarding_question_update: AuditLogActionCategory.update, + AuditLogAction.onboarding_update: AuditLogActionCategory.update, + AuditLogAction.server_guide_create: AuditLogActionCategory.create, + AuditLogAction.server_guide_update: AuditLogActionCategory.update, } return lookup[self] @@ -530,6 +542,12 @@ def target_type(self) -> str | None: return "application_command_permission" elif v < 146: return "auto_moderation_rule" + elif v < 152: + return "monetization" + elif v < 168: + return "onboarding" + elif v < 192: + return "server_guide" class UserFlags(Enum): @@ -946,6 +964,20 @@ class ApplicationRoleConnectionMetadataType(Enum): boolean_not_equal = 8 +class PromptType(Enum): + """Guild Onboarding Prompt Type""" + + multiple_choice = 0 + dropdown = 1 + + +class OnboardingMode(Enum): + """Guild Onboarding Mode""" + + default = 0 + advanced = 1 + + class ReactionType(Enum): """The reaction type""" diff --git a/discord/guild.py b/discord/guild.py index f2ef9aa8a0..2e3da13d34 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -71,6 +71,7 @@ from .iterators import AuditLogIterator, BanIterator, MemberIterator from .member import Member, VoiceState from .mixins import Hashable +from .onboarding import Onboarding from .permissions import PermissionOverwrite from .role import Role from .scheduled_events import ScheduledEvent, ScheduledEventLocation @@ -3842,6 +3843,88 @@ async def create_auto_moderation_rule( ) return AutoModRule(state=self._state, data=data) + async def onboarding(self): + """|coro| + + Returns the :class:`Onboarding` flow for the guild. + + .. versionadded:: 2.5 + + Returns + ------- + :class:`Onboarding` + The onboarding flow for the guild. + + Raises + ------ + HTTPException + Retrieving the onboarding flow failed somehow. + """ + data = await self._state.http.get_onboarding(self.id) + return Onboarding(data=data, guild=self) + + async def edit_onboarding( + self, + *, + prompts: list[OnboardingPrompt] | None = MISSING, + default_channels: list[Snowflake] | None = MISSING, + enabled: bool | None = MISSING, + mode: OnboardingMode | None = MISSING, + reason: str | None = MISSING, + ) -> Onboarding: + """|coro| + + A shorthand for :attr:`Onboarding.edit` without fetching the onboarding flow. + + You must have the :attr:`~Permissions.manage_guild` and :attr:`~Permissions.manage_roles` permissions in the + guild to do this. + + Parameters + ---------- + + prompts: Optional[List[:class:`OnboardingPrompt`]] + The new list of prompts for this flow. + default_channels: Optional[List[:class:`Snowflake`]] + The new default channels that users are opted into. + enabled: Optional[:class:`bool`] + Whether onboarding should be enabled. Setting this to ``True`` requires + the guild to have ``COMMUNITY`` in :attr:`~Guild.features` and at + least 7 ``default_channels``. + mode: Optional[:class:`OnboardingMode`] + The new onboarding mode. + reason: Optional[:class:`str`] + The reason that shows up on Audit log. + + Returns + ------- + :class:`Onboarding` + The updated onboarding flow. + + Raises + ------ + + HTTPException + Editing the onboarding flow failed somehow. + Forbidden + You don't have permissions to edit the onboarding flow. + """ + + fields: dict[str, Any] = {} + if prompts is not MISSING: + fields["prompts"] = [prompt.to_dict() for prompt in prompts] + + if default_channels is not MISSING: + fields["default_channel_ids"] = [channel.id for channel in default_channels] + + if enabled is not MISSING: + fields["enabled"] = enabled + + if mode is not MISSING: + fields["mode"] = mode.value + + new = await self._state.http.edit_onboarding(self.id, fields, reason=reason) + return Onboarding(data=new, guild=self) + async def delete_auto_moderation_rule( self, id: int, diff --git a/discord/http.py b/discord/http.py index 30184b665a..9ab72b6252 100644 --- a/discord/http.py +++ b/discord/http.py @@ -69,6 +69,7 @@ invite, member, message, + onboarding, role, scheduled_events, sticker, @@ -2884,6 +2885,29 @@ def update_application_role_connection_metadata_records( ) return self.request(r, json=payload) + # Onboarding + + def get_onboarding(self, guild_id: Snowflake) -> Response[onboarding.Onboarding]: + return self.request( + Route("GET", "/guilds/{guild_id}/onboarding", guild_id=guild_id) + ) + + def edit_onboarding( + self, guild_id: Snowflake, payload: Any, *, reason: str | None = None + ) -> Response[onboarding.Onboarding]: + keys = ( + "prompts", + "default_channel_ids", + "enabled", + "mode", + ) + payload = {key: val for key, val in payload.items() if key in keys} + return self.request( + Route("PUT", "/guilds/{guild_id}/onboarding", guild_id=guild_id), + json=payload, + reason=reason, + ) + # Misc def application_info(self) -> Response[appinfo.AppInfo]: diff --git a/discord/onboarding.py b/discord/onboarding.py new file mode 100644 index 0000000000..bd98bbfdde --- /dev/null +++ b/discord/onboarding.py @@ -0,0 +1,482 @@ +""" +The MIT License (MIT) + +Copyright (c) 2021-present Pycord Development + +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 typing import TYPE_CHECKING, Any + +from .enums import OnboardingMode, PromptType, try_enum +from .partial_emoji import PartialEmoji +from .utils import MISSING, cached_property, generate_snowflake, get + +if TYPE_CHECKING: + from .abc import Snowflake + from .channel import ForumChannel, TextChannel, VoiceChannel + from .emoji import Emoji + from .guild import Guild + from .object import Object + from .partial_emoji import PartialEmoji + from .types.onboarding import Onboarding as OnboardingPayload + from .types.onboarding import OnboardingPrompt as OnboardingPromptPayload + from .types.onboarding import PromptOption as PromptOptionPayload + +__all__ = ( + "Onboarding", + "OnboardingPrompt", + "PromptOption", +) + + +class PromptOption: + """Represents an onboarding prompt option displayed in :class:`OnboardingPrompt`. + + .. versionadded:: 2.5 + + Attributes + ---------- + + id: :class:`int` + The id of the prompt option. + channels: List[:class:`Snowflake`] + The channels assigned to the user when they select this option. + roles: List[:class:`Snowflake`] + The roles assigned to the user when they select this option. + emoji: Union[:class:`Emoji`, :class:`PartialEmoji`] + The emoji displayed with the option. + title: :class:`str` + The option's title. + description: Optional[:class:`str`] + The option's description. + """ + + def __init__( + self, + title: str, + channels: list[Snowflake] | None = None, + roles: list[Snowflake] | None = None, + description: str | None = None, + emoji: Emoji | PartialEmoji | None = None, + id: int | None = None, + ): + # ID is required when making edits, but it can be any snowflake that isn't already used by another prompt during edits + self.id: int = int(id) if id else generate_snowflake() + self.title: str = title + self.channels: list[Snowflake] = channels or [] + self.roles: list[Snowflake] = roles or [] + self.description: str | None = description or None + self.emoji: Emoji | PartialEmoji | None = emoji + + def __repr__(self): + return f"" + + def to_dict(self) -> PromptOptionPayload: + dict_: PromptOptionPayload = { + "title": self.title, + "description": self.description, + "channel_ids": [channel.id for channel in self.channels], + "role_ids": [role.id for role in self.roles], + "id": str(self.id), + } + if self.emoji: + dict_["emoji"] = { + "id": str(self.emoji.id) if self.emoji.id else None, + "name": self.emoji.name, + "animated": self.emoji.animated, + } + dict_["emoji_name"] = self.emoji.name + if self.emoji.id: + dict_["emoji_id"] = str(self.emoji.id) + dict_["emoji_animated"] = self.emoji.animated + + return dict_ + + @classmethod + def _from_dict(cls, data: PromptOptionPayload, guild: Guild) -> PromptOption: + id = data.get("id", 0) + channel_ids = [int(channel_id) for channel_id in data.get("channel_ids", [])] + channels = [guild.get_channel(channel_id) for channel_id in channel_ids] + role_ids = [int(role_id) for role_id in data.get("role_ids", [])] + roles = [guild.get_role(role_id) for role_id in role_ids] + title = data.get("title") + description = data.get("description") + + _emoji: dict[str, Any] = data.get("emoji") or {} + if "name" in _emoji: + # Emoji object is {'id': None, 'name': None, 'animated': False} ... + emoji = PartialEmoji.from_dict(_emoji) + if emoji.id: + emoji = get(guild.emojis, id=emoji.id) or emoji + else: + emoji = None + + return cls(channels=channels, roles=roles, title=title, description=description, emoji=emoji, id=id) # type: ignore + + +class OnboardingPrompt: + """Represents an onboarding prompt displayed in :class:`Onboarding`. + + .. versionadded:: 2.5 + + Attributes + ---------- + + id: :class:`int` + The id of the prompt. + type: :class:`PromptType` + The type of onboarding prompt. + title: :class:`str` + The prompt's title. + options: List[:class:`PromptOption`] + The list of options available in the prompt. + single_select: :class:`bool` + Whether the user is limited to selecting one option on this prompt. + required: :class:`bool` + Whether the user is required to answer this prompt. + in_onboarding: :class:`bool` + Whether this prompt is displayed in the initial onboarding flow. + """ + + def __init__( + self, + type: PromptType, + title: str, + options: list[PromptOption], + single_select: bool, + required: bool, + in_onboarding: bool, + id: int | None = None, # Currently optional as users can manually create these + ): + # ID is required when making edits, but it can be any snowflake that isn't already used by another prompt during edits + self.id: int = int(id) if id else generate_snowflake() + + self.type: PromptType = type + if isinstance(self.type, int): + self.type = try_enum(PromptType, self.type) + self.options: list[PromptOption] = options + self.title: str = title + self.single_select: bool = single_select + self.required: bool = required + self.in_onboarding: bool = in_onboarding + + def __repr__(self): + return f"" + + def to_dict(self) -> OnboardingPromptPayload: + dict_: OnboardingPromptPayload = { + "type": self.type.value, + "title": self.title, + "single_select": self.single_select, + "required": self.required, + "in_onboarding": self.in_onboarding, + "options": [option.to_dict() for option in self.options], + "id": self.id, + } + + return dict_ + + @classmethod + def _from_dict( + cls, data: OnboardingPromptPayload, guild: Guild + ) -> OnboardingPrompt: + id = data.get("id", 0) + type = try_enum(PromptType, data.get("type")) + title = data.get("title") + single_select = data.get("single_select") + required = data.get("required") + in_onboarding = data.get("in_onboarding") + options = [ + PromptOption._from_dict(option, guild) for option in data.get("options", []) + ] + + return cls(type=type, title=title, single_select=single_select, required=required, in_onboarding=in_onboarding, options=options, id=id) # type: ignore + + +class Onboarding: + """Represents the onboarding flow for a guild. + + .. versionadded:: 2.5 + + Attributes + ---------- + + prompts: List[:class:`OnboardingPrompt`] + A list of prompts displayed in the onboarding flow. + enabled: :class:`bool` + Whether onboarding is enabled in the guild. + mode: :class:`OnboardingMode` + The current onboarding mode. + """ + + def __init__(self, data: OnboardingPayload, guild: Guild): + self.guild = guild + self._update(data) + + def __repr__(self): + return f"" + + def _update(self, data: OnboardingPayload): + self.guild_id: Snowflake = data["guild_id"] + self.prompts: list[OnboardingPrompt] = [ + OnboardingPrompt._from_dict(prompt, self.guild) + for prompt in data.get("prompts", []) + ] + self.default_channel_ids: list[int] = [ + int(c) for c in data["default_channel_ids"] + ] + self.enabled: bool = data["enabled"] + self.mode: OnboardingMode = try_enum(OnboardingMode, data.get("mode")) + + @cached_property + def default_channels( + self, + ) -> list[TextChannel | ForumChannel | VoiceChannel | Object]: + """The channels that members are opted into by default. + + If a channel is not found in the guild's cache, + then it will be returned as an :class:`Object`. + """ + if self.guild is None: + return [Object(channel_id) for channel_id in self.default_channel_ids] + return [ + self.guild.get_channel(channel_id) or Object(channel_id) + for channel_id in self.default_channel_ids + ] + + async def edit( + self, + *, + prompts: list[OnboardingPrompt] | None = MISSING, + default_channels: list[Snowflake] | None = MISSING, + enabled: bool | None = MISSING, + mode: OnboardingMode | None = MISSING, + reason: str | None = MISSING, + ) -> Onboarding: + """|coro| + + Edits this onboarding flow. + + You must have the :attr:`~Permissions.manage_guild` and :attr:`~Permissions.manage_roles` permissions in the + guild to do this. + + Parameters + ---------- + + prompts: Optional[List[:class:`OnboardingPrompt`]] + The new list of prompts for this flow. + default_channels: Optional[List[:class:`Snowflake`]] + The new default channels that users are opted into. + enabled: Optional[:class:`bool`] + Whether onboarding should be enabled. Setting this to ``True`` requires + the guild to have ``COMMUNITY`` in :attr:`~Guild.features` and at + least 7 ``default_channels``. + mode: Optional[:class:`OnboardingMode`] + The new onboarding mode. + reason: Optional[:class:`str`] + The reason for editing this onboarding flow. Shows up on the audit log. + + Returns + ------- + :class:`Onboarding` + The updated onboarding flow. + + Raises + ------ + HTTPException + Editing the onboarding flow failed somehow. + Forbidden + You don't have permissions to edit the onboarding flow. + """ + + fields: dict[str, Any] = {} + if prompts is not MISSING: + fields["prompts"] = [prompt.to_dict() for prompt in prompts] + + if default_channels is not MISSING: + fields["default_channel_ids"] = [channel.id for channel in default_channels] + + if enabled is not MISSING: + fields["enabled"] = enabled + + if mode is not MISSING: + fields["mode"] = mode.value + + new = await self.guild._state.http.edit_onboarding( + self.guild.id, fields, reason=reason + ) + self._update(new) + return self + + async def add_prompt( + self, + type: PromptType, + title: str, + options: list[PromptOption], + single_select: bool, + required: bool, + in_onboarding: bool, + *, + reason: str | None = None, + ): + """|coro| + + Adds a new onboarding prompt. + + You must have the :attr:`~Permissions.manage_guild` and :attr:`~Permissions.manage_roles` permissions in the + guild to do this. + + Parameters + ---------- + type: :class:`PromptType` + The type of onboarding prompt. + title: :class:`str` + The prompt's title. + options: List[:class:`PromptOption`] + The list of options available in the prompt. + single_select: :class:`bool` + Whether the user is limited to selecting one option on this prompt. + required: :class:`bool` + Whether the user is required to answer this prompt. + in_onboarding: :class:`bool` + Whether this prompt is displayed in the initial onboarding flow. + reason: Optional[:class:`str`] + The reason for adding this prompt. Shows up on the audit log. + + Returns + ------- + :class:`Onboarding` + The updated onboarding flow. + + Raises + ------ + HTTPException + Editing the onboarding flow failed somehow. + Forbidden + You don't have permissions to edit the onboarding flow. + """ + + prompt = OnboardingPrompt( + type, title, options, single_select, required, in_onboarding + ) + prompts = self.prompts + [prompt] + return await self.edit(prompts=prompts, reason=reason) + + async def append_prompt( + self, + prompt: OnboardingPrompt, + *, + reason: str | None = None, + ): + """|coro| + + Append an onboarding prompt onto this flow. + + You must have the :attr:`~Permissions.manage_guild` and :attr:`~Permissions.manage_roles` permissions in the + guild to do this. + + Parameters + ---------- + prompt: :class:`OnboardingPrompt` + The onboarding prompt to append. + reason: Optional[:class:`str`] + The reason for appending this prompt. Shows up on the audit log. + + Returns + ------- + :class:`Onboarding` + The updated onboarding flow. + + Raises + ------ + HTTPException + Editing the onboarding flow failed somehow. + Forbidden + You don't have permissions to edit the onboarding flow. + """ + + prompts = self.prompts + [prompt] + return await self.edit(prompts=prompts, reason=reason) + + def get_prompt( + self, + id: int, + ): + """|coro| + + Get an onboarding prompt with the given ID. + + Parameters + ---------- + id: :class:`int` + The ID of the prompt to get. + + Returns + ------- + :class:`OnboardingPrompt` + The matching prompt, or None if it didn't exist. + """ + + return get(self.prompts, id=id) + + async def delete_prompt( + self, + id: int, + *, + reason: str | None = MISSING, + ): + """|coro| + + Delete an onboarding prompt with the given ID. + + You must have the :attr:`~Permissions.manage_guild` and :attr:`~Permissions.manage_roles` permissions in the + guild to do this. + + Parameters + ---------- + id: :class:`int` + The ID of the prompt to delete. + reason: Optional[:class:`str`] + The reason for deleting this prompt. Shows up on the audit log. + + Returns + ------- + :class:`Onboarding` + The updated onboarding flow. + + Raises + ------ + ValueError + No prompt with this ID exists. + HTTPException + Editing the onboarding flow failed somehow. + Forbidden + You don't have permissions to edit the onboarding flow. + """ + + to_delete = self.get_prompt(id) + if not to_delete: + raise ValueError("Prompt with the given ID was not found.") + + prompts = self.prompts[:] + prompts.remove(to_delete) + return await self.edit(prompts=prompts, reason=reason) diff --git a/discord/types/onboarding.py b/discord/types/onboarding.py new file mode 100644 index 0000000000..4fd4027bcc --- /dev/null +++ b/discord/types/onboarding.py @@ -0,0 +1,64 @@ +""" +The MIT License (MIT) + +Copyright (c) 2021-present Pycord Development + +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 typing import Literal, TypedDict + +from .._typed_dict import NotRequired +from .emoji import Emoji +from .snowflake import Snowflake, SnowflakeList + +PromptType = Literal[0, 1] +OnboardingMode = Literal[0, 1] + + +class Onboarding(TypedDict): + guild_id: Snowflake + prompts: list[OnboardingPrompt] + default_channel_ids: SnowflakeList + enabled: bool + mode: OnboardingMode + + +class OnboardingPrompt(TypedDict): + id: Snowflake + type: PromptType + options: list[PromptOption] + title: str + single_select: bool + required: bool + in_onboarding: bool + + +class PromptOption(TypedDict): + id: Snowflake + channel_ids: SnowflakeList + role_ids: SnowflakeList + emoji: NotRequired[Emoji] + emoji_id: NotRequired[Snowflake] + emoji_name: NotRequired[str] + emoji_animated: NotRequired[bool] + title: str + description: NotRequired[str] diff --git a/docs/api/enums.rst b/docs/api/enums.rst index 45317a37ce..77e2cea86d 100644 --- a/docs/api/enums.rst +++ b/docs/api/enums.rst @@ -2293,6 +2293,34 @@ of :class:`enum.Enum`. Represents the slurs keyword preset rule. +.. class:: PromptType + + Represents how each prompt's options are displayed. + + .. versionadded:: 2.5 + + .. attribute:: multiple_choice + + The options will appear in a grid form, showing the name and description. + + .. attribute:: dropdown + + The options will appear in a dropdown (similar to select menus), but without the description displayed. This is **enforced** if there are more than 12 options in the prompt. + +.. class:: OnboardingMode + + Represents the current mode of the guild's onboarding flow. + + .. versionadded:: 2.5 + + .. attribute:: default + + Only default channels are counted towards the Onboarding requirements. + + .. attribute:: advanced + + Both default channels and questions (``OnboardingPrompt``s) will count towards the Onboarding requirements. + .. class:: ReactionType Represents a Reaction's type. diff --git a/docs/api/models.rst b/docs/api/models.rst index 4d11fed63e..b2f85185b6 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -227,6 +227,24 @@ Welcome Screen .. autoclass:: WelcomeScreenChannel() :members: +Onboarding +~~~~~~~~~~~~~~ + +.. attributetable:: Onboarding + +.. autoclass:: Onboarding() + :members: + +.. attributetable:: OnboardingPrompt + +.. autoclass:: OnboardingPrompt() + :members: + +.. attributetable:: PromptOption + +.. autoclass:: PromptOption() + :members: + Integration ~~~~~~~~~~~