From 156e7740c71824b04e6d7af0c3c551d02a81f4c6 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Fri, 24 Feb 2023 22:38:38 +0100 Subject: [PATCH] Push implementation --- discord/enums.py | 3 + discord/guild.py | 17 +++ discord/http.py | 4 + discord/onboarding.py | 260 ++++++++++++++++++++++++++++++++++++ discord/types/onboarding.py | 60 +++++++++ docs/api.rst | 39 ++++++ 6 files changed, 383 insertions(+) create mode 100644 discord/onboarding.py create mode 100644 discord/types/onboarding.py diff --git a/discord/enums.py b/discord/enums.py index 43ae5b4f7..a08115c2a 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -750,6 +750,9 @@ class ForumLayoutType(Enum): list_view = 1 gallery_view = 2 +class OnboardingPromptType(Enum): + multiple_choice = 0 + dropdown = 1 def create_unknown_value(cls: Type[E], val: Any) -> E: value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below diff --git a/discord/guild.py b/discord/guild.py index 078a7b8be..b6355ceae 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -90,6 +90,7 @@ from .audit_logs import AuditLogEntry from .object import OLDEST_OBJECT, Object from .welcome_screen import WelcomeScreen, WelcomeChannel from .automod import AutoModRule, AutoModTrigger, AutoModRuleAction +from .onboarding import Onboarding __all__ = ( @@ -4026,3 +4027,19 @@ class Guild(Hashable): ) return AutoModRule(data=data, guild=self, state=self._state) + + + async def fetch_onboarding(self) -> Optional[Onboarding]: + """|coro| + + Fetches the onboarding information for this guild. + + .. versionadded:: 2.2 + + Returns + -------- + Optional[:class:`Onboarding`] + The onboarding information for this guild. + """ + data = await self._state.http.get_guild_onboarding(self.id) + return Onboarding(guild=self, data=data) \ No newline at end of file diff --git a/discord/http.py b/discord/http.py index 5913f4911..d89fed131 100644 --- a/discord/http.py +++ b/discord/http.py @@ -90,6 +90,7 @@ if TYPE_CHECKING: scheduled_event, sticker, welcome_screen, + onboarding, ) from .types.snowflake import Snowflake, SnowflakeList @@ -1735,6 +1736,9 @@ class HTTPClient: ) -> Response[widget.WidgetSettings]: return self.request(Route('PATCH', '/guilds/{guild_id}/widget', guild_id=guild_id), json=payload, reason=reason) + def get_guild_onboarding(self, guild_id: Snowflake) -> Response[onboarding.Onboarding]: + return self.request(Route('GET', '/guilds/{guild_id}/onboarding', guild_id=guild_id)) + # Invite management def create_invite( diff --git a/discord/onboarding.py b/discord/onboarding.py new file mode 100644 index 000000000..8aaf120ff --- /dev/null +++ b/discord/onboarding.py @@ -0,0 +1,260 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present 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 __future__ import annotations +from typing import TYPE_CHECKING, Dict, List, Union + +from .mixins import Hashable +from .object import Object +from .role import Role +from .enums import OnboardingPromptType, try_enum +from .partial_emoji import PartialEmoji +from .utils import SequenceProxy + +if TYPE_CHECKING: + from .abc import GuildChannel, PartialMessageable + from .guild import Guild + from .threads import Thread + from .types.onboarding import ( + Onboarding as OnboardingPayload, + OnboardingPrompt as OnboardingPromptPayload, + OnboardingPromptOption as OnboardingPromptOptionPayload, + ) + + + +class OnboardingPromptOption(Hashable): + """Represents a guild's onboarding prompt's option. + + .. container:: operations + + .. describe:: x == y + + Checks if two guilds are equal. + + .. describe:: x != y + + Checks if two guilds are not equal. + + .. describe:: hash(x) + + Returns the guild's hash. + + .. describe:: str(x) + + Returns the guild's name. + + .. versionadded:: 2.2 + + Attributes + ----------- + id: :class:`int` + The ID of the option. + title: :class:`str` + The title of the option. + description: :class:`str` + The description of the option. + emoji: :class:`PartialEmoji` + The emoji of the option. + """ + __slots__ = ('id', 'title', 'description', 'emoji', '_channels', '_roles', '_onboarding') + + def __init__(self, *, onboarding: Onboarding, data: OnboardingPromptOptionPayload) -> None: + self._onboarding: Onboarding = onboarding + + self._channels: Dict[int, Union[GuildChannel, Thread, PartialMessageable, Object]] = {} + self._roles: Dict[int, Union[Role, Object]] = {} + self._from_data(data) + + def _from_data(self, data: OnboardingPromptOptionPayload) -> None: + guild = self._onboarding._guild + state = guild._state + + self.id: int = int(data['id']) + self.title: str = data['title'] + self.description: str = data['description'] + + emoji = PartialEmoji.from_dict(data['emoji']) + emoji._state = state + self.emoji: PartialEmoji = emoji + + channel_ids = data.get('channel_ids', []) + for channel_id in channel_ids: + channel = guild.get_channel_or_thread(int(channel_id)) or state.get_channel(int(channel_id)) + self._channels[int(channel_id)] = channel or Object(id=channel_id) # type: ignore # can't be PrivateChannel + + role_ids = data.get('role_ids', []) + for role_id in role_ids: + role = guild.get_role(int(role_id)) + + self._roles[int(role_id)] = role or Object(id=role_id, type=Role) + + def __repr__(self) -> str: + return f'' + + + @property + def channels(self) -> SequenceProxy[Union[GuildChannel, Thread, PartialMessageable, Object]]: + """List[:class:`Union[GuildChannel, Thread, PartialMessageable, Object]`]: A list of channels that are opted into when this option is selected.""" + return SequenceProxy(self._channels.values()) + + @property + def roles(self) -> SequenceProxy[Union[Role, Object]]: + """List[:class:`Union[Role, Object]`]: A list of roles that are assigned when this option is selected.""" + return SequenceProxy(self._roles.values()) + + +class OnboardingPrompt(Hashable): + """Represents a guild's onboarding prompt. + + .. container:: operations + + .. describe:: x == y + + Checks if two guilds are equal. + + .. describe:: x != y + + Checks if two guilds are not equal. + + .. describe:: hash(x) + + Returns the guild's hash. + + .. describe:: str(x) + + Returns the guild's name. + + .. versionadded:: 2.2 + + + Attributes + ----------- + id: :class:`int` + The ID of the prompt. + title: :class:`str` + The title of the prompt. + single_select: :class:`bool` + Whether only one option can be selected at a time. + required: :class:`bool` + Whether the prompt is required in the onboarding flow. + in_onboarding: :class:`bool` + Whether the prompt is in the onboarding flow. + """ + __slots__ = ('id', 'title', 'single_select', 'required', 'in_onboarding', '_oboarding', '_options', '_type') + def __init__(self, *, onboarding: Onboarding, data: OnboardingPromptPayload) -> None: + self._oboarding: Onboarding = onboarding + self._from_data(data) + + def _from_data(self, data: OnboardingPromptPayload) -> None: + self.id: int = int(data['id']) + self.title: str = data['title'] + self.single_select: bool = data['single_select'] + self.required: bool = data['required'] + self._type: OnboardingPromptType = try_enum(OnboardingPromptType, data['type']) + self.in_onboarding: bool = data['in_onboarding'] + self._options: List[OnboardingPromptOption] = [ + OnboardingPromptOption(onboarding=self._oboarding, data=option) for option in data['options'] + ] + + def __repr__(self) -> str: + return f'' + + @property + def type(self) -> OnboardingPromptType: + """Optional[:class:`OnboardingPromptType`]: The type of the prompt.""" + return self._type + + @property + def options(self) -> SequenceProxy[OnboardingPromptOption]: + """List[:class:`OnboardingPromptOption`]: The options available to the prompt.""" + return SequenceProxy(self._options) + + +class Onboarding: + """Represents a guild's onboarding. + + .. container:: operations + + .. describe:: x == y + + Checks if two guilds are equal. + + .. describe:: x != y + + Checks if two guilds are not equal. + + .. describe:: hash(x) + + Returns the guild's hash. + + .. describe:: str(x) + + Returns the guild's name. + + .. versionadded:: 2.2 + + Attributes + ----------- + enabled: :class:`bool` + Whether guild onboarding is enabled. + """ + + __slots__ = ('enabled', '_guild', '_default_channel_ids', '_default_channels', '_prompts', '_guild_id') + + def __init__(self, *, guild: Guild, data: OnboardingPayload) -> None: + self._guild = guild + + self._default_channels: Dict[int, Union[GuildChannel, Thread, PartialMessageable, Object]] = {} + self._from_data(data) + + def _from_data(self, data: OnboardingPayload) -> None: + guild = self._guild + state = guild._state + + self.enabled: bool = data['enabled'] + self._guild_id: int = int(data['guild_id']) + + prompts = data.get('prompts', []) + self._prompts: List[OnboardingPrompt] = [ + OnboardingPrompt(onboarding=self, data=prompt) for prompt in prompts + ] + default_channel_ids = data.get('default_channel_ids', []) + for channel_id in default_channel_ids: + channel = guild.get_channel_or_thread(int(channel_id)) or state.get_channel(int(channel_id)) + self._default_channels[int(channel_id)] = channel or Object(id=channel_id) # type: ignore # can't be a private channel + + def __repr__(self) -> str: + return f'' + + @property + def default_channels(self) -> SequenceProxy[Union[GuildChannel, Thread, PartialMessageable, Object]]: + """List[Union[:class:`GuildChannel`, :class:`Thread`, :class:`Object`]: The channels that new members get opted into automatically.""" + return SequenceProxy(self._default_channels.values()) + + @property + def prompts(self) -> SequenceProxy[OnboardingPrompt]: + """List[:class:`GuildOnboardingPrompt`]: The prompts shown during onboarding and in costomize community.""" + return SequenceProxy(self._prompts) diff --git a/discord/types/onboarding.py b/discord/types/onboarding.py new file mode 100644 index 000000000..78f4ff4f3 --- /dev/null +++ b/discord/types/onboarding.py @@ -0,0 +1,60 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present 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 __future__ import annotations + +from typing import List, Literal, TypedDict, TYPE_CHECKING +from .snowflake import Snowflake + +if TYPE_CHECKING: + from .emoji import PartialEmoji + + +OnboardingPromptType = Literal[0, 1] + + +class OnboardingPromptOption(TypedDict): + id: Snowflake + channel_ids: List[Snowflake] + role_ids: List[Snowflake] + emoji: PartialEmoji + title: str + description: str + + +class OnboardingPrompt(TypedDict): + id: Snowflake + options: List[OnboardingPromptOption] + title: str + single_select: bool + required: bool + in_onboarding: bool + type: OnboardingPromptType + + +class Onboarding(TypedDict): + guild_id: Snowflake + prompts: List[OnboardingPrompt] + default_channel_ids: List[Snowflake] + enabled: bool \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 32e393ef2..253620e06 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3276,6 +3276,20 @@ of :class:`enum.Enum`. Displays posts as a collection of tiles. +.. class:: OnboardingPromptType + + Represents the type of a guild onboarding prompt. + + .. versionadded:: 2.2 + + .. attribute:: multiple_choice + + The prompt will be a multiple choice. + + .. attribute:: dropdown + + The prompt will be a dropdown. + .. _discord-api-audit-logs: @@ -4608,6 +4622,31 @@ PartialWebhookChannel .. autoclass:: PartialWebhookChannel() :members: +Onboarding +~~~~~~~~~~~~ + +.. attributetable:: Onboarding + +.. autoclass:: Onboarding() + :members: + +OnboardingPrompt +~~~~~~~~~~~~~~~~~~ + +.. attributetable:: OnboardingPrompt + +.. autoclass:: OnboardingPrompt() + :members: + +OnboardingPromptOption +~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: OnboardingPromptOption + +.. autoclass:: OnboardingPromptOption() + :members: + + .. _discord_api_data: Data Classes