From 0390aea13665ac7e720de79d0034e17f221a812d Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:32:05 +0200 Subject: [PATCH 01/29] Added onboarding routes --- hikari/internal/routes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hikari/internal/routes.py b/hikari/internal/routes.py index 1563a6d09..7ad3384b2 100644 --- a/hikari/internal/routes.py +++ b/hikari/internal/routes.py @@ -442,6 +442,9 @@ def compile_to_file( POST_GUILD_ROLES: typing.Final[Route] = Route(POST, "/guilds/{guild}/roles") PATCH_GUILD_ROLES: typing.Final[Route] = Route(PATCH, "/guilds/{guild}/roles") +GET_GUILD_ONBOARDING: typing.Final[Route] = Route(GET, "/guilds/{guild}/onboarding") +PUT_GUILD_ONBOARDING: typing.Final[Route] = Route(PUT, "/guilds/{guild}/onboarding") + GET_GUILD_VANITY_URL: typing.Final[Route] = Route(GET, "/guilds/{guild}/vanity-url") PATCH_GUILD_VOICE_STATE: typing.Final[Route] = Route(PATCH, "/guilds/{guild}/voice-states/{user}") From e2b91e9d0e2440690d49f1bf45f39bed4a63c300 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:32:12 +0200 Subject: [PATCH 02/29] Added onboarding rest-routes --- hikari/api/rest.py | 83 +++++++++++++++++++++++++++++++++++++++++++++ hikari/impl/rest.py | 38 +++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index 62cc5806c..bfd6b9f95 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6452,6 +6452,89 @@ async def edit_welcome_screen( If an internal error occurs on Discord while handling the request. """ + @abc.abstractmethod + async def fetch_guild_onboarding( + self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild] + ) -> guilds.GuildOnboarding: + """Fetch a guild's onboarding. + + Parameters + ---------- + guild + Object or ID of the guild to fetch the welcome screen for. + + Returns + ------- + hikari.guilds.GuildOnboarding + The requested onboarding object. + + Raises + ------ + hikari.errors.NotFoundError + If the guild is not found or the welcome screen has never been set + for this guild (if the welcome screen has been set for a guild + before and then disabled you should still be able to fetch it). + hikari.errors.UnauthorizedError + If you are unauthorized to make the request (invalid/missing token). + hikari.errors.RateLimitTooLongError + Raised in the event that a rate limit occurs that is + longer than `max_rate_limit` when making a request. + hikari.errors.InternalServerError + If an internal error occurs on Discord while handling the request. + """ + + @abc.abstractmethod + async def edit_guild_onboarding( + self, + guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], + *, + enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + default_channels: undefined.UndefinedNoneOr[ + typing.Sequence[snowflakes.SnowflakeishOr[channels_.GuildChannel]] + ] = undefined.UNDEFINED, + mode: undefined.UndefinedOr[guilds.OnboardingMode] = undefined.UNDEFINED, + prompts: undefined.UndefinedNoneOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, + reason: undefined.UndefinedOr[str] = undefined.UNDEFINED, + ) -> guilds.GuildOnboarding: + """Edit the onboarding of a community guild. + + Parameters + ---------- + guild + ID or object of the guild to edit the onboarding for. + enabled + If provided, Whether the guild's onboarding should be enabled. + default_channels + If provided, channel IDs that members get opted into automatically. + mode + If provided, the onboarding mode to set for the guild. + prompts + If provided, prompts shown during onboarding and in customize community. + + Returns + ------- + hikari.guilds.GuildOnboarding + The edited onboarding object. + + Raises + ------ + hikari.errors.BadRequestError + # TODO: fix docs + hikari.errors.ForbiddenError + If you are missing the [`hikari.permissions.Permissions.MANAGE_GUILD`][] permission, are not part of + the guild or the guild doesn't have access to the community welcome + screen feature. + hikari.errors.NotFoundError + If the guild is not found. + hikari.errors.UnauthorizedError + If you are unauthorized to make the request (invalid/missing token). + hikari.errors.RateLimitTooLongError + Raised in the event that a rate limit occurs that is + longer than `max_rate_limit` when making a request. + hikari.errors.InternalServerError + If an internal error occurs on Discord while handling the request. + """ + @abc.abstractmethod async def fetch_vanity_url(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> invites.VanityURL: """Fetch a guild's vanity url. diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index f6d939742..b9eed808f 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3644,6 +3644,44 @@ async def edit_welcome_screen( assert isinstance(response, dict) return self._entity_factory.deserialize_welcome_screen(response) + async def fetch_guild_onboarding( + self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild] + ) -> guilds.GuildOnboarding: + route = routes.GET_GUILD_ONBOARDING.compile(guild=guild) + response = await self._request(route) + assert isinstance(response, dict) + return self._entity_factory.deserialize_guild_onboarding(response) + + async def edit_guild_onboarding( + self, + guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], + *, + enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + default_channels: undefined.UndefinedNoneOr[ + typing.Sequence[snowflakes.SnowflakeishOr[channels_.GuildChannel]] + ] = undefined.UNDEFINED, + mode: undefined.UndefinedOr[guilds.OnboardingMode] = undefined.UNDEFINED, + prompts: undefined.UndefinedNoneOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, + reason: undefined.UndefinedOr[str] = undefined.UNDEFINED, + ) -> guilds.GuildOnboarding: + route = routes.PUT_GUILD_ONBOARDING.compile(guild=guild) + + body = data_binding.JSONObjectBuilder() + + body.put("enabled", enabled) + body.put("mode", mode) + + if default_channels is not undefined.UNDEFINED and default_channels is not None: + default_channels = [int(snowflakes.Snowflake(channel)) for channel in default_channels] + body.put_array("default_channel_ids", default_channels, conversion=str) + + if prompts is not None: + body.put_array("prompts", prompts, conversion=self._entity_factory.serialize_onboarding_prompt) + + response = await self._request(route, json=body, reason=reason) + assert isinstance(response, dict) + return self._entity_factory.deserialize_guild_onboarding(response) + async def fetch_vanity_url(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> invites.VanityURL: route = routes.GET_GUILD_VANITY_URL.compile(guild=guild) response = await self._request(route) From a49fc22afd07db293326d5829fede74d126c9a55 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:32:19 +0200 Subject: [PATCH 03/29] Added onboarding classes --- hikari/guilds.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/hikari/guilds.py b/hikari/guilds.py index 37235c265..078724e34 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1302,6 +1302,97 @@ class WelcomeChannel: """ID of the emoji shown in the welcome screen channel if it's set to a custom emoji.""" +@typing.final +class OnboardingPromptType(int, enums.Enum): + """The type of onboarding prompt.""" + + MULTIPLE_CHOICE = 0 + """A multiple choice prompt.""" + + DROPDOWN = 1 + """A dropdown prompt.""" + + +@typing.final +class OnboardingMode(int, enums.Enum): + """The mode of onboarding.""" + + ONBOARDING_DEFAULT = 0 + """Counts only Default Channels towards constraints.""" + + ONBOARDING_ADVANCED = 1 + """Counts Default Channels and Questions towards constraints.""" + + +@attrs_extensions.with_copy +@attrs.define(hash=False, kw_only=True, weakref_slot=False) +class GuildOnboarding: + """Used to represent guild onboarding settings on Discord.""" + + guild_id: snowflakes.Snowflake = attrs.field(repr=True) + """ID of the guild this onboarding is part of.""" + + prompts: typing.List[OnboardingPrompt] = attrs.field(repr=True) + """Prompts shown during onboarding and in customize community.""" + + default_channel_ids: typing.List[snowflakes.Snowflake] = attrs.field(repr=True) + """Channel IDs that members get opted into automatically.""" + + enabled: bool = attrs.field(repr=True) + """Whether the guild onboarding is enabled.""" + + mode: OnboardingMode = attrs.field(repr=True) + """The mode of onboarding.""" + + +@attrs_extensions.with_copy +@attrs.define(hash=False, kw_only=True, weakref_slot=False) +class OnboardingPrompt: + """Used to represent an onboarding prompt.""" + + id: snowflakes.Snowflake = attrs.field(repr=True) + """The ID of the onboarding prompt.""" + + type: OnboardingPromptType = attrs.field(repr=True) + """The type of the onboarding prompt.""" + + options: typing.List[OnboardingPromptOption] = attrs.field(repr=True) + """Options available within the prompt.""" + + title: str = attrs.field(repr=True) + """The title of the onboarding prompt.""" + + single_select: bool = attrs.field(repr=True) + """Indicates whether users are limited to selecting one option for the prompt.""" + + required: bool = attrs.field(repr=True) + """Indicates whether the prompt is required before a user completes the onboarding flow.""" + + in_onboarding: bool = attrs.field(repr=True) + """Indicates whether the prompt is present in the onboarding flow. + + If `false`, the prompt will only appear in the Channels & Roles tab. + """ + + +@attrs_extensions.with_copy +@attrs.define(hash=False, kw_only=True, weakref_slot=False) +class OnboardingPromptOption: + """Used to represent an onboarding prompt option.""" + + id: snowflakes.Snowflake = attrs.field(repr=True) + + channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) + + role_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) + + emoji: typing.Optional[emojis_.Emoji] = attrs.field(repr=True) + + title: str = attrs.field(repr=True) + + description: typing.Optional[str] = attrs.field(repr=True) + + @attrs_extensions.with_copy @attrs.define(hash=False, kw_only=True, weakref_slot=False) class WelcomeScreen: From 36fa8854ffc88ae0f23d4dc1496ad9ebde60dbfe Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:33:11 +0200 Subject: [PATCH 04/29] Added onboarding serialization/deserialization --- hikari/api/entity_factory.py | 79 +++++++++++++++++++++++++++++ hikari/impl/entity_factory.py | 95 +++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) diff --git a/hikari/api/entity_factory.py b/hikari/api/entity_factory.py index 6bc26cae7..8768f5b94 100644 --- a/hikari/api/entity_factory.py +++ b/hikari/api/entity_factory.py @@ -1014,6 +1014,85 @@ def deserialize_guild_widget(self, payload: data_binding.JSONObject) -> guild_mo The deserialized guild widget object. """ + @abc.abstractmethod + def deserialize_guild_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: + """Parse a raw payload from Discord into a guild onboarding object. + + Parameters + ---------- + payload + The JSON payload to deserialize. + + Returns + ------- + hikari.guilds.GuildOnboarding + The deserialized guild onboarding object. + """ + + @abc.abstractmethod + def deserialize_onboarding_prompt(self, payload: data_binding.JSONObject) -> guild_models.OnboardingPrompt: + """Parse a raw payload from Discord into an onboarding prompt object. + + Parameters + ---------- + payload + The JSON payload to deserialize. + + Returns + ------- + hikari.guilds.OnboardingPrompt + The deserialized onboarding prompt object. + """ + + @abc.abstractmethod + def deserialize_onboarding_prompt_option( + self, payload: data_binding.JSONObject + ) -> guild_models.OnboardingPromptOption: + """Parse a raw payload from Discord into an onboarding prompt option object. + + Parameters + ---------- + payload + The JSON payload to deserialize. + + Returns + ------- + hikari.guilds.OnboardingPromptOption + The deserialized onboarding prompt option object. + """ + + @abc.abstractmethod + def serialize_onboarding_prompt_option( + self, option: guild_models.OnboardingPromptOption + ) -> data_binding.JSONObject: + """Serialize an onboarding prompt option object to a json serializable dict. + + Parameters + ---------- + option + The onboarding prompt option object to serialize. + + Returns + ------- + hikari.internal.data_binding.JSONObject + The serialized representation of the onboarding prompt option. + """ + + @abc.abstractmethod + def serialize_onboarding_prompt(self, prompt: guild_models.OnboardingPrompt) -> data_binding.JSONObject: + """Serialize an onboarding prompt object to a json serializable dict. + + Parameters + ---------- + prompt + The onboarding prompt object to serialize. + + Returns + ------- + hikari.internal.data_binding.JSONObject + The serialized representation of the onboarding prompt. + """ + @abc.abstractmethod def deserialize_welcome_screen(self, payload: data_binding.JSONObject) -> guild_models.WelcomeScreen: """Parse a raw payload from Discord into a guild welcome screen object. diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index dec2c96e5..f459f6d99 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1776,6 +1776,101 @@ def deserialize_guild_widget(self, payload: data_binding.JSONObject) -> guild_mo return guild_models.GuildWidget(app=self._app, channel_id=channel_id, is_enabled=payload["enabled"]) + def deserialize_guild_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: + default_channel_ids: typing.List[snowflakes.Snowflake] = [] + for raw_channel_id in payload["default_channel_ids"]: + default_channel_ids.append(snowflakes.Snowflake(raw_channel_id)) + + prompts: typing.List[guild_models.OnboardingPrompt] = [] + for prompt_payload in payload["prompts"]: + prompts.append(self.deserialize_onboarding_prompt(prompt_payload)) + + mode: guild_models.OnboardingMode = guild_models.OnboardingMode(payload["mode"]) + + return guild_models.GuildOnboarding( + guild_id=snowflakes.Snowflake(payload["guild_id"]), + prompts=prompts, + default_channel_ids=default_channel_ids, + mode=mode, + enabled=bool(payload.get("enabled")), + ) + + def deserialize_onboarding_prompt(self, payload: data_binding.JSONObject) -> guild_models.OnboardingPrompt: + options: typing.List[guild_models.OnboardingPromptOption] = [] + for option_payload in payload["options"]: + options.append(self.deserialize_onboarding_prompt_option(option_payload)) + + return guild_models.OnboardingPrompt( + id=snowflakes.Snowflake(payload["id"]), + type=guild_models.OnboardingPromptType(payload["type"]), + options=options, + title=payload["title"], + single_select=bool(payload.get("single_select")), + required=bool(payload.get("required")), + in_onboarding=bool(payload.get("in_onboarding")), + ) + + def deserialize_onboarding_prompt_option( + self, payload: data_binding.JSONObject + ) -> guild_models.OnboardingPromptOption: + channel_ids: typing.List[snowflakes.Snowflake] = [] + for raw_channel_id in payload["channel_ids"]: + channel_ids.append(snowflakes.Snowflake(raw_channel_id)) + + role_ids: typing.List[snowflakes.Snowflake] = [] + for raw_role_id in payload["role_ids"]: + role_ids.append(snowflakes.Snowflake(raw_role_id)) + + description = payload.get("description", None) + + emoji: typing.Optional[emoji_models.Emoji] = None + if raw_emoji := payload.get("emoji"): + emoji = self.deserialize_emoji(raw_emoji) + + return guild_models.OnboardingPromptOption( + id=snowflakes.Snowflake(payload["id"]), + channel_ids=channel_ids, + role_ids=role_ids, + emoji=emoji, + title=payload["title"], + description=description, + ) + + def serialize_onboarding_prompt(self, prompt: guild_models.OnboardingPrompt) -> data_binding.JSONObject: + payload: typing.Dict[str, typing.Any] = { + "id": str(prompt.id), + "type": prompt.type.value, + "title": prompt.title, + "options": [self.serialize_onboarding_prompt_option(option) for option in prompt.options], + "single_select": prompt.single_select, + "required": prompt.required, + "in_onboarding": prompt.in_onboarding, + } + + return payload + + def serialize_onboarding_prompt_option( + self, option: guild_models.OnboardingPromptOption + ) -> data_binding.JSONObject: + payload: typing.Dict[str, typing.Any] = { + "id": str(option.id), + "channel_ids": [str(channel_id) for channel_id in option.channel_ids], + "role_ids": [str(role_id) for role_id in option.role_ids], + "title": option.title, + } + + if option.description is not None: + payload["description"] = option.description + + if isinstance(option.emoji, emoji_models.UnicodeEmoji): + payload["emoji_name"] = option.emoji.name + elif isinstance(option.emoji, emoji_models.CustomEmoji): + payload["emoji_id"] = str(option.emoji.id) + payload["emoji_name"] = option.emoji.name + payload["emoji_animated"] = option.emoji.is_animated + + return payload + def deserialize_welcome_screen(self, payload: data_binding.JSONObject) -> guild_models.WelcomeScreen: channels: typing.List[guild_models.WelcomeChannel] = [] From 11699bb1fc23264fcb1a6825a581aaa9df55d759 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:39:53 +0200 Subject: [PATCH 05/29] Add changelog --- changes/2005.feature.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/2005.feature.md diff --git a/changes/2005.feature.md b/changes/2005.feature.md new file mode 100644 index 000000000..de56c48aa --- /dev/null +++ b/changes/2005.feature.md @@ -0,0 +1 @@ +Add guild onboarding functionality From 9b6fb289312b19f54f6c5f2c16104d92a46d6bf9 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 02:46:26 +0200 Subject: [PATCH 06/29] Switch attrs.define hash parameter to unsafe_hash --- hikari/guilds.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index e16c7e465..88cc7d04d 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1325,7 +1325,7 @@ class OnboardingMode(int, enums.Enum): @attrs_extensions.with_copy -@attrs.define(hash=False, kw_only=True, weakref_slot=False) +@attrs.define(unsafe_hash=False, kw_only=True, weakref_slot=False) class GuildOnboarding: """Used to represent guild onboarding settings on Discord.""" @@ -1346,7 +1346,7 @@ class GuildOnboarding: @attrs_extensions.with_copy -@attrs.define(hash=False, kw_only=True, weakref_slot=False) +@attrs.define(unsafe_hash=False, kw_only=True, weakref_slot=False) class OnboardingPrompt: """Used to represent an onboarding prompt.""" @@ -1376,7 +1376,7 @@ class OnboardingPrompt: @attrs_extensions.with_copy -@attrs.define(hash=False, kw_only=True, weakref_slot=False) +@attrs.define(unsafe_hash=False, kw_only=True, weakref_slot=False) class OnboardingPromptOption: """Used to represent an onboarding prompt option.""" From b284f354805dad379556583f5e34f20ea0da0d9e Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:03:31 +0200 Subject: [PATCH 07/29] Update hikari/guilds.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/guilds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index 88cc7d04d..417190afd 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1325,7 +1325,7 @@ class OnboardingMode(int, enums.Enum): @attrs_extensions.with_copy -@attrs.define(unsafe_hash=False, kw_only=True, weakref_slot=False) +@attrs.define(kw_only=True, weakref_slot=False) class GuildOnboarding: """Used to represent guild onboarding settings on Discord.""" From 1d44ea28db5eef5a4b61ba7c9e52ee9d17f86596 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:03:42 +0200 Subject: [PATCH 08/29] Update hikari/guilds.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/guilds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index 417190afd..f4c0d9926 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1346,7 +1346,7 @@ class GuildOnboarding: @attrs_extensions.with_copy -@attrs.define(unsafe_hash=False, kw_only=True, weakref_slot=False) +@attrs.define(kw_only=True, weakref_slot=False) class OnboardingPrompt: """Used to represent an onboarding prompt.""" From 4f57a410588196724b6e234d66ede4eae1c08227 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:08:27 +0200 Subject: [PATCH 09/29] Update hikari/impl/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index f459f6d99..348350c1e 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1859,7 +1859,7 @@ def serialize_onboarding_prompt_option( "title": option.title, } - if option.description is not None: + if option.description: payload["description"] = option.description if isinstance(option.emoji, emoji_models.UnicodeEmoji): From 09f46ac347c9e7ed83f6d46d0c9f5b1d8c76eec4 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:09:11 +0200 Subject: [PATCH 10/29] Update hikari/impl/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 348350c1e..5f37e084b 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1865,7 +1865,7 @@ def serialize_onboarding_prompt_option( if isinstance(option.emoji, emoji_models.UnicodeEmoji): payload["emoji_name"] = option.emoji.name elif isinstance(option.emoji, emoji_models.CustomEmoji): - payload["emoji_id"] = str(option.emoji.id) + payload["emoji_id"] = option.emoji.id payload["emoji_name"] = option.emoji.name payload["emoji_animated"] = option.emoji.is_animated From ca0b5ccf77165ff410e3fb419ee199c69d33bf55 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:10:50 +0200 Subject: [PATCH 11/29] Update hikari/api/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/api/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/api/entity_factory.py b/hikari/api/entity_factory.py index 8768f5b94..77ac7508a 100644 --- a/hikari/api/entity_factory.py +++ b/hikari/api/entity_factory.py @@ -1015,7 +1015,7 @@ def deserialize_guild_widget(self, payload: data_binding.JSONObject) -> guild_mo """ @abc.abstractmethod - def deserialize_guild_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: + def deserialize_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: """Parse a raw payload from Discord into a guild onboarding object. Parameters From f830f5b319f515aa0135c74d692ad0f33ad269a5 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:11:01 +0200 Subject: [PATCH 12/29] Update hikari/guilds.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/guilds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index f4c0d9926..df3f98066 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1332,10 +1332,10 @@ class GuildOnboarding: guild_id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the guild this onboarding is part of.""" - prompts: typing.List[OnboardingPrompt] = attrs.field(repr=True) + prompts: typing.Sequence[OnboardingPrompt] = attrs.field(repr=True) """Prompts shown during onboarding and in customize community.""" - default_channel_ids: typing.List[snowflakes.Snowflake] = attrs.field(repr=True) + default_channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) """Channel IDs that members get opted into automatically.""" enabled: bool = attrs.field(repr=True) From 9d52ce730e803436927a47bf2a49f5745dc8bb6f Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:53:51 +0200 Subject: [PATCH 13/29] Update hikari/guilds.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/guilds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index df3f98066..7588cbd60 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1371,7 +1371,7 @@ class OnboardingPrompt: in_onboarding: bool = attrs.field(repr=True) """Indicates whether the prompt is present in the onboarding flow. - If `false`, the prompt will only appear in the Channels & Roles tab. + If `[False][]`, the prompt will only appear in the "Channels & Roles" tab. """ From be1abea1081230a6befcd704021394ea6b71cb94 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:54:04 +0200 Subject: [PATCH 14/29] Update hikari/impl/rest.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/rest.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index b9eed808f..33c22657f 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3671,9 +3671,7 @@ async def edit_guild_onboarding( body.put("enabled", enabled) body.put("mode", mode) - if default_channels is not undefined.UNDEFINED and default_channels is not None: - default_channels = [int(snowflakes.Snowflake(channel)) for channel in default_channels] - body.put_array("default_channel_ids", default_channels, conversion=str) + body.put_snowlake_array("default_channel_ids", default_channels) if prompts is not None: body.put_array("prompts", prompts, conversion=self._entity_factory.serialize_onboarding_prompt) From 77b423257eb21e1be5fb303588b2c249f24a7b4a Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:54:14 +0200 Subject: [PATCH 15/29] Update hikari/impl/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 5f37e084b..16468aa8e 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1853,7 +1853,7 @@ def serialize_onboarding_prompt_option( self, option: guild_models.OnboardingPromptOption ) -> data_binding.JSONObject: payload: typing.Dict[str, typing.Any] = { - "id": str(option.id), + "id": option.id, "channel_ids": [str(channel_id) for channel_id in option.channel_ids], "role_ids": [str(role_id) for role_id in option.role_ids], "title": option.title, From c845a085e5999b90dcc12c562fb510e832239259 Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:54:40 +0200 Subject: [PATCH 16/29] Update hikari/impl/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 16468aa8e..6840a13f8 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1821,7 +1821,7 @@ def deserialize_onboarding_prompt_option( for raw_role_id in payload["role_ids"]: role_ids.append(snowflakes.Snowflake(raw_role_id)) - description = payload.get("description", None) + description = payload.get("description") or None emoji: typing.Optional[emoji_models.Emoji] = None if raw_emoji := payload.get("emoji"): From 53bd006b364ab7a66411179e498c9b6afff8cbff Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:22:31 +0200 Subject: [PATCH 17/29] change to list comprehensions --- hikari/impl/entity_factory.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 6840a13f8..4e04ba869 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1796,9 +1796,9 @@ def deserialize_guild_onboarding(self, payload: data_binding.JSONObject) -> guil ) def deserialize_onboarding_prompt(self, payload: data_binding.JSONObject) -> guild_models.OnboardingPrompt: - options: typing.List[guild_models.OnboardingPromptOption] = [] - for option_payload in payload["options"]: - options.append(self.deserialize_onboarding_prompt_option(option_payload)) + options: typing.List[guild_models.OnboardingPromptOption] = [ + self.deserialize_onboarding_prompt_option(option_payload) for option_payload in payload["options"] + ] return guild_models.OnboardingPrompt( id=snowflakes.Snowflake(payload["id"]), @@ -1813,13 +1813,13 @@ def deserialize_onboarding_prompt(self, payload: data_binding.JSONObject) -> gui def deserialize_onboarding_prompt_option( self, payload: data_binding.JSONObject ) -> guild_models.OnboardingPromptOption: - channel_ids: typing.List[snowflakes.Snowflake] = [] - for raw_channel_id in payload["channel_ids"]: - channel_ids.append(snowflakes.Snowflake(raw_channel_id)) + channel_ids: typing.List[snowflakes.Snowflake] = [ + snowflakes.Snowflake(raw_channel_id) for raw_channel_id in payload["channel_ids"] + ] - role_ids: typing.List[snowflakes.Snowflake] = [] - for raw_role_id in payload["role_ids"]: - role_ids.append(snowflakes.Snowflake(raw_role_id)) + role_ids: typing.List[snowflakes.Snowflake] = [ + snowflakes.Snowflake(raw_role_id) for raw_role_id in payload["role_ids"] + ] description = payload.get("description") or None From 72a65d207aeb3057a360ca48d8f381d686c10eca Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:23:05 +0200 Subject: [PATCH 18/29] remove unnecessary check --- hikari/impl/rest.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 33c22657f..326c749b8 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3650,7 +3650,7 @@ async def fetch_guild_onboarding( route = routes.GET_GUILD_ONBOARDING.compile(guild=guild) response = await self._request(route) assert isinstance(response, dict) - return self._entity_factory.deserialize_guild_onboarding(response) + return self._entity_factory.deserialize_onboarding(response) async def edit_guild_onboarding( self, @@ -3671,14 +3671,12 @@ async def edit_guild_onboarding( body.put("enabled", enabled) body.put("mode", mode) - body.put_snowlake_array("default_channel_ids", default_channels) - - if prompts is not None: - body.put_array("prompts", prompts, conversion=self._entity_factory.serialize_onboarding_prompt) + body.put_snowflake_array("default_channel_ids", default_channels) + body.put_array("prompts", prompts, conversion=self._entity_factory.serialize_onboarding_prompt) response = await self._request(route, json=body, reason=reason) assert isinstance(response, dict) - return self._entity_factory.deserialize_guild_onboarding(response) + return self._entity_factory.deserialize_onboarding(response) async def fetch_vanity_url(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> invites.VanityURL: route = routes.GET_GUILD_VANITY_URL.compile(guild=guild) From 8b10f6c340536e3b8a63bdddc3e5aa4d01a4d052 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:23:29 +0200 Subject: [PATCH 19/29] rename deserialize_onboarding --- hikari/impl/entity_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 4e04ba869..899041dae 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1776,7 +1776,7 @@ def deserialize_guild_widget(self, payload: data_binding.JSONObject) -> guild_mo return guild_models.GuildWidget(app=self._app, channel_id=channel_id, is_enabled=payload["enabled"]) - def deserialize_guild_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: + def deserialize_onboarding(self, payload: data_binding.JSONObject) -> guild_models.GuildOnboarding: default_channel_ids: typing.List[snowflakes.Snowflake] = [] for raw_channel_id in payload["default_channel_ids"]: default_channel_ids.append(snowflakes.Snowflake(raw_channel_id)) From b3dd3d8480ce13bccc62f123951cec184e181e6c Mon Sep 17 00:00:00 2001 From: Blaze <88249929+syncblaze@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:24:03 +0200 Subject: [PATCH 20/29] Update hikari/impl/entity_factory.py Co-authored-by: davfsa Signed-off-by: Blaze <88249929+syncblaze@users.noreply.github.com> --- hikari/impl/entity_factory.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 899041dae..cbf0a59db 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1837,9 +1837,9 @@ def deserialize_onboarding_prompt_option( ) def serialize_onboarding_prompt(self, prompt: guild_models.OnboardingPrompt) -> data_binding.JSONObject: - payload: typing.Dict[str, typing.Any] = { - "id": str(prompt.id), - "type": prompt.type.value, + return { + "id": prompt.id, + "type": prompt.type, "title": prompt.title, "options": [self.serialize_onboarding_prompt_option(option) for option in prompt.options], "single_select": prompt.single_select, @@ -1847,8 +1847,6 @@ def serialize_onboarding_prompt(self, prompt: guild_models.OnboardingPrompt) -> "in_onboarding": prompt.in_onboarding, } - return payload - def serialize_onboarding_prompt_option( self, option: guild_models.OnboardingPromptOption ) -> data_binding.JSONObject: From 932d612267c76bdd1dc806c3be25996d53fff5ef Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:27:33 +0200 Subject: [PATCH 21/29] send ids as int --- hikari/impl/entity_factory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index cbf0a59db..89627bbf6 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1852,8 +1852,8 @@ def serialize_onboarding_prompt_option( ) -> data_binding.JSONObject: payload: typing.Dict[str, typing.Any] = { "id": option.id, - "channel_ids": [str(channel_id) for channel_id in option.channel_ids], - "role_ids": [str(role_id) for role_id in option.role_ids], + "channel_ids": option.channel_ids, + "role_ids": option.role_ids, "title": option.title, } From 10013f6dd53700c173ebcb40fcdb7d6ea778f8b6 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:32:43 +0200 Subject: [PATCH 22/29] forgot to run nox pipeline --- hikari/api/rest.py | 4 ++-- hikari/impl/rest.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index bfd6b9f95..1d076f990 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6489,11 +6489,11 @@ async def edit_guild_onboarding( guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], *, enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - default_channels: undefined.UndefinedNoneOr[ + default_channels: undefined.UndefinedOr[ typing.Sequence[snowflakes.SnowflakeishOr[channels_.GuildChannel]] ] = undefined.UNDEFINED, mode: undefined.UndefinedOr[guilds.OnboardingMode] = undefined.UNDEFINED, - prompts: undefined.UndefinedNoneOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, + prompts: undefined.UndefinedOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, reason: undefined.UndefinedOr[str] = undefined.UNDEFINED, ) -> guilds.GuildOnboarding: """Edit the onboarding of a community guild. diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 326c749b8..5e4656a35 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3657,11 +3657,11 @@ async def edit_guild_onboarding( guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], *, enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - default_channels: undefined.UndefinedNoneOr[ + default_channels: undefined.UndefinedOr[ typing.Sequence[snowflakes.SnowflakeishOr[channels_.GuildChannel]] ] = undefined.UNDEFINED, mode: undefined.UndefinedOr[guilds.OnboardingMode] = undefined.UNDEFINED, - prompts: undefined.UndefinedNoneOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, + prompts: undefined.UndefinedOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, reason: undefined.UndefinedOr[str] = undefined.UNDEFINED, ) -> guilds.GuildOnboarding: route = routes.PUT_GUILD_ONBOARDING.compile(guild=guild) From 5784ac4a38abe10a9707e7ed979eccb2abd40cc7 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:39:38 +0200 Subject: [PATCH 23/29] Changes repr, added docstrings --- hikari/guilds.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index 7588cbd60..b1e491bbe 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1332,10 +1332,10 @@ class GuildOnboarding: guild_id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the guild this onboarding is part of.""" - prompts: typing.Sequence[OnboardingPrompt] = attrs.field(repr=True) + prompts: typing.Sequence[OnboardingPrompt] = attrs.field(repr=False) """Prompts shown during onboarding and in customize community.""" - default_channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) + default_channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=False) """Channel IDs that members get opted into automatically.""" enabled: bool = attrs.field(repr=True) @@ -1356,7 +1356,7 @@ class OnboardingPrompt: type: OnboardingPromptType = attrs.field(repr=True) """The type of the onboarding prompt.""" - options: typing.List[OnboardingPromptOption] = attrs.field(repr=True) + options: typing.List[OnboardingPromptOption] = attrs.field(repr=False) """Options available within the prompt.""" title: str = attrs.field(repr=True) @@ -1368,7 +1368,7 @@ class OnboardingPrompt: required: bool = attrs.field(repr=True) """Indicates whether the prompt is required before a user completes the onboarding flow.""" - in_onboarding: bool = attrs.field(repr=True) + in_onboarding: bool = attrs.field(repr=False) """Indicates whether the prompt is present in the onboarding flow. If `[False][]`, the prompt will only appear in the "Channels & Roles" tab. @@ -1381,16 +1381,22 @@ class OnboardingPromptOption: """Used to represent an onboarding prompt option.""" id: snowflakes.Snowflake = attrs.field(repr=True) + """ID of the prompt option.""" - channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) + channel_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=False) + """IDs for channels a member is added to when the option is selected.""" - role_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=True) + role_ids: typing.Sequence[snowflakes.Snowflake] = attrs.field(repr=False) + """IDs for roles assigned to a member when the option is selected.""" emoji: typing.Optional[emojis_.Emoji] = attrs.field(repr=True) + """Emoji of the option.""" title: str = attrs.field(repr=True) + """Title of the option.""" description: typing.Optional[str] = attrs.field(repr=True) + """Description of the option.""" @attrs_extensions.with_copy From c0168267c036cc12adb386285aa0273305f48f53 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 12:58:03 +0200 Subject: [PATCH 24/29] Adding app field for usage in get_ and fetch_ utility functions --- hikari/guilds.py | 106 ++++++++++++++++++++++++++++++++++ hikari/impl/entity_factory.py | 2 + 2 files changed, 108 insertions(+) diff --git a/hikari/guilds.py b/hikari/guilds.py index b1e491bbe..b050d306f 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1329,6 +1329,11 @@ class OnboardingMode(int, enums.Enum): class GuildOnboarding: """Used to represent guild onboarding settings on Discord.""" + app: traits.RESTAware = attrs.field( + repr=False, eq=False, hash=False, metadata={attrs_extensions.SKIP_DEEP_COPY: True} + ) + """Client application that models may use for procedures.""" + guild_id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the guild this onboarding is part of.""" @@ -1344,6 +1349,61 @@ class GuildOnboarding: mode: OnboardingMode = attrs.field(repr=True) """The mode of onboarding.""" + def get_guild(self) -> typing.Optional[Guild]: + """Return the guild associated with this member. + + Returns + ------- + typing.Optional[hikari.guilds.Guild] + The linked guild object or [`None`][] if it's not cached. + """ + if not isinstance(self.app, traits.CacheAware): + return None + + return self.app.cache.get_guild(self.guild_id) + + def get_default_channels(self) -> typing.Sequence[channels_.GuildChannel]: + """Return the cached default channels for this guild. + + This will be empty if the channels are missing from the cache. + + Returns + ------- + typing.Sequence[hikari.channels.GuildChannel] + The default channels for this guild. + """ + channels: typing.List[channels_.GuildChannel] = [] + if not isinstance(self.app, traits.CacheAware): + return channels + + for channel_id in self.default_channel_ids: + if channel := self.app.cache.get_guild_channel(channel_id): + channels.append(channel) + + return channels + + async def fetch_self(self) -> GuildOnboarding: + """Fetch an up-to-date view of this guild onboarding from the API. + + Returns + ------- + hikari.guilds.GuildOnboarding + An up-to-date view of this guild onboarding. + + Raises + ------ + hikari.errors.UnauthorizedError + If you are unauthorized to make the request (invalid/missing token). + hikari.errors.NotFoundError + If the guild onboarding is not found. + hikari.errors.RateLimitTooLongError + Raised in the event that a rate limit occurs that is + longer than `max_rate_limit` when making a request. + hikari.errors.InternalServerError + If an internal error occurs on Discord while handling the request. + """ + return await self.app.rest.fetch_guild_onboarding(self.guild_id) + @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) @@ -1380,6 +1440,11 @@ class OnboardingPrompt: class OnboardingPromptOption: """Used to represent an onboarding prompt option.""" + app: traits.RESTAware = attrs.field( + repr=False, eq=False, hash=False, metadata={attrs_extensions.SKIP_DEEP_COPY: True} + ) + """Client application that models may use for procedures.""" + id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the prompt option.""" @@ -1398,6 +1463,47 @@ class OnboardingPromptOption: description: typing.Optional[str] = attrs.field(repr=True) """Description of the option.""" + def get_channels(self) -> typing.Sequence[channels_.GuildChannel]: + """Return the cached channels associated with this option. + + This will be empty if the channels are missing from the cache. + + Returns + ------- + typing.Sequence[hikari.channels.GuildChannel] + The cached channels associated with this option. + """ + channels: typing.List[channels_.GuildChannel] = [] + if not isinstance(self.app, traits.CacheAware): + return channels + + for channel_id in self.channel_ids: + if channel := self.app.cache.get_guild_channel(channel_id): + channels.append(channel) + + return channels + + def get_roles(self) -> typing.Sequence[Role]: + """Return the roles associated with this option. + + This will be empty if the roles are missing from the cache. + + Returns + ------- + typing.Sequence[hikari.guilds.Role] + The roles associated with this option. + """ + roles: typing.List[Role] = [] + + if not isinstance(self.app, traits.CacheAware): + return roles + + for role_id in self.role_ids: + if role := self.app.cache.get_role(role_id): + roles.append(role) + + return roles + @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 89627bbf6..8c8f1e374 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1788,6 +1788,7 @@ def deserialize_onboarding(self, payload: data_binding.JSONObject) -> guild_mode mode: guild_models.OnboardingMode = guild_models.OnboardingMode(payload["mode"]) return guild_models.GuildOnboarding( + app=self._app, guild_id=snowflakes.Snowflake(payload["guild_id"]), prompts=prompts, default_channel_ids=default_channel_ids, @@ -1828,6 +1829,7 @@ def deserialize_onboarding_prompt_option( emoji = self.deserialize_emoji(raw_emoji) return guild_models.OnboardingPromptOption( + app=self._app, id=snowflakes.Snowflake(payload["id"]), channel_ids=channel_ids, role_ids=role_ids, From 958da95e7aba8514d5534c8927d8e2b55c055a87 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 15:06:31 +0200 Subject: [PATCH 25/29] Screw utility functions --- hikari/guilds.py | 106 ---------------------------------- hikari/impl/entity_factory.py | 2 - 2 files changed, 108 deletions(-) diff --git a/hikari/guilds.py b/hikari/guilds.py index b050d306f..b1e491bbe 100644 --- a/hikari/guilds.py +++ b/hikari/guilds.py @@ -1329,11 +1329,6 @@ class OnboardingMode(int, enums.Enum): class GuildOnboarding: """Used to represent guild onboarding settings on Discord.""" - app: traits.RESTAware = attrs.field( - repr=False, eq=False, hash=False, metadata={attrs_extensions.SKIP_DEEP_COPY: True} - ) - """Client application that models may use for procedures.""" - guild_id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the guild this onboarding is part of.""" @@ -1349,61 +1344,6 @@ class GuildOnboarding: mode: OnboardingMode = attrs.field(repr=True) """The mode of onboarding.""" - def get_guild(self) -> typing.Optional[Guild]: - """Return the guild associated with this member. - - Returns - ------- - typing.Optional[hikari.guilds.Guild] - The linked guild object or [`None`][] if it's not cached. - """ - if not isinstance(self.app, traits.CacheAware): - return None - - return self.app.cache.get_guild(self.guild_id) - - def get_default_channels(self) -> typing.Sequence[channels_.GuildChannel]: - """Return the cached default channels for this guild. - - This will be empty if the channels are missing from the cache. - - Returns - ------- - typing.Sequence[hikari.channels.GuildChannel] - The default channels for this guild. - """ - channels: typing.List[channels_.GuildChannel] = [] - if not isinstance(self.app, traits.CacheAware): - return channels - - for channel_id in self.default_channel_ids: - if channel := self.app.cache.get_guild_channel(channel_id): - channels.append(channel) - - return channels - - async def fetch_self(self) -> GuildOnboarding: - """Fetch an up-to-date view of this guild onboarding from the API. - - Returns - ------- - hikari.guilds.GuildOnboarding - An up-to-date view of this guild onboarding. - - Raises - ------ - hikari.errors.UnauthorizedError - If you are unauthorized to make the request (invalid/missing token). - hikari.errors.NotFoundError - If the guild onboarding is not found. - hikari.errors.RateLimitTooLongError - Raised in the event that a rate limit occurs that is - longer than `max_rate_limit` when making a request. - hikari.errors.InternalServerError - If an internal error occurs on Discord while handling the request. - """ - return await self.app.rest.fetch_guild_onboarding(self.guild_id) - @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) @@ -1440,11 +1380,6 @@ class OnboardingPrompt: class OnboardingPromptOption: """Used to represent an onboarding prompt option.""" - app: traits.RESTAware = attrs.field( - repr=False, eq=False, hash=False, metadata={attrs_extensions.SKIP_DEEP_COPY: True} - ) - """Client application that models may use for procedures.""" - id: snowflakes.Snowflake = attrs.field(repr=True) """ID of the prompt option.""" @@ -1463,47 +1398,6 @@ class OnboardingPromptOption: description: typing.Optional[str] = attrs.field(repr=True) """Description of the option.""" - def get_channels(self) -> typing.Sequence[channels_.GuildChannel]: - """Return the cached channels associated with this option. - - This will be empty if the channels are missing from the cache. - - Returns - ------- - typing.Sequence[hikari.channels.GuildChannel] - The cached channels associated with this option. - """ - channels: typing.List[channels_.GuildChannel] = [] - if not isinstance(self.app, traits.CacheAware): - return channels - - for channel_id in self.channel_ids: - if channel := self.app.cache.get_guild_channel(channel_id): - channels.append(channel) - - return channels - - def get_roles(self) -> typing.Sequence[Role]: - """Return the roles associated with this option. - - This will be empty if the roles are missing from the cache. - - Returns - ------- - typing.Sequence[hikari.guilds.Role] - The roles associated with this option. - """ - roles: typing.List[Role] = [] - - if not isinstance(self.app, traits.CacheAware): - return roles - - for role_id in self.role_ids: - if role := self.app.cache.get_role(role_id): - roles.append(role) - - return roles - @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 8c8f1e374..89627bbf6 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -1788,7 +1788,6 @@ def deserialize_onboarding(self, payload: data_binding.JSONObject) -> guild_mode mode: guild_models.OnboardingMode = guild_models.OnboardingMode(payload["mode"]) return guild_models.GuildOnboarding( - app=self._app, guild_id=snowflakes.Snowflake(payload["guild_id"]), prompts=prompts, default_channel_ids=default_channel_ids, @@ -1829,7 +1828,6 @@ def deserialize_onboarding_prompt_option( emoji = self.deserialize_emoji(raw_emoji) return guild_models.OnboardingPromptOption( - app=self._app, id=snowflakes.Snowflake(payload["id"]), channel_ids=channel_ids, role_ids=role_ids, From bc44482c8c9af20d83136d807c719980b8f000c3 Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 15:49:49 +0200 Subject: [PATCH 26/29] Added audit log support for onboarding. --- hikari/audit_logs.py | 26 ++++++++++++++++++++++++++ hikari/impl/entity_factory.py | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/hikari/audit_logs.py b/hikari/audit_logs.py index 16af1c9ce..2fdb1fa47 100644 --- a/hikari/audit_logs.py +++ b/hikari/audit_logs.py @@ -265,6 +265,27 @@ class AuditLogChangeKey(str, enums.Enum): COMMUNICATION_DISABLED_UNTIL = "communication_disabled_until" """The datetime when a timeout will expire.""" + ENABLED = "enabled" + """Enabled.""" + + DEFAULT_CHANNEL_IDS = "default_channel_ids" + """Default Channel IDs.""" + + MODE = "mode" + """Mode.""" + + PROMPTS = "prompts" + """Prompts.""" + + OPTIONS = "options" + """Options.""" + + SINGLE_SELECT = "single_select" + """Single Select.""" + + REQUIRED = "required" + """Required.""" + # Who needs consistency? ADD_ROLE_TO_MEMBER = "$add" """Role added to a member.""" @@ -345,6 +366,11 @@ class AuditLogEventType(int, enums.Enum): THREAD_DELETE = 112 CREATOR_MONETIZATION_REQUEST_CREATED = 150 CREATOR_MONETIZATION_TERMS_ACCEPTED = 151 + ONBOARDING_PROMPT_CREATE = 163 + ONBOARDING_PROMPT_UPDATE = 164 + ONBOARDING_PROMPT_DELETE = 165 + ONBOARDING_CREATE = 166 + ONBOARDING_UPDATE = 167 @attrs.define(kw_only=True, weakref_slot=False) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 89627bbf6..291136aa0 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -486,6 +486,8 @@ def __init__(self, app: traits.RESTAware) -> None: audit_log_models.AuditLogChangeKey.REMOVE_ROLE_FROM_MEMBER: self._deserialize_audit_log_change_roles, audit_log_models.AuditLogChangeKey.PERMISSION_OVERWRITES: self._deserialize_audit_log_overwrites, audit_log_models.AuditLogChangeKey.COMMUNICATION_DISABLED_UNTIL: time.iso8601_datetime_string_to_datetime, + audit_log_models.AuditLogChangeKey.OPTIONS: self._deserialize_audit_log_change_options, + audit_log_models.AuditLogChangeKey.PROMPTS: self._deserialize_audit_log_change_prompts, } self._audit_log_event_mapping: typing.Dict[ typing.Union[int, audit_log_models.AuditLogEventType], @@ -772,6 +774,23 @@ def _deserialize_audit_log_overwrites( for overwrite in payload } + def _deserialize_audit_log_change_options( + self, payload: data_binding.JSONArray + ) -> typing.Mapping[snowflakes.Snowflake, guild_models.OnboardingPromptOption]: + return { + snowflakes.Snowflake(option["id"]): self.deserialize_onboarding_prompt_option(option) + for option in payload + } + + def _deserialize_audit_log_change_prompts( + self, payload: data_binding.JSONArray + ) -> typing.Mapping[snowflakes.Snowflake, guild_models.OnboardingPrompt]: + return { + snowflakes.Snowflake(prompt["id"]): self.deserialize_onboarding_prompt(prompt) + for prompt in payload + } + + def _deserialize_channel_overwrite_entry_info( self, payload: data_binding.JSONObject ) -> audit_log_models.ChannelOverwriteEntryInfo: From 3ad44b3b792d9e6feba1ef3c0cfc4d7c5d766cac Mon Sep 17 00:00:00 2001 From: ChrissisCodeXD Date: Tue, 6 Aug 2024 15:53:21 +0200 Subject: [PATCH 27/29] Forgot to run nox pipeline --- hikari/impl/entity_factory.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 291136aa0..2b8df5c0a 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -778,18 +778,13 @@ def _deserialize_audit_log_change_options( self, payload: data_binding.JSONArray ) -> typing.Mapping[snowflakes.Snowflake, guild_models.OnboardingPromptOption]: return { - snowflakes.Snowflake(option["id"]): self.deserialize_onboarding_prompt_option(option) - for option in payload + snowflakes.Snowflake(option["id"]): self.deserialize_onboarding_prompt_option(option) for option in payload } def _deserialize_audit_log_change_prompts( self, payload: data_binding.JSONArray ) -> typing.Mapping[snowflakes.Snowflake, guild_models.OnboardingPrompt]: - return { - snowflakes.Snowflake(prompt["id"]): self.deserialize_onboarding_prompt(prompt) - for prompt in payload - } - + return {snowflakes.Snowflake(prompt["id"]): self.deserialize_onboarding_prompt(prompt) for prompt in payload} def _deserialize_channel_overwrite_entry_info( self, payload: data_binding.JSONObject From f251876d4185ab9b5ec1645c408569eb6fa6fbdf Mon Sep 17 00:00:00 2001 From: syncblaze Date: Sat, 17 Aug 2024 19:27:51 +0200 Subject: [PATCH 28/29] change types --- hikari/impl/rest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 5e4656a35..326c749b8 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3657,11 +3657,11 @@ async def edit_guild_onboarding( guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], *, enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - default_channels: undefined.UndefinedOr[ + default_channels: undefined.UndefinedNoneOr[ typing.Sequence[snowflakes.SnowflakeishOr[channels_.GuildChannel]] ] = undefined.UNDEFINED, mode: undefined.UndefinedOr[guilds.OnboardingMode] = undefined.UNDEFINED, - prompts: undefined.UndefinedOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, + prompts: undefined.UndefinedNoneOr[typing.Sequence[guilds.OnboardingPrompt]] = undefined.UNDEFINED, reason: undefined.UndefinedOr[str] = undefined.UNDEFINED, ) -> guilds.GuildOnboarding: route = routes.PUT_GUILD_ONBOARDING.compile(guild=guild) From c151882f903adbb8f8417ff863bdb9424a463ae8 Mon Sep 17 00:00:00 2001 From: syncblaze Date: Sat, 17 Aug 2024 19:28:03 +0200 Subject: [PATCH 29/29] adding tests --- tests/hikari/impl/test_rest.py | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index 135fc0de7..5b9add611 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -5385,6 +5385,71 @@ async def test_edit_welcome_screen_without_optional_kwargs(self, rest_client): rest_client._request.return_value ) + async def test_fetch_guild_onboarding(self, rest_client): + rest_client._request = mock.AsyncMock(return_value={"haha": "funny"}) + expected_route = routes.GET_GUILD_ONBOARDING.compile(guild=123) + + result = await rest_client.fetch_guild_onboarding(StubModel(123)) + assert result is rest_client._entity_factory.deserialize_onboarding.return_value + + rest_client._request.assert_awaited_once_with(expected_route) + rest_client._entity_factory.deserialize_onboarding.assert_called_once_with( + rest_client._request.return_value + ) + + async def test_edit_guild_onboarding_with_optional_kwargs(self, rest_client): + mock_channel = StubModel(456) + mock_prompt = object() + rest_client._request = mock.AsyncMock(return_value={"haha": "funny"}) + expected_route = routes.PUT_GUILD_ONBOARDING.compile(guild=123) + + result = await rest_client.edit_guild_onboarding( + StubModel(123), + enabled=True, + default_channels=[mock_channel], + mode=guilds.OnboardingMode.ONBOARDING_DEFAULT, + reason="because i can", + prompts=[mock_prompt] + ) + assert result is rest_client._entity_factory.deserialize_onboarding.return_value + + rest_client._request.assert_awaited_once_with( + expected_route, + json={ + "enabled": True, + "default_channel_ids": ["456"], + "mode": guilds.OnboardingMode.ONBOARDING_DEFAULT, + "prompts": [rest_client._entity_factory.serialize_onboarding_prompt.return_value], + }, + reason="because i can", + ) + rest_client._entity_factory.deserialize_onboarding.assert_called_once_with( + rest_client._request.return_value + ) + rest_client._entity_factory.serialize_onboarding_prompt.assert_called_once_with(mock_prompt) + + async def test_edit_guild_onboarding_with_null_kwargs(self, rest_client): + rest_client._request = mock.AsyncMock(return_value={"haha": "funny"}) + expected_route = routes.PUT_GUILD_ONBOARDING.compile(guild=123) + + result = await rest_client.edit_guild_onboarding(StubModel(123), enabled=None, default_channels=None, mode=None, prompts=None) + assert result is rest_client._entity_factory.deserialize_onboarding.return_value + + rest_client._request.assert_awaited_once_with(expected_route, json={}, reason=undefined.UNDEFINED) + rest_client._entity_factory.deserialize_onboarding.assert_called_once_with(rest_client._request.return_value) + rest_client._entity_factory.serialize_onboarding_prompt.assert_not_called() + + async def test_edit_guild_onboarding_without_optional_kwargs(self, rest_client): + rest_client._request = mock.AsyncMock(return_value={"haha": "funny"}) + expected_route = routes.PUT_GUILD_ONBOARDING.compile(guild=123) + + result = await rest_client.edit_guild_onboarding(StubModel(123)) + assert result is rest_client._entity_factory.deserialize_onboarding.return_value + + rest_client._request.assert_awaited_once_with(expected_route, json={}, reason=undefined.UNDEFINED) + rest_client._entity_factory.deserialize_onboarding.assert_called_once_with(rest_client._request.return_value) + rest_client._entity_factory.serialize_onboarding_prompt.assert_not_called() + async def test_fetch_vanity_url(self, rest_client): vanity_url = StubModel(789) expected_route = routes.GET_GUILD_VANITY_URL.compile(guild=123)