From 357410993027ac2d21601c53879136c98dc0d3c6 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 1 Jul 2024 20:44:50 +0800 Subject: [PATCH 01/12] feature: add user installations --- hikari/api/rest.py | 8 ++++++++ hikari/api/special_endpoints.py | 34 ++++++++++++++++++++++++++++++++ hikari/commands.py | 29 +++++++++++++++++++++++++++ hikari/impl/entity_factory.py | 32 ++++++++++++++++++++++++++++++ hikari/impl/rest.py | 13 ++++++++++++ hikari/impl/special_endpoints.py | 22 +++++++++++++++++++++ tests/hikari/test_commands.py | 9 +++++++++ 7 files changed, 147 insertions(+) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index 23cc255d34..5f7e4a3802 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6684,6 +6684,8 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.CommandInteractionContextType] ) -> commands.SlashCommand: r"""Create an application slash command. @@ -6758,6 +6760,8 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.CommandInteractionContextType] ) -> commands.ContextMenuCommand: r"""Create an application context menu command. @@ -6820,6 +6824,10 @@ async def set_application_commands( ) -> typing.Sequence[commands.PartialCommand]: """Set the commands for an application. + !!! note + If you want to add user install command, be aware if you removed guild argument. + Discord doesn't say anything about it when creating command + !!! warning Any existing commands not included in the provided commands array will be deleted. diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py index 1ae3b71e9d..8ceff25eb5 100644 --- a/hikari/api/special_endpoints.py +++ b/hikari/api/special_endpoints.py @@ -1124,6 +1124,40 @@ def set_name_localizations( Object of this command builder. """ + @abc.abstractmethod + def set_integration_types( + self, integration_types: typing.Sequence[commands.CommandIntegrationType], / + ) -> Self: + """Set the command integration types + + Parameters + ---------- + integration_types + Integration types that show where command would be shown up + + Returns + ------- + CommandBuilder + Object of this command builder for chained calls. + """ + + @abc.abstractmethod + def set_contexts( + self, contexts: typing.Sequence[commands.CommandInteractionContextType], / + ) -> Self: + """Set the command contexts + + Parameters + ---------- + contexts + Where command can be used + + Returns + ------- + CommandBuilder + Object of this command builder for chained calls. + """ + @abc.abstractmethod def build(self, entity_factory: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]: """Build a JSON object from this builder. diff --git a/hikari/commands.py b/hikari/commands.py index a35fd23222..00198f0948 100644 --- a/hikari/commands.py +++ b/hikari/commands.py @@ -34,6 +34,8 @@ "CommandType", "GuildCommandPermissions", "OptionType", + "CommandIntegrationType", + "CommandInteractionContextType" ) import typing @@ -110,6 +112,27 @@ class OptionType(int, enums.Enum): """Denotes a command option where the value will be an attachment.""" +@typing.final +class CommandIntegrationType(int, enums.Enum): + GUILD_INSTALL = 0 + """A guild install command integration type""" + + USER_INSTALL = 1 + """A user install command integration type""" + + +@typing.final +class CommandInteractionContextType(int, enums.Enum): + GUILD = 0 + """Interaction can be used within server""" + + BOT_DM = 1 + """Interaction can be used within DM's""" + + PRIVATE_CHANNEL = 2 + """Interaction can be used within group DM's and DM's""" + + @attrs_extensions.with_copy @attrs.define(hash=False, kw_only=True, weakref_slot=False) class CommandChoice: @@ -265,6 +288,12 @@ class PartialCommand(snowflakes.Unique): ) """A mapping of name localizations for this command.""" + integration_types: typing.Sequence[CommandIntegrationType] = attrs.field(eq=False, hash=False, repr=True) + """A sequence of command integration types""" + + contexts: typing.Sequence[CommandInteractionContextType] = attrs.field(eq=False, hash=False, repr=True) + """A sequence of command contexts""" + async def fetch_self(self) -> PartialCommand: """Fetch an up-to-date version of this command object. diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 4ddb940b7b..158aa5bf42 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -2279,6 +2279,21 @@ def deserialize_slash_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) + integration_types: typing.Sequence[commands.CommandIntegrationType] + if raw_integration_types := payload.get("integration_types"): + integration_types = [commands.CommandIntegrationType(integration_type) for integration_type in raw_integration_types] + else: + integration_types = [commands.CommandIntegrationType.GUILD_INSTALL] + + contexts: typing.Sequence[commands.CommandInteractionContextType] + if raw_contexts := payload.get("contexts"): + contexts = [commands.CommandInteractionContextType(context) for context in raw_contexts] + else: + contexts = [ + commands.CommandInteractionContextType.GUILD, + commands.CommandInteractionContextType.BOT_DM + ] + return commands.SlashCommand( app=self._app, id=snowflakes.Snowflake(payload["id"]), @@ -2294,6 +2309,8 @@ def deserialize_slash_command( version=snowflakes.Snowflake(payload["version"]), name_localizations=name_localizations, description_localizations=description_localizations, + integration_types=integration_types, + contexts=contexts ) def deserialize_context_menu_command( @@ -2320,6 +2337,19 @@ def deserialize_context_menu_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) + integration_types: typing.Sequence[commands.CommandIntegrationType] + if raw_integration_types := payload.get("integration_types"): + integration_types = [commands.CommandIntegrationType(integration_type) for integration_type in + raw_integration_types] + else: + integration_types = [commands.CommandIntegrationType.GUILD_INSTALL] + + contexts: typing.Sequence[commands.CommandInteractionContextType] + if raw_contexts := payload.get("contexts"): + contexts = [commands.CommandInteractionContextType(context) for context in raw_contexts] + else: + contexts = [commands.CommandInteractionContextType.GUILD] + return commands.ContextMenuCommand( app=self._app, id=snowflakes.Snowflake(payload["id"]), @@ -2332,6 +2362,8 @@ def deserialize_context_menu_command( guild_id=guild_id, version=snowflakes.Snowflake(payload["version"]), name_localizations=name_localizations, + integration_types=integration_types, + contexts=contexts ) def deserialize_command( diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 9d3d4b5f1d..4f5fd6f597 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3760,6 +3760,8 @@ async def _create_application_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.CommandInteractionContextType] = undefined.UNDEFINED ) -> data_binding.JSONObject: if guild is undefined.UNDEFINED: route = routes.POST_APPLICATION_COMMAND.compile(application=application) @@ -3780,8 +3782,11 @@ async def _create_application_command( # but we consider it to be the same as None for developer sanity reasons body.put("default_member_permissions", None if default_member_permissions == 0 else default_member_permissions) body.put("dm_permission", dm_enabled) + body.put("integration_types", integration_types) + body.put("contexts", contexts) response = await self._request(route, json=body) + assert isinstance(response, dict) return response @@ -3804,6 +3809,8 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.CommandInteractionContextType] ) -> commands.SlashCommand: response = await self._create_application_command( application=application, @@ -3817,6 +3824,8 @@ async def create_slash_command( default_member_permissions=default_member_permissions, dm_enabled=dm_enabled, nsfw=nsfw, + integration_types=integration_types, + contexts=contexts ) return self._entity_factory.deserialize_slash_command( response, guild_id=snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None @@ -3837,6 +3846,8 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.CommandInteractionContextType] ) -> commands.ContextMenuCommand: response = await self._create_application_command( application=application, @@ -3847,6 +3858,8 @@ async def create_context_menu_command( default_member_permissions=default_member_permissions, dm_enabled=dm_enabled, nsfw=nsfw, + integration_types=integration_types, + contexts=contexts ) return self._entity_factory.deserialize_context_menu_command( response, guild_id=snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index afdcf664ea..f495372147 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -1284,6 +1284,14 @@ class CommandBuilder(special_endpoints.CommandBuilder): alias="name_localizations", factory=dict, kw_only=True ) + _integration_types: typing.Sequence[commands.CommandIntegrationType] = attrs.field( + alias="integration_types", default=undefined.UNDEFINED, kw_only=True + ) + + _contexts: typing.Sequence[commands.CommandInteractionContextType] = attrs.field( + alias="contexts", default=undefined.UNDEFINED, kw_only=True + ) + @property def id(self) -> undefined.UndefinedOr[snowflakes.Snowflake]: return self._id @@ -1336,6 +1344,18 @@ def set_name_localizations( self._name_localizations = name_localizations return self + def set_integration_types( + self, integration_types: typing.Sequence[commands.CommandIntegrationType], / + ) -> Self: + self._integration_types = integration_types + return self + + def set_contexts( + self, contexts: typing.Sequence[commands.CommandInteractionContextType], / + ) -> Self: + self._contexts = contexts + return self + def build(self, _: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]: data = data_binding.JSONObjectBuilder() data["name"] = self._name @@ -1344,6 +1364,8 @@ def build(self, _: entity_factory_.EntityFactory, /) -> typing.MutableMapping[st data.put("name_localizations", self._name_localizations) data.put("dm_permission", self._is_dm_enabled) data.put("nsfw", self._is_nsfw) + data.put_array("integration_types", self._integration_types) + data.put_array("contexts", self._contexts) # Discord considers 0 the same thing as ADMINISTRATORS, but we make it nicer to work with # by using it correctly. diff --git a/tests/hikari/test_commands.py b/tests/hikari/test_commands.py index e22f95ee05..c078b9744d 100644 --- a/tests/hikari/test_commands.py +++ b/tests/hikari/test_commands.py @@ -49,6 +49,15 @@ def mock_command(self, mock_app): guild_id=snowflakes.Snowflake(31231235), version=snowflakes.Snowflake(43123123), name_localizations={}, + integration_types=[ + commands.CommandIntegrationType.GUILD_INSTALL, + commands.CommandIntegrationType.USER_INSTALL + ], + contexts=[ + commands.CommandInteractionContextType.GUILD, + commands.CommandInteractionContextType.BOT_DM, + commands.CommandInteractionContextType.PRIVATE_CHANNEL + ] ) @pytest.mark.asyncio From 1f6b410799ba5327ab913849b266f1ea9bbfff4b Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 1 Jul 2024 20:58:21 +0800 Subject: [PATCH 02/12] Add tests --- hikari/api/rest.py | 4 ++-- tests/hikari/impl/test_entity_factory.py | 2 ++ tests/hikari/impl/test_rest.py | 2 ++ tests/hikari/impl/test_special_endpoints.py | 10 ++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index 5f7e4a3802..203cb3b0e0 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6825,8 +6825,8 @@ async def set_application_commands( """Set the commands for an application. !!! note - If you want to add user install command, be aware if you removed guild argument. - Discord doesn't say anything about it when creating command + When creating user commands, make sure to not pass the `guild` argument. + There is no feedback from Discord when this happens and commands will not be created properly !!! warning Any existing commands not included in the provided commands array diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index c3d195af13..d77effa9f3 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -4056,6 +4056,8 @@ def test_deserialize_slash_command(self, entity_factory_impl, mock_app, slash_co assert command.is_dm_enabled is False assert command.is_nsfw is True assert command.version == 123321123 + assert command.integration_types == [commands.CommandIntegrationType.GUILD_INSTALL] + assert command.contexts == [commands.CommandInteractionContextType.GUILD, commands.CommandInteractionContextType.BOT_DM] # CommandOption assert len(command.options) == 1 diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index a781f1f6ca..222598762a 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -5517,6 +5517,8 @@ async def test__create_application_command_with_optionals(self, rest_client: res default_member_permissions=permissions.Permissions.ADMINISTRATOR, dm_enabled=False, nsfw=True, + integration_types=[commands.CommandIntegrationType.GUILD_INSTALL], + contexts=[commands.CommandInteractionContextType.GUILD, commands.CommandInteractionContextType.BOT_DM] ) assert result is rest_client._request.return_value diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py index 6274f2d5f3..52bdd3164a 100644 --- a/tests/hikari/impl/test_special_endpoints.py +++ b/tests/hikari/impl/test_special_endpoints.py @@ -1037,6 +1037,16 @@ def test_name_localizations_property(self, stub_command): assert builder.name_localizations == {"aaa": "bbb", "ccc": "DDd"} + def test_set_integration_types(self, stub_command): + builder = stub_command("oksksksk").set_integration_types([commands.CommandIntegrationType.GUILD_INSTALL]) + + assert builder.integration_types == [commands.CommandIntegrationType.GUILD_INSTALL] + + def test_set_contexts(self, stub_contexts): + builder = stub_command("oksksksk").set_contexts([commands.CommandInteractionContextType.BOT_DM]) + + assert builder.integration_types == [commands.CommandInteractionContextType.BOT_DM] + class TestSlashCommandBuilder: def test_description_property(self): From f87461889db5d142e41b5de9c0e383dc31be4e54 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 1 Jul 2024 22:05:09 +0800 Subject: [PATCH 03/12] Towncrier --- changes/1952.feature.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/1952.feature.md diff --git a/changes/1952.feature.md b/changes/1952.feature.md new file mode 100644 index 0000000000..f850e380e4 --- /dev/null +++ b/changes/1952.feature.md @@ -0,0 +1 @@ +Add support for user install commands From 3ac186fb7ba214fea7be230dbb042b559b0b62c1 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Sun, 7 Jul 2024 01:30:10 +0800 Subject: [PATCH 04/12] Additions because [changelog](https://discord.com/developers/docs/resources/application#application-object) --- hikari/api/rest.py | 10 +++-- hikari/api/special_endpoints.py | 5 ++- hikari/applications.py | 25 ++++++++++++ hikari/commands.py | 34 +++------------- hikari/impl/entity_factory.py | 39 +++++++++++-------- hikari/impl/rest.py | 17 ++++---- hikari/impl/special_endpoints.py | 8 ++-- hikari/interactions/base_interactions.py | 8 +++- hikari/interactions/command_interactions.py | 2 +- hikari/interactions/component_interactions.py | 2 +- hikari/interactions/modal_interactions.py | 2 +- tests/hikari/impl/test_entity_factory.py | 4 +- tests/hikari/impl/test_rest.py | 4 +- tests/hikari/impl/test_special_endpoints.py | 8 ++-- tests/hikari/test_commands.py | 13 ++++--- 15 files changed, 101 insertions(+), 80 deletions(-) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index 203cb3b0e0..cc85d0b522 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6684,8 +6684,8 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.CommandInteractionContextType] + integration_types: typing.Sequence[commands.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[commands.ApplicationInstallationContext] ) -> commands.SlashCommand: r"""Create an application slash command. @@ -6760,8 +6760,8 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.CommandInteractionContextType] + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> commands.ContextMenuCommand: r"""Create an application context menu command. @@ -6880,6 +6880,8 @@ async def edit_application_command( undefined.UndefinedType, int, permissions_.Permissions ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> commands.PartialCommand: """Edit a registered application command. diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py index 8ceff25eb5..e943164585 100644 --- a/hikari/api/special_endpoints.py +++ b/hikari/api/special_endpoints.py @@ -59,6 +59,7 @@ from typing_extensions import Self + from hikari import applications from hikari import channels from hikari import colors from hikari import commands @@ -1126,7 +1127,7 @@ def set_name_localizations( @abc.abstractmethod def set_integration_types( - self, integration_types: typing.Sequence[commands.CommandIntegrationType], / + self, integration_types: typing.Sequence[applications.ApplicationIntegrationType], / ) -> Self: """Set the command integration types @@ -1143,7 +1144,7 @@ def set_integration_types( @abc.abstractmethod def set_contexts( - self, contexts: typing.Sequence[commands.CommandInteractionContextType], / + self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], / ) -> Self: """Set the command contexts diff --git a/hikari/applications.py b/hikari/applications.py index 41ea209ce2..705d64a419 100644 --- a/hikari/applications.py +++ b/hikari/applications.py @@ -45,6 +45,8 @@ "ApplicationRoleConnectionMetadataRecordType", "ApplicationRoleConnectionMetadataRecord", "get_token_id", + "ApplicationIntegrationType", + "ApplicationInstallationContextType" ) import base64 @@ -71,6 +73,27 @@ from hikari import webhooks +@typing.final +class ApplicationIntegrationType(int, enums.Enum): + GUILD_INSTALL = 0 + """A guild install command integration type""" + + USER_INSTALL = 1 + """A user install command integration type""" + + +@typing.final +class ApplicationInstallationContextType(int, enums.Enum): + GUILD = 0 + """Interaction can be used within server""" + + BOT_DM = 1 + """Interaction can be used within DM's""" + + PRIVATE_CHANNEL = 2 + """Interaction can be used within group DM's and DM's""" + + @typing.final class ApplicationFlags(enums.Flag): """The known application flag bits.""" @@ -632,6 +655,8 @@ class Application(guilds.PartialApplication): install_parameters: typing.Optional[ApplicationInstallParameters] = attrs.field(eq=False, hash=False, repr=False) """Settings for the application's default in-app authorization link, if enabled.""" + # integration_types_config: typing.Optional[typing.Mapping[]] + approximate_guild_count: int = attrs.field(eq=False, hash=False, repr=False) """The approximate number of guilds this application is part of.""" diff --git a/hikari/commands.py b/hikari/commands.py index 00198f0948..2a19990efb 100644 --- a/hikari/commands.py +++ b/hikari/commands.py @@ -33,9 +33,7 @@ "CommandPermissionType", "CommandType", "GuildCommandPermissions", - "OptionType", - "CommandIntegrationType", - "CommandInteractionContextType" + "OptionType" ) import typing @@ -53,6 +51,7 @@ from hikari import channels from hikari import guilds from hikari import locales + from hikari import applications class CommandType(int, enums.Enum): @@ -112,27 +111,6 @@ class OptionType(int, enums.Enum): """Denotes a command option where the value will be an attachment.""" -@typing.final -class CommandIntegrationType(int, enums.Enum): - GUILD_INSTALL = 0 - """A guild install command integration type""" - - USER_INSTALL = 1 - """A user install command integration type""" - - -@typing.final -class CommandInteractionContextType(int, enums.Enum): - GUILD = 0 - """Interaction can be used within server""" - - BOT_DM = 1 - """Interaction can be used within DM's""" - - PRIVATE_CHANNEL = 2 - """Interaction can be used within group DM's and DM's""" - - @attrs_extensions.with_copy @attrs.define(hash=False, kw_only=True, weakref_slot=False) class CommandChoice: @@ -288,11 +266,11 @@ class PartialCommand(snowflakes.Unique): ) """A mapping of name localizations for this command.""" - integration_types: typing.Sequence[CommandIntegrationType] = attrs.field(eq=False, hash=False, repr=True) - """A sequence of command integration types""" + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = attrs.field(eq=False, hash=False, repr=True) + """A sequence of command integration types.""" - contexts: typing.Sequence[CommandInteractionContextType] = attrs.field(eq=False, hash=False, repr=True) - """A sequence of command contexts""" + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = attrs.field(eq=False, hash=False, repr=True) + """A sequence of command contexts.""" async def fetch_self(self) -> PartialCommand: """Fetch an up-to-date version of this command object. diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 158aa5bf42..4456bd1e12 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -2279,19 +2279,19 @@ def deserialize_slash_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) - integration_types: typing.Sequence[commands.CommandIntegrationType] + integration_types: typing.Sequence[commands.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [commands.CommandIntegrationType(integration_type) for integration_type in raw_integration_types] + integration_types = [commands.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] else: - integration_types = [commands.CommandIntegrationType.GUILD_INSTALL] + integration_types = [commands.ApplicationIntegrationType.GUILD_INSTALL] - contexts: typing.Sequence[commands.CommandInteractionContextType] + contexts: typing.Sequence[commands.ApplicationInstallationContext] if raw_contexts := payload.get("contexts"): - contexts = [commands.CommandInteractionContextType(context) for context in raw_contexts] + contexts = [commands.ApplicationInstallationContext(context) for context in raw_contexts] else: contexts = [ - commands.CommandInteractionContextType.GUILD, - commands.CommandInteractionContextType.BOT_DM + commands.ApplicationInstallationContext.GUILD, + commands.ApplicationInstallationContext.BOT_DM ] return commands.SlashCommand( @@ -2337,18 +2337,18 @@ def deserialize_context_menu_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) - integration_types: typing.Sequence[commands.CommandIntegrationType] + integration_types: typing.Sequence[commands.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [commands.CommandIntegrationType(integration_type) for integration_type in + integration_types = [commands.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] else: - integration_types = [commands.CommandIntegrationType.GUILD_INSTALL] + integration_types = [commands.ApplicationIntegrationType.GUILD_INSTALL] - contexts: typing.Sequence[commands.CommandInteractionContextType] + contexts: typing.Sequence[commands.ApplicationInstallationContext] if raw_contexts := payload.get("contexts"): - contexts = [commands.CommandInteractionContextType(context) for context in raw_contexts] + contexts = [commands.ApplicationInstallationContext(context) for context in raw_contexts] else: - contexts = [commands.CommandInteractionContextType.GUILD] + contexts = [commands.ApplicationInstallationContext.GUILD] return commands.ContextMenuCommand( app=self._app, @@ -2587,8 +2587,7 @@ def deserialize_command_interaction( if raw_target_id := data_payload.get("target_id"): target_id = snowflakes.Snowflake(raw_target_id) - app_perms = payload.get("app_permissions") - + app_perms = payload["app_permissions"] entitlements = [self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())] return command_interactions.CommandInteraction( @@ -2613,6 +2612,8 @@ def deserialize_command_interaction( app_permissions=permission_models.Permissions(app_perms) if app_perms else None, registered_guild_id=snowflakes.Snowflake(data_payload["guild_id"]) if "guild_id" in data_payload else None, entitlements=entitlements, + authorizing_integration_owners=payload["authorizing_integration_owners"], + context=payload.get("context") ) def deserialize_autocomplete_interaction( @@ -2656,6 +2657,8 @@ def deserialize_autocomplete_interaction( guild_locale=locales.Locale(payload["guild_locale"]) if "guild_locale" in payload else None, registered_guild_id=snowflakes.Snowflake(data_payload["guild_id"]) if "guild_id" in data_payload else None, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], + authorizing_integration_owners=payload["authorizing_integration_owners"], + context=payload.get("context") ) def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> modal_interactions.ModalInteraction: @@ -2680,7 +2683,7 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod if message_payload := payload.get("message"): message = self.deserialize_message(message_payload) - app_perms = payload.get("app_permissions") + app_perms = payload["app_permissions"] return modal_interactions.ModalInteraction( app=self._app, application_id=snowflakes.Snowflake(payload["application_id"]), @@ -2699,6 +2702,8 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod components=self._deserialize_components(data_payload["components"], self._modal_component_type_mapping), message=message, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], + authorizing_integration_owners=payload["authorizing_integration_owners"], + context=payload.get("context") ) def deserialize_interaction(self, payload: data_binding.JSONObject) -> base_interactions.PartialInteraction: @@ -2771,7 +2776,7 @@ def deserialize_component_interaction( if resolved_payload := data_payload.get("resolved"): resolved = self._deserialize_resolved_option_data(resolved_payload, guild_id=guild_id) - app_perms = payload.get("app_permissions") + app_perms = payload["app_permissions"] return component_interactions.ComponentInteraction( app=self._app, application_id=snowflakes.Snowflake(payload["application_id"]), diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 4f5fd6f597..64e7a74043 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3760,8 +3760,8 @@ async def _create_application_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.CommandInteractionContextType] = undefined.UNDEFINED + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> data_binding.JSONObject: if guild is undefined.UNDEFINED: route = routes.POST_APPLICATION_COMMAND.compile(application=application) @@ -3809,8 +3809,8 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.CommandInteractionContextType] + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> commands.SlashCommand: response = await self._create_application_command( application=application, @@ -3846,8 +3846,8 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.CommandIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.CommandInteractionContextType] + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> commands.ContextMenuCommand: response = await self._create_application_command( application=application, @@ -3896,6 +3896,8 @@ async def edit_application_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED ) -> commands.PartialCommand: if guild is undefined.UNDEFINED: route = routes.PATCH_APPLICATION_COMMAND.compile(application=application, command=command) @@ -3913,7 +3915,8 @@ async def edit_application_command( # Discord has some funky behaviour around what 0 means. They consider it to be the same as ADMINISTRATOR, # but we consider it to be the same as None for developer sanity reasons body.put("default_member_permissions", None if default_member_permissions == 0 else default_member_permissions) - body.put("dm_permission", dm_enabled) + body.put("integration_types", integration_types) + body.put("contexts", contexts) response = await self._request(route, json=body) assert isinstance(response, dict) diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index f495372147..032609548c 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -1284,11 +1284,11 @@ class CommandBuilder(special_endpoints.CommandBuilder): alias="name_localizations", factory=dict, kw_only=True ) - _integration_types: typing.Sequence[commands.CommandIntegrationType] = attrs.field( + _integration_types: typing.Sequence[applications.ApplicationIntegrationType] = attrs.field( alias="integration_types", default=undefined.UNDEFINED, kw_only=True ) - _contexts: typing.Sequence[commands.CommandInteractionContextType] = attrs.field( + _contexts: typing.Sequence[applications.ApplicationInstallationContextType] = attrs.field( alias="contexts", default=undefined.UNDEFINED, kw_only=True ) @@ -1345,13 +1345,13 @@ def set_name_localizations( return self def set_integration_types( - self, integration_types: typing.Sequence[commands.CommandIntegrationType], / + self, integration_types: typing.Sequence[applications.ApplicationIntegrationType], / ) -> Self: self._integration_types = integration_types return self def set_contexts( - self, contexts: typing.Sequence[commands.CommandInteractionContextType], / + self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], / ) -> Self: self._contexts = contexts return self diff --git a/hikari/interactions/base_interactions.py b/hikari/interactions/base_interactions.py index d9ad25e758..d5f22955f0 100644 --- a/hikari/interactions/base_interactions.py +++ b/hikari/interactions/base_interactions.py @@ -57,9 +57,9 @@ from hikari import permissions as permissions_ from hikari import traits from hikari import users + from hikari import applications from hikari.api import special_endpoints - _CommandResponseTypesT = typing.TypeVar("_CommandResponseTypesT", bound=int) @@ -219,6 +219,12 @@ class PartialInteraction(snowflakes.Unique, webhooks.ExecutableWebhook): version: int = attrs.field(eq=False, repr=True) """Version of the interaction system this interaction is under.""" + authorizing_integration_owners: typing.Mapping[applications.ApplicationIntegrationType, snowflakes.Snowflake] = attrs.field(eq=False, repr=False) + """Mapping installation contexts authorized for interaction to related user or guild IDs.""" + + context: typing.Optional[applications.ApplicationInstallationContextType] = attrs.field(eq=False, repr=False) + """A context where interaction was triggerred""" + @property def webhook_id(self) -> snowflakes.Snowflake: # <>. diff --git a/hikari/interactions/command_interactions.py b/hikari/interactions/command_interactions.py index 32cda9e8ef..ccc92db6d3 100644 --- a/hikari/interactions/command_interactions.py +++ b/hikari/interactions/command_interactions.py @@ -315,7 +315,7 @@ class CommandInteraction( ): """Represents a command interaction on Discord.""" - app_permissions: typing.Optional[permissions_.Permissions] = attrs.field(eq=False, hash=False, repr=False) + app_permissions: permissions_.Permissions = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" options: typing.Optional[typing.Sequence[CommandInteractionOption]] = attrs.field(eq=False, hash=False, repr=True) diff --git a/hikari/interactions/component_interactions.py b/hikari/interactions/component_interactions.py index c0bc3f7993..5312d0863d 100644 --- a/hikari/interactions/component_interactions.py +++ b/hikari/interactions/component_interactions.py @@ -147,7 +147,7 @@ class ComponentInteraction( locale: typing.Union[str, locales.Locale] = attrs.field(eq=False, hash=False, repr=True) """The selected language of the user who triggered this component interaction.""" - app_permissions: typing.Optional[permissions.Permissions] = attrs.field(eq=False, hash=False, repr=False) + app_permissions: permissions.Permissions = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" entitlements: typing.Sequence[monetization.Entitlement] = attrs.field(eq=False, hash=False, repr=True) diff --git a/hikari/interactions/modal_interactions.py b/hikari/interactions/modal_interactions.py index c11fe624fa..18adbe7a67 100644 --- a/hikari/interactions/modal_interactions.py +++ b/hikari/interactions/modal_interactions.py @@ -119,7 +119,7 @@ class ModalInteraction( locale: str = attrs.field(eq=False, hash=False, repr=True) """The selected language of the user who triggered this modal interaction.""" - app_permissions: typing.Optional[permissions.Permissions] = attrs.field(eq=False, hash=False, repr=False) + app_permissions: permissions.Permissions = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" components: typing.Sequence[components_.ModalActionRowComponent] = attrs.field(eq=False, hash=False, repr=True) diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index d77effa9f3..b6222202f9 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -4056,8 +4056,8 @@ def test_deserialize_slash_command(self, entity_factory_impl, mock_app, slash_co assert command.is_dm_enabled is False assert command.is_nsfw is True assert command.version == 123321123 - assert command.integration_types == [commands.CommandIntegrationType.GUILD_INSTALL] - assert command.contexts == [commands.CommandInteractionContextType.GUILD, commands.CommandInteractionContextType.BOT_DM] + assert command.integration_types == [application_models.ApplicationIntegrationType.GUILD_INSTALL] + assert command.contexts == [application_models.ApplicationInstallationContextType.GUILD, application_models.ApplicationInstallationContextType.BOT_DM] # CommandOption assert len(command.options) == 1 diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index 222598762a..ccaf5b048b 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -5517,8 +5517,8 @@ async def test__create_application_command_with_optionals(self, rest_client: res default_member_permissions=permissions.Permissions.ADMINISTRATOR, dm_enabled=False, nsfw=True, - integration_types=[commands.CommandIntegrationType.GUILD_INSTALL], - contexts=[commands.CommandInteractionContextType.GUILD, commands.CommandInteractionContextType.BOT_DM] + integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], + contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] ) assert result is rest_client._request.return_value diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py index 52bdd3164a..e4f257f937 100644 --- a/tests/hikari/impl/test_special_endpoints.py +++ b/tests/hikari/impl/test_special_endpoints.py @@ -1038,14 +1038,14 @@ def test_name_localizations_property(self, stub_command): assert builder.name_localizations == {"aaa": "bbb", "ccc": "DDd"} def test_set_integration_types(self, stub_command): - builder = stub_command("oksksksk").set_integration_types([commands.CommandIntegrationType.GUILD_INSTALL]) + builder = stub_command("oksksksk").set_integration_types([commands.ApplicationIntegrationType.GUILD_INSTALL]) - assert builder.integration_types == [commands.CommandIntegrationType.GUILD_INSTALL] + assert builder.integration_types == [commands.ApplicationIntegrationType.GUILD_INSTALL] def test_set_contexts(self, stub_contexts): - builder = stub_command("oksksksk").set_contexts([commands.CommandInteractionContextType.BOT_DM]) + builder = stub_command("oksksksk").set_contexts([commands.ApplicationInstallationContext.BOT_DM]) - assert builder.integration_types == [commands.CommandInteractionContextType.BOT_DM] + assert builder.integration_types == [commands.ApplicationInstallationContext.BOT_DM] class TestSlashCommandBuilder: diff --git a/tests/hikari/test_commands.py b/tests/hikari/test_commands.py index c078b9744d..88070132fe 100644 --- a/tests/hikari/test_commands.py +++ b/tests/hikari/test_commands.py @@ -23,6 +23,7 @@ import pytest from hikari import commands +from hikari import applications from hikari import snowflakes from hikari import traits from hikari import undefined @@ -40,7 +41,7 @@ def mock_command(self, mock_app): return hikari_test_helpers.mock_class_namespace(commands.PartialCommand)( app=mock_app, id=snowflakes.Snowflake(34123123), - type=commands.CommandType.SLASH, + type=applications.CommandType.SLASH, application_id=snowflakes.Snowflake(65234123), name="Name", default_member_permissions=None, @@ -50,13 +51,13 @@ def mock_command(self, mock_app): version=snowflakes.Snowflake(43123123), name_localizations={}, integration_types=[ - commands.CommandIntegrationType.GUILD_INSTALL, - commands.CommandIntegrationType.USER_INSTALL + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL ], contexts=[ - commands.CommandInteractionContextType.GUILD, - commands.CommandInteractionContextType.BOT_DM, - commands.CommandInteractionContextType.PRIVATE_CHANNEL + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + applications.ApplicationInstallationContext.PRIVATE_CHANNEL ] ) From 95c92c223f36857bbe9ad33a2668f271241a019e Mon Sep 17 00:00:00 2001 From: MagM1go Date: Sun, 7 Jul 2024 01:31:45 +0800 Subject: [PATCH 05/12] typing --- hikari/api/rest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index cc85d0b522..e8a83b7fc9 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6684,8 +6684,8 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[commands.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[commands.ApplicationInstallationContext] + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, + contexts: typing.Sequence[applications.ApplicationInstallationContextType] ) -> commands.SlashCommand: r"""Create an application slash command. From 66963592a1b95b6985c7fdc18b5c90f081ff13d5 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 16:39:48 +0800 Subject: [PATCH 06/12] more tests --- tests/hikari/impl/test_entity_factory.py | 22 +++++++++++++++++++ tests/hikari/impl/test_rest.py | 13 +++++++++++ tests/hikari/impl/test_special_endpoints.py | 11 +++++----- .../interactions/test_base_interactions.py | 7 ++++++ .../interactions/test_command_interactions.py | 5 +++++ .../test_component_interactions.py | 3 +++ .../interactions/test_modal_interactions.py | 3 +++ tests/hikari/test_commands.py | 4 ++-- 8 files changed, 61 insertions(+), 7 deletions(-) diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index b6222202f9..b4fc661461 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -4210,6 +4210,8 @@ def partial_interaction_payload(self): "type": 1, "version": 1, "application_id": "1", + "authorizing_integration_owners": {0: 12345}, + "context": 0 } def test_deserialize_partial_interaction(self, mock_app, entity_factory_impl, partial_interaction_payload): @@ -4221,6 +4223,8 @@ def test_deserialize_partial_interaction(self, mock_app, entity_factory_impl, pa assert interaction.type == 1 assert interaction.version == 1 assert interaction.application_id == 1 + assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.context == application_models.ApplicationInstallationContextType.GUILD assert type(interaction) is base_interactions.PartialInteraction @pytest.fixture @@ -4415,6 +4419,8 @@ def command_interaction_payload(self, interaction_member_payload, interaction_re "subscription_id": "1019653835926409216", } ], + "authorizing_integration_owners": {0: 12345}, + "context": 0 } def test_deserialize_command_interaction( @@ -4451,6 +4457,8 @@ def test_deserialize_command_interaction( assert len(interaction.entitlements) == 1 assert interaction.entitlements[0].id == 696969696969696 assert interaction.registered_guild_id == 12345678 + assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.context == 0 # CommandInteractionOption assert len(interaction.options) == 1 @@ -4514,6 +4522,8 @@ def context_menu_command_interaction_payload(self, interaction_member_payload, u "subscription_id": "1019653835926409216", } ], + "authorizing_integration_owners": {0: 12345}, + "context": 0 } def test_deserialize_command_interaction_with_context_menu_field( @@ -4590,6 +4600,8 @@ def autocomplete_interaction_payload(self, member_payload, user_payload, interac "subscription_id": "1019653835926409216", } ], + "authorizing_integration_owners": {0: 12345}, + "context": 0 } def test_deserialize_autocomplete_interaction( @@ -4617,6 +4629,8 @@ def test_deserialize_autocomplete_interaction( assert interaction.locale is locales.Locale.ES_ES assert interaction.guild_locale is locales.Locale.EN_US assert interaction.registered_guild_id == 12345678 + assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.context == 0 # AutocompleteInteractionOption assert len(interaction.options) == 1 @@ -4858,6 +4872,8 @@ def component_interaction_payload( "subscription_id": "1019653835926409216", } ], + "authorizing_integration_owners": {0: 12345}, + "context": 0 } def test_deserialize_component_interaction( @@ -4892,14 +4908,20 @@ def test_deserialize_component_interaction( assert interaction.guild_locale == "en-US" assert interaction.guild_locale is locales.Locale.EN_US assert interaction.app_permissions == 5431234 + assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.context == 0 + # ResolvedData assert interaction.resolved == entity_factory_impl._deserialize_resolved_option_data( interaction_resolved_data_payload, guild_id=290926798626357999 ) + assert isinstance(interaction, component_interactions.ComponentInteraction) assert len(interaction.entitlements) == 1 assert interaction.entitlements[0].id == 696969696969696 + assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.context == 0 def test_deserialize_component_interaction_with_undefined_fields( self, entity_factory_impl, user_payload, message_payload diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index ccaf5b048b..fd3a24228f 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -5533,6 +5533,8 @@ async def test__create_application_command_with_optionals(self, rest_client: res "default_member_permissions": 8, "dm_permission": False, "nsfw": True, + "integration_types": [0], + "contexts": [0, 1] }, ) @@ -5586,6 +5588,8 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl): default_member_permissions=permissions.Permissions.ADMINISTRATOR, dm_enabled=False, nsfw=True, + integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], + contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] ) assert result is rest_client._entity_factory.deserialize_slash_command.return_value @@ -5604,6 +5608,8 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl): default_member_permissions=permissions.Permissions.ADMINISTRATOR, dm_enabled=False, nsfw=True, + integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], + contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] ) async def test_create_context_menu_command(self, rest_client: rest.RESTClientImpl): @@ -5620,6 +5626,9 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp dm_enabled=False, nsfw=True, name_localizations={locales.Locale.TR: "hhh"}, + integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], + contexts=[applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM] ) assert result is rest_client._entity_factory.deserialize_context_menu_command.return_value @@ -5635,6 +5644,8 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp dm_enabled=False, nsfw=True, name_localizations={"tr": "hhh"}, + integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], + contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] ) async def test_set_application_commands_with_guild(self, rest_client): @@ -5700,6 +5711,7 @@ async def test_edit_application_command_with_optionals(self, rest_client): options=[mock_option], default_member_permissions=permissions.Permissions.BAN_MEMBERS, dm_enabled=True, + contexts=[applications.ApplicationInstallationContextType.GUILD] ) assert result is rest_client._entity_factory.deserialize_command.return_value @@ -5714,6 +5726,7 @@ async def test_edit_application_command_with_optionals(self, rest_client): "options": [rest_client._entity_factory.serialize_command_option.return_value], "default_member_permissions": 4, "dm_permission": True, + "contexts": [0] }, ) rest_client._entity_factory.serialize_command_option.assert_called_once_with(mock_option) diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py index e4f257f937..77f8f166e9 100644 --- a/tests/hikari/impl/test_special_endpoints.py +++ b/tests/hikari/impl/test_special_endpoints.py @@ -25,6 +25,7 @@ import mock import pytest +from hikari import applications from hikari import channels from hikari import commands from hikari import components @@ -1038,14 +1039,14 @@ def test_name_localizations_property(self, stub_command): assert builder.name_localizations == {"aaa": "bbb", "ccc": "DDd"} def test_set_integration_types(self, stub_command): - builder = stub_command("oksksksk").set_integration_types([commands.ApplicationIntegrationType.GUILD_INSTALL]) + builder = stub_command("oksksksk").set_integration_types([applications.ApplicationIntegrationType.GUILD_INSTALL]) - assert builder.integration_types == [commands.ApplicationIntegrationType.GUILD_INSTALL] + assert builder.integration_types == [applications.ApplicationIntegrationType.GUILD_INSTALL] - def test_set_contexts(self, stub_contexts): - builder = stub_command("oksksksk").set_contexts([commands.ApplicationInstallationContext.BOT_DM]) + def test_set_contexts(self, stub_command): + builder = stub_command("oksksksk").set_contexts([applications.ApplicationInstallationContextType.BOT_DM]) - assert builder.integration_types == [commands.ApplicationInstallationContext.BOT_DM] + assert builder.contexts == [applications.ApplicationInstallationContextType.BOT_DM] class TestSlashCommandBuilder: diff --git a/tests/hikari/interactions/test_base_interactions.py b/tests/hikari/interactions/test_base_interactions.py index ae2213550f..01e1d6d3d0 100644 --- a/tests/hikari/interactions/test_base_interactions.py +++ b/tests/hikari/interactions/test_base_interactions.py @@ -23,6 +23,7 @@ import mock import pytest +from hikari import applications from hikari import traits from hikari import undefined from hikari.interactions import base_interactions @@ -43,6 +44,8 @@ def mock_partial_interaction(self, mock_app): type=base_interactions.InteractionType.APPLICATION_COMMAND, token="399393939doodsodso", version=3122312, + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) def test_webhook_id_property(self, mock_partial_interaction): @@ -59,6 +62,8 @@ def mock_message_response_mixin(self, mock_app): type=base_interactions.InteractionType.APPLICATION_COMMAND, token="399393939doodsodso", version=3122312, + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) @pytest.mark.asyncio @@ -208,6 +213,8 @@ def mock_modal_response_mixin(self, mock_app): type=base_interactions.InteractionType.APPLICATION_COMMAND, token="399393939doodsodso", version=3122312, + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) @pytest.mark.asyncio diff --git a/tests/hikari/interactions/test_command_interactions.py b/tests/hikari/interactions/test_command_interactions.py index 874ca33491..d780e1ac7a 100644 --- a/tests/hikari/interactions/test_command_interactions.py +++ b/tests/hikari/interactions/test_command_interactions.py @@ -22,6 +22,7 @@ import mock import pytest +from hikari import applications from hikari import channels from hikari import monetization from hikari import snowflakes @@ -73,6 +74,8 @@ def mock_command_interaction(self, mock_app): subscription_id=None, ) ], + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) def test_build_response(self, mock_command_interaction, mock_app): @@ -151,6 +154,8 @@ def mock_autocomplete_interaction(self, mock_app): subscription_id=None, ) ], + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) @pytest.fixture diff --git a/tests/hikari/interactions/test_component_interactions.py b/tests/hikari/interactions/test_component_interactions.py index 345734c795..df3e910169 100644 --- a/tests/hikari/interactions/test_component_interactions.py +++ b/tests/hikari/interactions/test_component_interactions.py @@ -22,6 +22,7 @@ import mock import pytest +from hikari import applications from hikari import channels from hikari import monetization from hikari import snowflakes @@ -71,6 +72,8 @@ def mock_component_interaction(self, mock_app): subscription_id=None, ) ], + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) def test_build_response(self, mock_component_interaction, mock_app): diff --git a/tests/hikari/interactions/test_modal_interactions.py b/tests/hikari/interactions/test_modal_interactions.py index 506df033df..8f5684a55b 100644 --- a/tests/hikari/interactions/test_modal_interactions.py +++ b/tests/hikari/interactions/test_modal_interactions.py @@ -22,6 +22,7 @@ import mock import pytest +from hikari import applications from hikari import channels from hikari import components from hikari import monetization @@ -77,6 +78,8 @@ def mock_modal_interaction(self, mock_app): subscription_id=None, ) ], + authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, + context=applications.ApplicationInstallationContextType.GUILD ) def test_build_response(self, mock_modal_interaction, mock_app): diff --git a/tests/hikari/test_commands.py b/tests/hikari/test_commands.py index 88070132fe..190ada08a8 100644 --- a/tests/hikari/test_commands.py +++ b/tests/hikari/test_commands.py @@ -41,7 +41,7 @@ def mock_command(self, mock_app): return hikari_test_helpers.mock_class_namespace(commands.PartialCommand)( app=mock_app, id=snowflakes.Snowflake(34123123), - type=applications.CommandType.SLASH, + type=commands.CommandType.SLASH, application_id=snowflakes.Snowflake(65234123), name="Name", default_member_permissions=None, @@ -57,7 +57,7 @@ def mock_command(self, mock_app): contexts=[ applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM, - applications.ApplicationInstallationContext.PRIVATE_CHANNEL + applications.ApplicationInstallationContextType.PRIVATE_CHANNEL ] ) From 87e776dd762b122778876c405870d8793280201e Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 16:40:39 +0800 Subject: [PATCH 07/12] add installation properties --- hikari/impl/special_endpoints.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index 032609548c..4b2696e240 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -71,6 +71,7 @@ from hikari.internal import mentions from hikari.internal import routes from hikari.internal import time +from hikari.internal import deprecation if typing.TYPE_CHECKING: import concurrent.futures @@ -1312,6 +1313,14 @@ def is_nsfw(self) -> undefined.UndefinedOr[bool]: def name(self) -> str: return self._name + @property + def integration_types(self) -> undefined.UndefinedOr[typing.Sequence[applications.ApplicationIntegrationType]]: + return self._integration_types + + @property + def contexts(self) -> undefined.UndefinedOr[typing.Sequence[applications.ApplicationInstallationContextType]]: + return self._contexts + def set_name(self, name: str, /) -> Self: self._name = name return self From 705a833631b73c7fc02de507f978818393a259cc Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 16:41:10 +0800 Subject: [PATCH 08/12] oauth2 install parameters --- hikari/applications.py | 3 +- hikari/impl/entity_factory.py | 74 +++++++++++++++++++++++++---------- hikari/impl/rest.py | 1 + 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/hikari/applications.py b/hikari/applications.py index 705d64a419..b7460be76d 100644 --- a/hikari/applications.py +++ b/hikari/applications.py @@ -655,7 +655,8 @@ class Application(guilds.PartialApplication): install_parameters: typing.Optional[ApplicationInstallParameters] = attrs.field(eq=False, hash=False, repr=False) """Settings for the application's default in-app authorization link, if enabled.""" - # integration_types_config: typing.Optional[typing.Mapping[]] + integration_types_config: typing.Optional[typing.Mapping[ApplicationIntegrationType, ApplicationInstallParameters]] = attrs.field(eq=False, hash=False, repr=False) + """Default scopes and permissions for each supported installation context.""" approximate_guild_count: int = attrs.field(eq=False, hash=False, repr=False) """The approximate number of guilds this application is part of.""" diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 4456bd1e12..10ecde2aeb 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -603,6 +603,12 @@ def deserialize_own_application_role_connection( metadata=payload.get("metadata") or {}, ) + def _deserialize_install_parameters(self, payload: data_binding.JSONObject) -> application_models.ApplicationInstallParameters: + return application_models.ApplicationInstallParameters( + scopes=[application_models.OAuth2Scope(scope) for scope in payload["scopes"]], + permissions=permission_models.Permissions(payload["permissions"]), + ) + def deserialize_application(self, payload: data_binding.JSONObject) -> application_models.Application: team: typing.Optional[application_models.Team] = None if (team_payload := payload.get("team")) is not None: @@ -627,10 +633,14 @@ def deserialize_application(self, payload: data_binding.JSONObject) -> applicati install_parameters: typing.Optional[application_models.ApplicationInstallParameters] = None if (install_payload := payload.get("install_params")) is not None: - install_parameters = application_models.ApplicationInstallParameters( - scopes=[application_models.OAuth2Scope(scope) for scope in install_payload["scopes"]], - permissions=permission_models.Permissions(install_payload["permissions"]), - ) + install_parameters = self._deserialize_install_parameters(install_payload) + + integration_types_config: typing.Optional[typing.Mapping[application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters]] = {} + if (integration_types_config_payload := payload.get("integration_types_config")) is not None: + integration_types_config = { + application_models.ApplicationIntegrationType(k): self._deserialize_install_parameters(v) + for k, v in integration_types_config_payload.items() + } return application_models.Application( app=self._app, @@ -652,6 +662,7 @@ def deserialize_application(self, payload: data_binding.JSONObject) -> applicati custom_install_url=payload.get("custom_install_url"), tags=payload.get("tags") or [], install_parameters=install_parameters, + integration_types_config=integration_types_config, approximate_guild_count=payload["approximate_guild_count"], ) @@ -2279,19 +2290,19 @@ def deserialize_slash_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) - integration_types: typing.Sequence[commands.ApplicationIntegrationType] + integration_types: typing.Sequence[application_models.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [commands.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] + integration_types = [application_models.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] else: - integration_types = [commands.ApplicationIntegrationType.GUILD_INSTALL] + integration_types = [application_models.ApplicationIntegrationType.GUILD_INSTALL] - contexts: typing.Sequence[commands.ApplicationInstallationContext] + contexts: typing.Sequence[application_models.ApplicationInstallationContextType] if raw_contexts := payload.get("contexts"): - contexts = [commands.ApplicationInstallationContext(context) for context in raw_contexts] + contexts = [application_models.ApplicationInstallationContextType(context) for context in raw_contexts] else: contexts = [ - commands.ApplicationInstallationContext.GUILD, - commands.ApplicationInstallationContext.BOT_DM + application_models.ApplicationInstallationContextType.GUILD, + application_models.ApplicationInstallationContextType.BOT_DM ] return commands.SlashCommand( @@ -2339,16 +2350,16 @@ def deserialize_context_menu_command( integration_types: typing.Sequence[commands.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [commands.ApplicationIntegrationType(integration_type) for integration_type in + integration_types = [application_models.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] else: - integration_types = [commands.ApplicationIntegrationType.GUILD_INSTALL] + integration_types = [application_models.ApplicationIntegrationType.GUILD_INSTALL] - contexts: typing.Sequence[commands.ApplicationInstallationContext] + contexts: typing.Sequence[application_models.ApplicationInstallationContextType] if raw_contexts := payload.get("contexts"): - contexts = [commands.ApplicationInstallationContext(context) for context in raw_contexts] + contexts = [application_models.ApplicationInstallationContextType(context) for context in raw_contexts] else: - contexts = [commands.ApplicationInstallationContext.GUILD] + contexts = [application_models.ApplicationInstallationContextType.GUILD] return commands.ContextMenuCommand( app=self._app, @@ -2403,6 +2414,14 @@ def serialize_command_permission(self, permission: commands.CommandPermission) - return {"id": str(permission.id), "type": permission.type, "permission": permission.has_access} def deserialize_partial_interaction(self, payload: data_binding.JSONObject) -> base_interactions.PartialInteraction: + authorizing_integration_owners: typing.Mapping[application_models.ApplicationIntegrationType, snowflakes.Snowflake] = {} + if (authorizing_integration_owners_payload := payload.get("authorizing_integration_owners")) is not None: + authorizing_integration_owners = { + application_models.ApplicationIntegrationType(k): snowflakes.Snowflake(v) + for k, v in authorizing_integration_owners_payload.items() + } + + context = payload.get("context") return base_interactions.PartialInteraction( app=self._app, id=snowflakes.Snowflake(payload["id"]), @@ -2410,6 +2429,8 @@ def deserialize_partial_interaction(self, payload: data_binding.JSONObject) -> b token=payload["token"], version=payload["version"], application_id=snowflakes.Snowflake(payload["application_id"]), + authorizing_integration_owners=authorizing_integration_owners, + context=application_models.ApplicationInstallationContextType(context) if context else None ) def _deserialize_interaction_command_option( @@ -2587,7 +2608,7 @@ def deserialize_command_interaction( if raw_target_id := data_payload.get("target_id"): target_id = snowflakes.Snowflake(raw_target_id) - app_perms = payload["app_permissions"] + app_permissions = payload.get("app_permissions") entitlements = [self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())] return command_interactions.CommandInteraction( @@ -2609,7 +2630,7 @@ def deserialize_command_interaction( options=options, resolved=resolved, target_id=target_id, - app_permissions=permission_models.Permissions(app_perms) if app_perms else None, + app_permissions=permission_models.Permissions(app_permissions) if app_permissions else None, registered_guild_id=snowflakes.Snowflake(data_payload["guild_id"]) if "guild_id" in data_payload else None, entitlements=entitlements, authorizing_integration_owners=payload["authorizing_integration_owners"], @@ -2684,6 +2705,17 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod message = self.deserialize_message(message_payload) app_perms = payload["app_permissions"] + + authorizing_integration_owners: typing.Mapping[application_models.ApplicationIntegrationType, snowflakes.Snowflake] = None + if (authorizing_integration_owners_payload := payload.get("authorizing_integration_owners")) is not None: + authorizing_integration_owners = { + application_models.ApplicationIntegrationType(k): snowflakes.Snowflake(v) + for k, v in authorizing_integration_owners_payload.items() + } + else: + authorizing_integration_owners = {} + + context = payload.get("context") return modal_interactions.ModalInteraction( app=self._app, application_id=snowflakes.Snowflake(payload["application_id"]), @@ -2702,8 +2734,8 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod components=self._deserialize_components(data_payload["components"], self._modal_component_type_mapping), message=message, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], - authorizing_integration_owners=payload["authorizing_integration_owners"], - context=payload.get("context") + authorizing_integration_owners=authorizing_integration_owners, + context=application_models.ApplicationInstallationContextType(context) if context else None ) def deserialize_interaction(self, payload: data_binding.JSONObject) -> base_interactions.PartialInteraction: @@ -2776,7 +2808,7 @@ def deserialize_component_interaction( if resolved_payload := data_payload.get("resolved"): resolved = self._deserialize_resolved_option_data(resolved_payload, guild_id=guild_id) - app_perms = payload["app_permissions"] + app_perms = payload.get("app_permissions") return component_interactions.ComponentInteraction( app=self._app, application_id=snowflakes.Snowflake(payload["application_id"]), diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 64e7a74043..8d4f258e89 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3915,6 +3915,7 @@ async def edit_application_command( # Discord has some funky behaviour around what 0 means. They consider it to be the same as ADMINISTRATOR, # but we consider it to be the same as None for developer sanity reasons body.put("default_member_permissions", None if default_member_permissions == 0 else default_member_permissions) + body.put("dm_permission", dm_enabled) body.put("integration_types", integration_types) body.put("contexts", contexts) From 2045a975ae6756481aac3dec94e1f7afcd917cb4 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 16:45:06 +0800 Subject: [PATCH 09/12] oauth2_install_params key --- hikari/impl/entity_factory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 10ecde2aeb..27ffc488cd 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -604,6 +604,7 @@ def deserialize_own_application_role_connection( ) def _deserialize_install_parameters(self, payload: data_binding.JSONObject) -> application_models.ApplicationInstallParameters: + return application_models.ApplicationInstallParameters( scopes=[application_models.OAuth2Scope(scope) for scope in payload["scopes"]], permissions=permission_models.Permissions(payload["permissions"]), @@ -638,7 +639,7 @@ def deserialize_application(self, payload: data_binding.JSONObject) -> applicati integration_types_config: typing.Optional[typing.Mapping[application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters]] = {} if (integration_types_config_payload := payload.get("integration_types_config")) is not None: integration_types_config = { - application_models.ApplicationIntegrationType(k): self._deserialize_install_parameters(v) + application_models.ApplicationIntegrationType(k): self._deserialize_install_parameters(v["oauth2_install_params"]) for k, v in integration_types_config_payload.items() } From 973f6276c2423b6d903f4ef17ebaa37acb2885ff Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 17:43:44 +0800 Subject: [PATCH 10/12] nox & tests --- hikari/api/rest.py | 36 ++++++++-- hikari/api/special_endpoints.py | 8 +-- hikari/applications.py | 10 ++- hikari/commands.py | 12 ++-- hikari/impl/entity_factory.py | 65 +++++++++++++------ hikari/impl/rest.py | 36 +++++++--- hikari/impl/special_endpoints.py | 13 ++-- hikari/interactions/base_interactions.py | 6 +- hikari/interactions/command_interactions.py | 2 +- hikari/interactions/component_interactions.py | 2 +- hikari/interactions/modal_interactions.py | 2 +- tests/hikari/impl/test_entity_factory.py | 35 ++++++---- tests/hikari/impl/test_interaction_server.py | 45 +++++++++++-- tests/hikari/impl/test_rest.py | 32 ++++++--- tests/hikari/impl/test_special_endpoints.py | 49 +++++++++++++- .../interactions/test_base_interactions.py | 6 +- .../interactions/test_command_interactions.py | 4 +- .../test_component_interactions.py | 2 +- .../interactions/test_modal_interactions.py | 2 +- tests/hikari/test_commands.py | 8 +-- 20 files changed, 283 insertions(+), 92 deletions(-) diff --git a/hikari/api/rest.py b/hikari/api/rest.py index e8a83b7fc9..007582b13a 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -6684,8 +6684,12 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.SlashCommand: r"""Create an application slash command. @@ -6721,6 +6725,10 @@ async def create_slash_command( This can only be applied to non-guild commands. nsfw Whether this command should be age-restricted. + integration_types + The integration types for this command. + contexts + The contexts for this command. Returns ------- @@ -6760,8 +6768,12 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.ContextMenuCommand: r"""Create an application context menu command. @@ -6792,6 +6804,10 @@ async def create_context_menu_command( This can only be applied to non-guild commands. nsfw Whether this command should be age-restricted. + integration_types + The integration types for this command. + contexts + The contexts for this command. Returns ------- @@ -6880,8 +6896,12 @@ async def edit_application_command( undefined.UndefinedType, int, permissions_.Permissions ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.PartialCommand: """Edit a registered application command. @@ -6913,6 +6933,10 @@ async def edit_application_command( Whether this command is enabled in DMs with the bot. This can only be applied to non-guild commands. + integration_types + The integration types for this command. + contexts + The contexts for this command. Returns ------- diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py index e943164585..2112267e47 100644 --- a/hikari/api/special_endpoints.py +++ b/hikari/api/special_endpoints.py @@ -1129,7 +1129,7 @@ def set_name_localizations( def set_integration_types( self, integration_types: typing.Sequence[applications.ApplicationIntegrationType], / ) -> Self: - """Set the command integration types + """Set the command integration types. Parameters ---------- @@ -1143,10 +1143,8 @@ def set_integration_types( """ @abc.abstractmethod - def set_contexts( - self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], / - ) -> Self: - """Set the command contexts + def set_contexts(self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], /) -> Self: + """Set the command contexts. Parameters ---------- diff --git a/hikari/applications.py b/hikari/applications.py index b7460be76d..9a3a2a4e24 100644 --- a/hikari/applications.py +++ b/hikari/applications.py @@ -46,7 +46,7 @@ "ApplicationRoleConnectionMetadataRecord", "get_token_id", "ApplicationIntegrationType", - "ApplicationInstallationContextType" + "ApplicationInstallationContextType", ) import base64 @@ -75,6 +75,8 @@ @typing.final class ApplicationIntegrationType(int, enums.Enum): + """The known integration types.""" + GUILD_INSTALL = 0 """A guild install command integration type""" @@ -84,6 +86,8 @@ class ApplicationIntegrationType(int, enums.Enum): @typing.final class ApplicationInstallationContextType(int, enums.Enum): + """The known installation context types.""" + GUILD = 0 """Interaction can be used within server""" @@ -655,7 +659,9 @@ class Application(guilds.PartialApplication): install_parameters: typing.Optional[ApplicationInstallParameters] = attrs.field(eq=False, hash=False, repr=False) """Settings for the application's default in-app authorization link, if enabled.""" - integration_types_config: typing.Optional[typing.Mapping[ApplicationIntegrationType, ApplicationInstallParameters]] = attrs.field(eq=False, hash=False, repr=False) + integration_types_config: typing.Optional[ + typing.Mapping[ApplicationIntegrationType, ApplicationInstallParameters] + ] = attrs.field(eq=False, hash=False, repr=False) """Default scopes and permissions for each supported installation context.""" approximate_guild_count: int = attrs.field(eq=False, hash=False, repr=False) diff --git a/hikari/commands.py b/hikari/commands.py index 2a19990efb..4dea5cdf29 100644 --- a/hikari/commands.py +++ b/hikari/commands.py @@ -33,7 +33,7 @@ "CommandPermissionType", "CommandType", "GuildCommandPermissions", - "OptionType" + "OptionType", ) import typing @@ -48,10 +48,10 @@ from hikari.internal import enums if typing.TYPE_CHECKING: + from hikari import applications from hikari import channels from hikari import guilds from hikari import locales - from hikari import applications class CommandType(int, enums.Enum): @@ -266,10 +266,14 @@ class PartialCommand(snowflakes.Unique): ) """A mapping of name localizations for this command.""" - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = attrs.field(eq=False, hash=False, repr=True) + integration_types: typing.Sequence[applications.ApplicationIntegrationType] = attrs.field( + eq=False, hash=False, repr=True + ) """A sequence of command integration types.""" - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = attrs.field(eq=False, hash=False, repr=True) + contexts: typing.Sequence[applications.ApplicationInstallationContextType] = attrs.field( + eq=False, hash=False, repr=True + ) """A sequence of command contexts.""" async def fetch_self(self) -> PartialCommand: diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 27ffc488cd..e26a00c0ea 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -603,7 +603,9 @@ def deserialize_own_application_role_connection( metadata=payload.get("metadata") or {}, ) - def _deserialize_install_parameters(self, payload: data_binding.JSONObject) -> application_models.ApplicationInstallParameters: + def _deserialize_install_parameters( + self, payload: data_binding.JSONObject + ) -> application_models.ApplicationInstallParameters: return application_models.ApplicationInstallParameters( scopes=[application_models.OAuth2Scope(scope) for scope in payload["scopes"]], @@ -636,10 +638,16 @@ def deserialize_application(self, payload: data_binding.JSONObject) -> applicati if (install_payload := payload.get("install_params")) is not None: install_parameters = self._deserialize_install_parameters(install_payload) - integration_types_config: typing.Optional[typing.Mapping[application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters]] = {} + integration_types_config: typing.Optional[ + typing.Mapping[ + application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters + ] + ] = {} if (integration_types_config_payload := payload.get("integration_types_config")) is not None: integration_types_config = { - application_models.ApplicationIntegrationType(k): self._deserialize_install_parameters(v["oauth2_install_params"]) + application_models.ApplicationIntegrationType(k): self._deserialize_install_parameters( + v["oauth2_install_params"] + ) for k, v in integration_types_config_payload.items() } @@ -2293,7 +2301,10 @@ def deserialize_slash_command( integration_types: typing.Sequence[application_models.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [application_models.ApplicationIntegrationType(integration_type) for integration_type in raw_integration_types] + integration_types = [ + application_models.ApplicationIntegrationType(integration_type) + for integration_type in raw_integration_types + ] else: integration_types = [application_models.ApplicationIntegrationType.GUILD_INSTALL] @@ -2303,7 +2314,7 @@ def deserialize_slash_command( else: contexts = [ application_models.ApplicationInstallationContextType.GUILD, - application_models.ApplicationInstallationContextType.BOT_DM + application_models.ApplicationInstallationContextType.BOT_DM, ] return commands.SlashCommand( @@ -2322,7 +2333,7 @@ def deserialize_slash_command( name_localizations=name_localizations, description_localizations=description_localizations, integration_types=integration_types, - contexts=contexts + contexts=contexts, ) def deserialize_context_menu_command( @@ -2349,10 +2360,12 @@ def deserialize_context_menu_command( else: default_member_permissions = permission_models.Permissions(default_member_permissions or 0) - integration_types: typing.Sequence[commands.ApplicationIntegrationType] + integration_types: typing.Sequence[application_models.ApplicationIntegrationType] if raw_integration_types := payload.get("integration_types"): - integration_types = [application_models.ApplicationIntegrationType(integration_type) for integration_type in - raw_integration_types] + integration_types = [ + application_models.ApplicationIntegrationType(integration_type) + for integration_type in raw_integration_types + ] else: integration_types = [application_models.ApplicationIntegrationType.GUILD_INSTALL] @@ -2375,7 +2388,7 @@ def deserialize_context_menu_command( version=snowflakes.Snowflake(payload["version"]), name_localizations=name_localizations, integration_types=integration_types, - contexts=contexts + contexts=contexts, ) def deserialize_command( @@ -2415,7 +2428,9 @@ def serialize_command_permission(self, permission: commands.CommandPermission) - return {"id": str(permission.id), "type": permission.type, "permission": permission.has_access} def deserialize_partial_interaction(self, payload: data_binding.JSONObject) -> base_interactions.PartialInteraction: - authorizing_integration_owners: typing.Mapping[application_models.ApplicationIntegrationType, snowflakes.Snowflake] = {} + authorizing_integration_owners: typing.Mapping[ + application_models.ApplicationIntegrationType, snowflakes.Snowflake + ] = {} if (authorizing_integration_owners_payload := payload.get("authorizing_integration_owners")) is not None: authorizing_integration_owners = { application_models.ApplicationIntegrationType(k): snowflakes.Snowflake(v) @@ -2431,7 +2446,7 @@ def deserialize_partial_interaction(self, payload: data_binding.JSONObject) -> b version=payload["version"], application_id=snowflakes.Snowflake(payload["application_id"]), authorizing_integration_owners=authorizing_integration_owners, - context=application_models.ApplicationInstallationContextType(context) if context else None + context=application_models.ApplicationInstallationContextType(context) if context is not None else None, ) def _deserialize_interaction_command_option( @@ -2635,7 +2650,7 @@ def deserialize_command_interaction( registered_guild_id=snowflakes.Snowflake(data_payload["guild_id"]) if "guild_id" in data_payload else None, entitlements=entitlements, authorizing_integration_owners=payload["authorizing_integration_owners"], - context=payload.get("context") + context=payload.get("context"), ) def deserialize_autocomplete_interaction( @@ -2680,7 +2695,7 @@ def deserialize_autocomplete_interaction( registered_guild_id=snowflakes.Snowflake(data_payload["guild_id"]) if "guild_id" in data_payload else None, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], authorizing_integration_owners=payload["authorizing_integration_owners"], - context=payload.get("context") + context=payload.get("context"), ) def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> modal_interactions.ModalInteraction: @@ -2705,16 +2720,16 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod if message_payload := payload.get("message"): message = self.deserialize_message(message_payload) - app_perms = payload["app_permissions"] + app_perms = payload.get("app_permissions") - authorizing_integration_owners: typing.Mapping[application_models.ApplicationIntegrationType, snowflakes.Snowflake] = None + authorizing_integration_owners: typing.Mapping[ + application_models.ApplicationIntegrationType, snowflakes.Snowflake + ] = {} if (authorizing_integration_owners_payload := payload.get("authorizing_integration_owners")) is not None: authorizing_integration_owners = { application_models.ApplicationIntegrationType(k): snowflakes.Snowflake(v) for k, v in authorizing_integration_owners_payload.items() } - else: - authorizing_integration_owners = {} context = payload.get("context") return modal_interactions.ModalInteraction( @@ -2736,7 +2751,7 @@ def deserialize_modal_interaction(self, payload: data_binding.JSONObject) -> mod message=message, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], authorizing_integration_owners=authorizing_integration_owners, - context=application_models.ApplicationInstallationContextType(context) if context else None + context=application_models.ApplicationInstallationContextType(context) if context is not None else None, ) def deserialize_interaction(self, payload: data_binding.JSONObject) -> base_interactions.PartialInteraction: @@ -2809,7 +2824,17 @@ def deserialize_component_interaction( if resolved_payload := data_payload.get("resolved"): resolved = self._deserialize_resolved_option_data(resolved_payload, guild_id=guild_id) + authorizing_integration_owners: typing.Mapping[ + application_models.ApplicationIntegrationType, snowflakes.Snowflake + ] = {} + if (authorizing_integration_owners_payload := payload.get("authorizing_integration_owners")) is not None: + authorizing_integration_owners = { + application_models.ApplicationIntegrationType(k): snowflakes.Snowflake(v) + for k, v in authorizing_integration_owners_payload.items() + } + app_perms = payload.get("app_permissions") + context = payload.get("context") return component_interactions.ComponentInteraction( app=self._app, application_id=snowflakes.Snowflake(payload["application_id"]), @@ -2830,6 +2855,8 @@ def deserialize_component_interaction( guild_locale=locales.Locale(payload["guild_locale"]) if "guild_locale" in payload else None, app_permissions=permission_models.Permissions(app_perms) if app_perms else None, entitlements=[self.deserialize_entitlement(entitlement) for entitlement in payload.get("entitlements", ())], + authorizing_integration_owners=authorizing_integration_owners, + context=application_models.ApplicationInstallationContextType(context) if context is not None else None, ) ################## diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index 8d4f258e89..afb687c6c4 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -3760,8 +3760,12 @@ async def _create_application_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> data_binding.JSONObject: if guild is undefined.UNDEFINED: route = routes.POST_APPLICATION_COMMAND.compile(application=application) @@ -3809,8 +3813,12 @@ async def create_slash_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.SlashCommand: response = await self._create_application_command( application=application, @@ -3825,7 +3833,7 @@ async def create_slash_command( dm_enabled=dm_enabled, nsfw=nsfw, integration_types=integration_types, - contexts=contexts + contexts=contexts, ) return self._entity_factory.deserialize_slash_command( response, guild_id=snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None @@ -3846,8 +3854,12 @@ async def create_context_menu_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.ContextMenuCommand: response = await self._create_application_command( application=application, @@ -3859,7 +3871,7 @@ async def create_context_menu_command( dm_enabled=dm_enabled, nsfw=nsfw, integration_types=integration_types, - contexts=contexts + contexts=contexts, ) return self._entity_factory.deserialize_context_menu_command( response, guild_id=snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None @@ -3896,8 +3908,12 @@ async def edit_application_command( ] = undefined.UNDEFINED, dm_enabled: undefined.UndefinedOr[bool] = undefined.UNDEFINED, nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED, - integration_types: typing.Sequence[applications.ApplicationIntegrationType] = undefined.UNDEFINED, - contexts: typing.Sequence[applications.ApplicationInstallationContextType] = undefined.UNDEFINED + integration_types: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationIntegrationType] + ] = undefined.UNDEFINED, + contexts: undefined.UndefinedOr[ + typing.Sequence[applications.ApplicationInstallationContextType] + ] = undefined.UNDEFINED, ) -> commands.PartialCommand: if guild is undefined.UNDEFINED: route = routes.PATCH_APPLICATION_COMMAND.compile(application=application, command=command) diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index 4b2696e240..a584e61af7 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -68,10 +68,10 @@ from hikari.interactions import base_interactions from hikari.internal import attrs_extensions from hikari.internal import data_binding +from hikari.internal import deprecation from hikari.internal import mentions from hikari.internal import routes from hikari.internal import time -from hikari.internal import deprecation if typing.TYPE_CHECKING: import concurrent.futures @@ -1303,6 +1303,9 @@ def default_member_permissions(self) -> typing.Union[undefined.UndefinedType, pe @property def is_dm_enabled(self) -> undefined.UndefinedOr[bool]: + deprecation.warn_deprecated( + "dm_permission", additional_info="use contexts instead", removal_version="2.0.0.dev129" + ) return self._is_dm_enabled @property @@ -1359,9 +1362,7 @@ def set_integration_types( self._integration_types = integration_types return self - def set_contexts( - self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], / - ) -> Self: + def set_contexts(self, contexts: typing.Sequence[applications.ApplicationInstallationContextType], /) -> Self: self._contexts = contexts return self @@ -1455,6 +1456,8 @@ async def create( default_member_permissions=self._default_member_permissions, dm_enabled=self._is_dm_enabled, nsfw=self._is_nsfw, + integration_types=self._integration_types, + contexts=self._contexts, ) @@ -1488,6 +1491,8 @@ async def create( default_member_permissions=self._default_member_permissions, dm_enabled=self._is_dm_enabled, nsfw=self.is_nsfw, + integration_types=self._integration_types, + contexts=self._contexts, ) diff --git a/hikari/interactions/base_interactions.py b/hikari/interactions/base_interactions.py index d5f22955f0..ab69a218cc 100644 --- a/hikari/interactions/base_interactions.py +++ b/hikari/interactions/base_interactions.py @@ -51,13 +51,13 @@ from hikari.internal import enums if typing.TYPE_CHECKING: + from hikari import applications from hikari import embeds as embeds_ from hikari import files from hikari import messages from hikari import permissions as permissions_ from hikari import traits from hikari import users - from hikari import applications from hikari.api import special_endpoints _CommandResponseTypesT = typing.TypeVar("_CommandResponseTypesT", bound=int) @@ -219,7 +219,9 @@ class PartialInteraction(snowflakes.Unique, webhooks.ExecutableWebhook): version: int = attrs.field(eq=False, repr=True) """Version of the interaction system this interaction is under.""" - authorizing_integration_owners: typing.Mapping[applications.ApplicationIntegrationType, snowflakes.Snowflake] = attrs.field(eq=False, repr=False) + authorizing_integration_owners: typing.Mapping[applications.ApplicationIntegrationType, snowflakes.Snowflake] = ( + attrs.field(eq=False, repr=False) + ) """Mapping installation contexts authorized for interaction to related user or guild IDs.""" context: typing.Optional[applications.ApplicationInstallationContextType] = attrs.field(eq=False, repr=False) diff --git a/hikari/interactions/command_interactions.py b/hikari/interactions/command_interactions.py index ccc92db6d3..32cda9e8ef 100644 --- a/hikari/interactions/command_interactions.py +++ b/hikari/interactions/command_interactions.py @@ -315,7 +315,7 @@ class CommandInteraction( ): """Represents a command interaction on Discord.""" - app_permissions: permissions_.Permissions = attrs.field(eq=False, hash=False, repr=False) + app_permissions: typing.Optional[permissions_.Permissions] = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" options: typing.Optional[typing.Sequence[CommandInteractionOption]] = attrs.field(eq=False, hash=False, repr=True) diff --git a/hikari/interactions/component_interactions.py b/hikari/interactions/component_interactions.py index 5312d0863d..c0bc3f7993 100644 --- a/hikari/interactions/component_interactions.py +++ b/hikari/interactions/component_interactions.py @@ -147,7 +147,7 @@ class ComponentInteraction( locale: typing.Union[str, locales.Locale] = attrs.field(eq=False, hash=False, repr=True) """The selected language of the user who triggered this component interaction.""" - app_permissions: permissions.Permissions = attrs.field(eq=False, hash=False, repr=False) + app_permissions: typing.Optional[permissions.Permissions] = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" entitlements: typing.Sequence[monetization.Entitlement] = attrs.field(eq=False, hash=False, repr=True) diff --git a/hikari/interactions/modal_interactions.py b/hikari/interactions/modal_interactions.py index 18adbe7a67..c11fe624fa 100644 --- a/hikari/interactions/modal_interactions.py +++ b/hikari/interactions/modal_interactions.py @@ -119,7 +119,7 @@ class ModalInteraction( locale: str = attrs.field(eq=False, hash=False, repr=True) """The selected language of the user who triggered this modal interaction.""" - app_permissions: permissions.Permissions = attrs.field(eq=False, hash=False, repr=False) + app_permissions: typing.Optional[permissions.Permissions] = attrs.field(eq=False, hash=False, repr=False) """Permissions the bot has in this interaction's channel if it's in a guild.""" components: typing.Sequence[components_.ModalActionRowComponent] = attrs.field(eq=False, hash=False, repr=True) diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index b4fc661461..1f768afb17 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -4057,7 +4057,10 @@ def test_deserialize_slash_command(self, entity_factory_impl, mock_app, slash_co assert command.is_nsfw is True assert command.version == 123321123 assert command.integration_types == [application_models.ApplicationIntegrationType.GUILD_INSTALL] - assert command.contexts == [application_models.ApplicationInstallationContextType.GUILD, application_models.ApplicationInstallationContextType.BOT_DM] + assert command.contexts == [ + application_models.ApplicationInstallationContextType.GUILD, + application_models.ApplicationInstallationContextType.BOT_DM, + ] # CommandOption assert len(command.options) == 1 @@ -4211,7 +4214,7 @@ def partial_interaction_payload(self): "version": 1, "application_id": "1", "authorizing_integration_owners": {0: 12345}, - "context": 0 + "context": 0, } def test_deserialize_partial_interaction(self, mock_app, entity_factory_impl, partial_interaction_payload): @@ -4223,7 +4226,9 @@ def test_deserialize_partial_interaction(self, mock_app, entity_factory_impl, pa assert interaction.type == 1 assert interaction.version == 1 assert interaction.application_id == 1 - assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.authorizing_integration_owners == { + application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345 + } assert interaction.context == application_models.ApplicationInstallationContextType.GUILD assert type(interaction) is base_interactions.PartialInteraction @@ -4420,7 +4425,7 @@ def command_interaction_payload(self, interaction_member_payload, interaction_re } ], "authorizing_integration_owners": {0: 12345}, - "context": 0 + "context": 0, } def test_deserialize_command_interaction( @@ -4457,7 +4462,9 @@ def test_deserialize_command_interaction( assert len(interaction.entitlements) == 1 assert interaction.entitlements[0].id == 696969696969696 assert interaction.registered_guild_id == 12345678 - assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.authorizing_integration_owners == { + application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345 + } assert interaction.context == 0 # CommandInteractionOption @@ -4523,7 +4530,7 @@ def context_menu_command_interaction_payload(self, interaction_member_payload, u } ], "authorizing_integration_owners": {0: 12345}, - "context": 0 + "context": 0, } def test_deserialize_command_interaction_with_context_menu_field( @@ -4601,7 +4608,7 @@ def autocomplete_interaction_payload(self, member_payload, user_payload, interac } ], "authorizing_integration_owners": {0: 12345}, - "context": 0 + "context": 0, } def test_deserialize_autocomplete_interaction( @@ -4629,7 +4636,9 @@ def test_deserialize_autocomplete_interaction( assert interaction.locale is locales.Locale.ES_ES assert interaction.guild_locale is locales.Locale.EN_US assert interaction.registered_guild_id == 12345678 - assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.authorizing_integration_owners == { + application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345 + } assert interaction.context == 0 # AutocompleteInteractionOption @@ -4873,7 +4882,7 @@ def component_interaction_payload( } ], "authorizing_integration_owners": {0: 12345}, - "context": 0 + "context": 0, } def test_deserialize_component_interaction( @@ -4908,7 +4917,9 @@ def test_deserialize_component_interaction( assert interaction.guild_locale == "en-US" assert interaction.guild_locale is locales.Locale.EN_US assert interaction.app_permissions == 5431234 - assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.authorizing_integration_owners == { + application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345 + } assert interaction.context == 0 # ResolvedData @@ -4920,7 +4931,9 @@ def test_deserialize_component_interaction( assert len(interaction.entitlements) == 1 assert interaction.entitlements[0].id == 696969696969696 - assert interaction.authorizing_integration_owners == {application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345} + assert interaction.authorizing_integration_owners == { + application_models.ApplicationIntegrationType.GUILD_INSTALL: 12345 + } assert interaction.context == 0 def test_deserialize_component_interaction_with_undefined_fields( diff --git a/tests/hikari/impl/test_interaction_server.py b/tests/hikari/impl/test_interaction_server.py index 97fdc32e28..6c4cdcfe63 100644 --- a/tests/hikari/impl/test_interaction_server.py +++ b/tests/hikari/impl/test_interaction_server.py @@ -665,7 +665,14 @@ async def test_on_interaction( mock_file_1 = mock.Mock() mock_file_2 = mock.Mock() mock_entity_factory.deserialize_interaction.return_value = base_interactions.PartialInteraction( - app=None, id=123, application_id=541324, type=2, token="ok", version=1 + app=None, + id=123, + application_id=541324, + type=2, + token="ok", + version=1, + authorizing_integration_owners={0: 1}, + context=0, ) mock_builder = mock.Mock(build=mock.Mock(return_value=({"ok": "No boomer"}, [mock_file_1, mock_file_2]))) mock_listener = mock.AsyncMock(return_value=mock_builder) @@ -706,7 +713,14 @@ async def mock_generator_listener(event): mock_file_1 = mock.Mock() mock_file_2 = mock.Mock() mock_entity_factory.deserialize_interaction.return_value = base_interactions.PartialInteraction( - app=None, id=123, application_id=541324, type=2, token="ok", version=1 + app=None, + id=123, + application_id=541324, + type=2, + token="ok", + version=1, + authorizing_integration_owners={0: 1}, + context=0, ) mock_builder = mock.Mock(build=mock.Mock(return_value=({"ok": "No boomer"}, [mock_file_1, mock_file_2]))) g_called = False @@ -871,7 +885,14 @@ async def test_on_interaction_on_dispatch_error( mock_interaction_server._public_key = mock.Mock() mock_exception = TypeError("OK") mock_entity_factory.deserialize_interaction.return_value = base_interactions.PartialInteraction( - app=None, id=123, application_id=541324, type=2, token="ok", version=1 + app=None, + id=123, + application_id=541324, + type=2, + token="ok", + version=1, + authorizing_integration_owners={0: 1}, + context=0, ) mock_interaction_server.set_listener( base_interactions.PartialInteraction, mock.Mock(side_effect=mock_exception) @@ -900,7 +921,14 @@ async def test_on_interaction_when_response_builder_error( mock_interaction_server._public_key = mock.Mock() mock_exception = TypeError("OK") mock_entity_factory.deserialize_interaction.return_value = base_interactions.PartialInteraction( - app=None, id=123, application_id=541324, type=2, token="ok", version=1 + app=None, + id=123, + application_id=541324, + type=2, + token="ok", + version=1, + authorizing_integration_owners={0: 1}, + context=0, ) mock_builder = mock.Mock(build=mock.Mock(side_effect=mock_exception)) mock_interaction_server.set_listener( @@ -931,7 +959,14 @@ async def test_on_interaction_when_json_encode_fails( mock_exception = TypeError("OK") mock_interaction_server._dumps = mock.Mock(side_effect=mock_exception) mock_entity_factory.deserialize_interaction.return_value = base_interactions.PartialInteraction( - app=None, id=123, application_id=541324, type=2, token="ok", version=1 + app=None, + id=123, + application_id=541324, + type=2, + token="ok", + version=1, + authorizing_integration_owners={0: 1}, + context=0, ) mock_builder = mock.Mock(build=mock.Mock(return_value=({"ok": "No"}, []))) mock_interaction_server.set_listener( diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index fd3a24228f..4856c833f8 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -5518,7 +5518,10 @@ async def test__create_application_command_with_optionals(self, rest_client: res dm_enabled=False, nsfw=True, integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], - contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] + contexts=[ + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + ], ) assert result is rest_client._request.return_value @@ -5534,7 +5537,7 @@ async def test__create_application_command_with_optionals(self, rest_client: res "dm_permission": False, "nsfw": True, "integration_types": [0], - "contexts": [0, 1] + "contexts": [0, 1], }, ) @@ -5589,7 +5592,10 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl): dm_enabled=False, nsfw=True, integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], - contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] + contexts=[ + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + ], ) assert result is rest_client._entity_factory.deserialize_slash_command.return_value @@ -5609,7 +5615,10 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl): dm_enabled=False, nsfw=True, integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], - contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] + contexts=[ + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + ], ) async def test_create_context_menu_command(self, rest_client: rest.RESTClientImpl): @@ -5627,8 +5636,10 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp nsfw=True, name_localizations={locales.Locale.TR: "hhh"}, integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], - contexts=[applications.ApplicationInstallationContextType.GUILD, - applications.ApplicationInstallationContextType.BOT_DM] + contexts=[ + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + ], ) assert result is rest_client._entity_factory.deserialize_context_menu_command.return_value @@ -5645,7 +5656,10 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp nsfw=True, name_localizations={"tr": "hhh"}, integration_types=[applications.ApplicationIntegrationType.GUILD_INSTALL], - contexts=[applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM] + contexts=[ + applications.ApplicationInstallationContextType.GUILD, + applications.ApplicationInstallationContextType.BOT_DM, + ], ) async def test_set_application_commands_with_guild(self, rest_client): @@ -5711,7 +5725,7 @@ async def test_edit_application_command_with_optionals(self, rest_client): options=[mock_option], default_member_permissions=permissions.Permissions.BAN_MEMBERS, dm_enabled=True, - contexts=[applications.ApplicationInstallationContextType.GUILD] + contexts=[applications.ApplicationInstallationContextType.GUILD], ) assert result is rest_client._entity_factory.deserialize_command.return_value @@ -5726,7 +5740,7 @@ async def test_edit_application_command_with_optionals(self, rest_client): "options": [rest_client._entity_factory.serialize_command_option.return_value], "default_member_permissions": 4, "dm_permission": True, - "contexts": [0] + "contexts": [0], }, ) rest_client._entity_factory.serialize_command_option.assert_called_once_with(mock_option) diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py index 77f8f166e9..5589d72d7e 100644 --- a/tests/hikari/impl/test_special_endpoints.py +++ b/tests/hikari/impl/test_special_endpoints.py @@ -1039,7 +1039,9 @@ def test_name_localizations_property(self, stub_command): assert builder.name_localizations == {"aaa": "bbb", "ccc": "DDd"} def test_set_integration_types(self, stub_command): - builder = stub_command("oksksksk").set_integration_types([applications.ApplicationIntegrationType.GUILD_INSTALL]) + builder = stub_command("oksksksk").set_integration_types( + [applications.ApplicationIntegrationType.GUILD_INSTALL] + ) assert builder.integration_types == [applications.ApplicationIntegrationType.GUILD_INSTALL] @@ -1123,6 +1125,13 @@ async def test_create(self): .set_default_member_permissions(permissions.Permissions.BAN_MEMBERS) .set_is_dm_enabled(True) .set_is_nsfw(True) + .set_integration_types( + [ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ] + ) + .set_contexts([applications.ApplicationInstallationContextType.GUILD]) ) mock_rest = mock.AsyncMock() @@ -1140,6 +1149,11 @@ async def test_create(self): default_member_permissions=permissions.Permissions.BAN_MEMBERS, dm_enabled=True, nsfw=True, + integration_types=[ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ], + contexts=[applications.ApplicationInstallationContextType.GUILD], ) @pytest.mark.asyncio @@ -1149,6 +1163,13 @@ async def test_create_with_guild(self): .set_default_member_permissions(permissions.Permissions.BAN_MEMBERS) .set_is_dm_enabled(True) .set_is_nsfw(True) + .set_integration_types( + [ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ] + ) + .set_contexts([applications.ApplicationInstallationContextType.GUILD]) ) mock_rest = mock.AsyncMock() @@ -1169,6 +1190,11 @@ async def test_create_with_guild(self): default_member_permissions=permissions.Permissions.BAN_MEMBERS, dm_enabled=True, nsfw=True, + integration_types=[ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ], + contexts=[applications.ApplicationInstallationContextType.GUILD], ) @@ -1210,6 +1236,13 @@ async def test_create(self): .set_name_localizations({"meow": "nyan"}) .set_is_dm_enabled(True) .set_is_nsfw(True) + .set_integration_types( + [ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ] + ) + .set_contexts([applications.ApplicationInstallationContextType.GUILD]) ) mock_rest = mock.AsyncMock() @@ -1225,6 +1258,8 @@ async def test_create(self): name_localizations={"meow": "nyan"}, dm_enabled=True, nsfw=True, + integration_types=[0, 1], + contexts=[0], ) @pytest.mark.asyncio @@ -1235,6 +1270,13 @@ async def test_create_with_guild(self): .set_name_localizations({"en-ghibli": "meow"}) .set_is_dm_enabled(True) .set_is_nsfw(True) + .set_integration_types( + [ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ] + ) + .set_contexts([applications.ApplicationInstallationContextType.GUILD]) ) mock_rest = mock.AsyncMock() @@ -1250,6 +1292,11 @@ async def test_create_with_guild(self): name_localizations={"en-ghibli": "meow"}, dm_enabled=True, nsfw=True, + integration_types=[ + applications.ApplicationIntegrationType.GUILD_INSTALL, + applications.ApplicationIntegrationType.USER_INSTALL, + ], + contexts=[applications.ApplicationInstallationContextType.GUILD], ) diff --git a/tests/hikari/interactions/test_base_interactions.py b/tests/hikari/interactions/test_base_interactions.py index 01e1d6d3d0..7d801f1caf 100644 --- a/tests/hikari/interactions/test_base_interactions.py +++ b/tests/hikari/interactions/test_base_interactions.py @@ -45,7 +45,7 @@ def mock_partial_interaction(self, mock_app): token="399393939doodsodso", version=3122312, authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) def test_webhook_id_property(self, mock_partial_interaction): @@ -63,7 +63,7 @@ def mock_message_response_mixin(self, mock_app): token="399393939doodsodso", version=3122312, authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) @pytest.mark.asyncio @@ -214,7 +214,7 @@ def mock_modal_response_mixin(self, mock_app): token="399393939doodsodso", version=3122312, authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) @pytest.mark.asyncio diff --git a/tests/hikari/interactions/test_command_interactions.py b/tests/hikari/interactions/test_command_interactions.py index d780e1ac7a..67f536dcb2 100644 --- a/tests/hikari/interactions/test_command_interactions.py +++ b/tests/hikari/interactions/test_command_interactions.py @@ -75,7 +75,7 @@ def mock_command_interaction(self, mock_app): ) ], authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) def test_build_response(self, mock_command_interaction, mock_app): @@ -155,7 +155,7 @@ def mock_autocomplete_interaction(self, mock_app): ) ], authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) @pytest.fixture diff --git a/tests/hikari/interactions/test_component_interactions.py b/tests/hikari/interactions/test_component_interactions.py index df3e910169..a0f121ea95 100644 --- a/tests/hikari/interactions/test_component_interactions.py +++ b/tests/hikari/interactions/test_component_interactions.py @@ -73,7 +73,7 @@ def mock_component_interaction(self, mock_app): ) ], authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) def test_build_response(self, mock_component_interaction, mock_app): diff --git a/tests/hikari/interactions/test_modal_interactions.py b/tests/hikari/interactions/test_modal_interactions.py index 8f5684a55b..3175839fc7 100644 --- a/tests/hikari/interactions/test_modal_interactions.py +++ b/tests/hikari/interactions/test_modal_interactions.py @@ -79,7 +79,7 @@ def mock_modal_interaction(self, mock_app): ) ], authorizing_integration_owners={applications.ApplicationIntegrationType.GUILD_INSTALL: 12345}, - context=applications.ApplicationInstallationContextType.GUILD + context=applications.ApplicationInstallationContextType.GUILD, ) def test_build_response(self, mock_modal_interaction, mock_app): diff --git a/tests/hikari/test_commands.py b/tests/hikari/test_commands.py index 190ada08a8..ee1be72c68 100644 --- a/tests/hikari/test_commands.py +++ b/tests/hikari/test_commands.py @@ -22,8 +22,8 @@ import mock import pytest -from hikari import commands from hikari import applications +from hikari import commands from hikari import snowflakes from hikari import traits from hikari import undefined @@ -52,13 +52,13 @@ def mock_command(self, mock_app): name_localizations={}, integration_types=[ applications.ApplicationIntegrationType.GUILD_INSTALL, - applications.ApplicationIntegrationType.USER_INSTALL + applications.ApplicationIntegrationType.USER_INSTALL, ], contexts=[ applications.ApplicationInstallationContextType.GUILD, applications.ApplicationInstallationContextType.BOT_DM, - applications.ApplicationInstallationContextType.PRIVATE_CHANNEL - ] + applications.ApplicationInstallationContextType.PRIVATE_CHANNEL, + ], ) @pytest.mark.asyncio From c61fb5c0621683069d1f877c225165aa97167d9e Mon Sep 17 00:00:00 2001 From: MagM1go Date: Mon, 8 Jul 2024 19:45:06 +0800 Subject: [PATCH 11/12] deprecation warning dm_permission -> is_dm_enabled --- hikari/impl/special_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index a584e61af7..247e995371 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -1304,7 +1304,7 @@ def default_member_permissions(self) -> typing.Union[undefined.UndefinedType, pe @property def is_dm_enabled(self) -> undefined.UndefinedOr[bool]: deprecation.warn_deprecated( - "dm_permission", additional_info="use contexts instead", removal_version="2.0.0.dev129" + "is_dm_enabled", additional_info="use contexts instead", removal_version="2.0.0.dev129" ) return self._is_dm_enabled From fce534b0e6ae94408b960431bf1b39e0c9d162d5 Mon Sep 17 00:00:00 2001 From: MagM1go Date: Fri, 2 Aug 2024 12:01:47 +0800 Subject: [PATCH 12/12] remove typing.Optional in integration_types_config --- hikari/applications.py | 16 ++++++++-------- hikari/impl/entity_factory.py | 6 ++---- hikari/interactions/base_interactions.py | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/hikari/applications.py b/hikari/applications.py index 9a3a2a4e24..a2f40afaff 100644 --- a/hikari/applications.py +++ b/hikari/applications.py @@ -78,10 +78,10 @@ class ApplicationIntegrationType(int, enums.Enum): """The known integration types.""" GUILD_INSTALL = 0 - """A guild install command integration type""" + """A guild install command integration type.""" USER_INSTALL = 1 - """A user install command integration type""" + """A user install command integration type.""" @typing.final @@ -89,13 +89,13 @@ class ApplicationInstallationContextType(int, enums.Enum): """The known installation context types.""" GUILD = 0 - """Interaction can be used within server""" + """Interaction can be used within server.""" BOT_DM = 1 - """Interaction can be used within DM's""" + """Interaction can be used within DM's.""" PRIVATE_CHANNEL = 2 - """Interaction can be used within group DM's and DM's""" + """Interaction can be used within group DM's and DM's.""" @typing.final @@ -659,9 +659,9 @@ class Application(guilds.PartialApplication): install_parameters: typing.Optional[ApplicationInstallParameters] = attrs.field(eq=False, hash=False, repr=False) """Settings for the application's default in-app authorization link, if enabled.""" - integration_types_config: typing.Optional[ - typing.Mapping[ApplicationIntegrationType, ApplicationInstallParameters] - ] = attrs.field(eq=False, hash=False, repr=False) + integration_types_config: typing.Mapping[ApplicationIntegrationType, ApplicationInstallParameters] = attrs.field( + eq=False, hash=False, repr=False + ) """Default scopes and permissions for each supported installation context.""" approximate_guild_count: int = attrs.field(eq=False, hash=False, repr=False) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index 553f2c34ec..25479d148e 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -638,10 +638,8 @@ def deserialize_application(self, payload: data_binding.JSONObject) -> applicati if (install_payload := payload.get("install_params")) is not None: install_parameters = self._deserialize_install_parameters(install_payload) - integration_types_config: typing.Optional[ - typing.Mapping[ - application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters - ] + integration_types_config: typing.Mapping[ + application_models.ApplicationIntegrationType, application_models.ApplicationInstallParameters ] = {} if (integration_types_config_payload := payload.get("integration_types_config")) is not None: integration_types_config = { diff --git a/hikari/interactions/base_interactions.py b/hikari/interactions/base_interactions.py index ab69a218cc..584f66eb9b 100644 --- a/hikari/interactions/base_interactions.py +++ b/hikari/interactions/base_interactions.py @@ -225,7 +225,7 @@ class PartialInteraction(snowflakes.Unique, webhooks.ExecutableWebhook): """Mapping installation contexts authorized for interaction to related user or guild IDs.""" context: typing.Optional[applications.ApplicationInstallationContextType] = attrs.field(eq=False, repr=False) - """A context where interaction was triggerred""" + """A context on where interaction was triggered.""" @property def webhook_id(self) -> snowflakes.Snowflake: