diff --git a/discord/appinfo.py b/discord/appinfo.py index 074892d051af..9f147652f747 100644 --- a/discord/appinfo.py +++ b/discord/appinfo.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing import List, TYPE_CHECKING, Optional +from typing import List, TYPE_CHECKING, Literal, Optional from . import utils from .asset import Asset @@ -41,6 +41,7 @@ PartialAppInfo as PartialAppInfoPayload, Team as TeamPayload, InstallParams as InstallParamsPayload, + AppIntegrationTypeConfig as AppIntegrationTypeConfigPayload, ) from .user import User from .state import ConnectionState @@ -175,6 +176,7 @@ class AppInfo: 'interactions_endpoint_url', 'redirect_uris', 'approximate_guild_count', + '_integration_types_config', ) def __init__(self, state: ConnectionState, data: AppInfoPayload): @@ -212,6 +214,9 @@ def __init__(self, state: ConnectionState, data: AppInfoPayload): self.interactions_endpoint_url: Optional[str] = data.get('interactions_endpoint_url') self.redirect_uris: List[str] = data.get('redirect_uris', []) self.approximate_guild_count: int = data.get('approximate_guild_count', 0) + self._integration_types_config: Dict[Literal["0", "1"], AppIntegrationTypeConfigPayload] = data.get( + 'integration_types_config', {} + ) def __repr__(self) -> str: return ( @@ -254,6 +259,36 @@ def flags(self) -> ApplicationFlags: """ return ApplicationFlags._from_value(self._flags) + @property + def default_guild_install_params(self) -> Optional[AppInstallParams]: + """Optional[:class:`AppInstallParams`]: The default settings for the + application's installation context in a guild. + + .. versionadded:: 2.4 + """ + if not self._integration_types_config: + return None + + try: + return AppInstallParams(self._integration_types_config['0']['oauth2_install_params']) + except KeyError: + return None + + @property + def default_user_install_params(self) -> Optional[AppInstallParams]: + """Optional[:class:`AppInstallParams`]: The default settings for the + application's installation context as a user. + + .. versionadded:: 2.4 + """ + if not self._integration_types_config: + return None + + try: + return AppInstallParams(self._integration_types_config['1']['oauth2_install_params']) + except KeyError: + return None + async def edit( self, *, @@ -268,6 +303,10 @@ async def edit( cover_image: Optional[bytes] = MISSING, interactions_endpoint_url: Optional[str] = MISSING, tags: Optional[List[str]] = MISSING, + default_guild_install_scopes: Optional[List[str]] = MISSING, + default_guild_install_permissions: Optional[Permissions] = MISSING, + default_user_install_scopes: Optional[List[str]] = MISSING, + default_user_install_permissions: Optional[Permissions] = MISSING, ) -> AppInfo: r"""|coro| @@ -309,6 +348,16 @@ async def edit( over the gateway. Can be ``None`` to remove the URL. tags: Optional[List[:class:`str`]] The new list of tags describing the functionality of the application. Can be ``None`` to remove the tags. + default_guild_install_scopes: Optional[List[:class:`str`]] + The new list of :ddocs:`OAuth2 scopes ` of + the default guild installation context. Can be ``None`` to remove the scopes. + default_guild_install_permissions: Optional[:class:`Permissions`] + The new permissions of the default guild installation context. Can be ``None`` to remove the permissions. + default_user_install_scopes: Optional[List[:class:`str`]] + The new list of :ddocs:`OAuth2 scopes ` of + the default user installation context. Can be ``None`` to remove the scopes. + default_user_install_permissions: Optional[:class:`Permissions`] + The new permissions of the default user installation context. Can be ``None`` to remove the permissions. reason: Optional[:class:`str`] The reason for editing the application. Shows up on the audit log. @@ -319,6 +368,7 @@ async def edit( ValueError The image format passed in to ``icon`` or ``cover_image`` is invalid. This is also raised when ``install_params_scopes`` and ``install_params_permissions`` are incompatible with each other. + or when ``default_guild_install_scopes`` and ``default_guild_install_permissions`` are incompatible with each other. Returns ------- @@ -383,6 +433,51 @@ async def edit( if tags is not MISSING: payload['tags'] = tags + + integration_types_config: Dict[str, Any] = {} + if default_guild_install_scopes is not MISSING or default_guild_install_permissions is not MISSING: + guild_install_params: Optional[Dict[str, Any]] = {} + if default_guild_install_scopes in (None, MISSING): + default_guild_install_scopes = [] + + if "bot" not in default_guild_install_scopes and default_guild_install_permissions is not MISSING: + raise ValueError("'bot' must be in default_guild_install_scopes if default_guild_install_permissions is set") + + if default_guild_install_permissions in (None, MISSING): + guild_install_params['permissions'] = 0 + else: + guild_install_params['permissions'] = default_guild_install_permissions.value + + guild_install_params['scopes'] = default_guild_install_scopes + + integration_types_config['0'] = {'oauth2_install_params': guild_install_params or None} + else: + if default_guild_install_permissions is not MISSING: + raise ValueError("default_guild_install_scopes must be set if default_guild_install_permissions is set") + + if default_user_install_scopes is not MISSING or default_user_install_permissions is not MISSING: + user_install_params: Optional[Dict[str, Any]] = {} + if default_user_install_scopes in (None, MISSING): + default_user_install_scopes = [] + + if "bot" not in default_user_install_scopes and default_user_install_permissions is not MISSING: + raise ValueError("'bot' must be in default_user_install_scopes if default_user_install_permissions is set") + + if default_user_install_permissions in (None, MISSING): + user_install_params['permissions'] = 0 + else: + user_install_params['permissions'] = default_user_install_permissions.value + + user_install_params['scopes'] = default_user_install_scopes + + integration_types_config['1'] = {'oauth2_install_params': user_install_params or None} + else: + if default_user_install_permissions is not MISSING: + raise ValueError("default_user_install_scopes must be set if default_user_install_permissions is set") + + if integration_types_config: + payload['integration_types_config'] = integration_types_config + data = await self._state.http.edit_application_info(reason=reason, payload=payload) return AppInfo(data=data, state=self._state) diff --git a/discord/http.py b/discord/http.py index f36d191e4b0e..86f3259a2044 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2508,6 +2508,7 @@ def edit_application_info(self, *, reason: Optional[str], payload: Any) -> Respo 'cover_image', 'interactions_endpoint_url ', 'tags', + 'integration_types_config', ) payload = {k: v for k, v in payload.items() if k in valid_keys} diff --git a/discord/types/appinfo.py b/discord/types/appinfo.py index e291babfa3e0..fcb3d737b34c 100644 --- a/discord/types/appinfo.py +++ b/discord/types/appinfo.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing import TypedDict, List, Optional +from typing import Literal, Dict, TypedDict, List, Optional from typing_extensions import NotRequired from .user import User @@ -37,6 +37,10 @@ class InstallParams(TypedDict): permissions: str +class AppIntegrationTypeConfig(TypedDict): + oauth2_install_params: NotRequired[InstallParams] + + class BaseAppInfo(TypedDict): id: Snowflake name: str @@ -67,6 +71,7 @@ class AppInfo(BaseAppInfo): tags: NotRequired[List[str]] install_params: NotRequired[InstallParams] custom_install_url: NotRequired[str] + integration_types_config: NotRequired[Dict[Literal["0", "1"], AppIntegrationTypeConfig]] class PartialAppInfo(BaseAppInfo, total=False):