diff --git a/discord/guild.py b/discord/guild.py index b6b1fc4ba..13bfc7922 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -52,8 +52,7 @@ from .permissions import PermissionOverwrite from .colour import Colour from .errors import InvalidArgument, ClientException from .channel import * -from .channel import _guild_channel_factory -from .channel import _threaded_guild_channel_factory +from .channel import _guild_channel_factory, _threaded_guild_channel_factory from .enums import ( AuditLogAction, VideoQualityMode, @@ -74,12 +73,13 @@ from .asset import Asset from .flags import SystemChannelFlags from .integrations import Integration, _integration_factory from .stage_instance import StageInstance -from .threads import Thread, ThreadMember +from .threads import Thread from .sticker import GuildSticker from .file import File from .settings import GuildSettings from .profile import MemberProfile from .partial_emoji import PartialEmoji +from .welcome_screen import * __all__ = ( @@ -2860,6 +2860,80 @@ class Guild(Hashable): await self._state.http.edit_widget(self.id, payload=payload) + async def welcome_screen(self) -> WelcomeScreen: + """|coro| + + Returns the guild's welcome screen. + + You must have the :attr:`~Permissions.manage_guild` permission to + use this. + + .. note:: + + The guild must have the welcome screen enabled to get this information. + + .. versionadded:: 2.0 + + Raises + ------- + NotFound + The guild does not have a welcome screen. + Forbidden + You do not have the :attr:`~Permissions.manage_guild` permission. + HTTPException + Retrieving the welcome screen failed. + + Returns + -------- + :class:`WelcomeScreen` + The welcome screen. + """ + data = await self._state.http.get_welcome_screen(self.id) + return WelcomeScreen(data=data, guild=self) + + async def edit_welcome_screen( + self, + *, + description: str = MISSING, + welcome_channels: List[WelcomeChannel] = MISSING, + enabled: bool = MISSING, + ): + """|coro| + + Edit the welcome screen. + You must have the :attr:`~Permissions.manage_guild` permission to do this. + + All parameters are optional. + + Parameters + ------------ + enabled: :class:`bool` + Whether the welcome screen will be shown. + description: :class:`str` + The welcome screen's description. + welcome_channels: Optional[List[:class:`WelcomeChannel`]] + The welcome channels (in order). + + Raises + ------- + HTTPException + Editing the welcome screen failed failed. + Forbidden + You don't have permissions to edit the welcome screen. + """ + payload = {} + + if enabled is not MISSING: + payload['enabled'] = enabled + if description is not MISSING: + payload['description'] = description + if welcome_channels is not MISSING: + channels = [channel.to_dict() for channel in welcome_channels] if welcome_channels else [] + payload['welcome_channels'] = channels + + if payload: + await self._state.http.edit_welcome_screen(self.id, payload) + async def chunk(self, *, cache: bool = True) -> None: """|coro| diff --git a/discord/http.py b/discord/http.py index 292de58ed..a142c10f1 100644 --- a/discord/http.py +++ b/discord/http.py @@ -78,6 +78,7 @@ if TYPE_CHECKING: widget, threads, sticker, + welcome_screen, ) from .types.snowflake import Snowflake, SnowflakeList @@ -1479,6 +1480,12 @@ class HTTPClient: def edit_widget(self, guild_id: Snowflake, payload) -> Response[widget.WidgetSettings]: return self.request(Route('PATCH', '/guilds/{guild_id}/widget', guild_id=guild_id), json=payload) + def get_welcome_screen(self, guild_id: Snowflake) -> Response[welcome_screen.WelcomeScreen]: + return self.request(Route('GET', '/guilds/{guild_id}/welcome-screen', guild_id=guild_id)) + + def edit_welcome_screen(self, guild_id: Snowflake, payload) -> Response[welcome_screen.WelcomeScreen]: + return self.request(Route('PATCH', '/guilds/{guild_id}/welcome-screen', guild_id=guild_id), json=payload) + # Invite management def accept_invite( diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py new file mode 100644 index 000000000..371332b80 --- /dev/null +++ b/discord/welcome_screen.py @@ -0,0 +1,187 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from __future__ import annotations + +from typing import List, Optional, TYPE_CHECKING, Union + +from .object import Object +from .partial_emoji import PartialEmoji +from .utils import _get_as_snowflake, MISSING + +if TYPE_CHECKING: + from .abc import Snowflake + from .emoji import Emoji + from .guild import Guild + from .state import ConnectionState + from .types.welcome_screen import ( + WelcomeScreen as WelcomeScreenPayload, + WelcomeScreenChannel as WelcomeScreenChannelPayload, + ) + +__all__ = ( + 'WelcomeChannel', + 'WelcomeScreen', +) + + +class WelcomeChannel: + """Represents a channel shown on a :class:`WelcomeScreen`. + + .. versionadded:: 2.0 + + Attributes + ----------- + channel: :class:`abc.Snowflake` + The channel that is being shown. + description: :class:`str` + The description of the channel. + emoji: Optional[Union[:class:`PartialEmoji`, :class:`Emoji`] + The emoji shown under the description. + """ + + def __init__( + self, *, channel: Snowflake, description: str, emoji: Optional[Union[PartialEmoji, Emoji]] = None + ) -> None: + self.channel = channel + self.description = description + self.emoji = emoji + + def __repr__(self) -> str: + return f'' + + @classmethod + def _from_dict(cls, *, data: WelcomeScreenChannelPayload, state: ConnectionState) -> WelcomeChannel: + channel_id = _get_as_snowflake(data, 'channel_id') + channel = state.get_channel(channel_id) or Object(id=channel_id) + + emoji = None + if (emoji_id := _get_as_snowflake(data, 'emoji_id')) is not None: + emoji = state.get_emoji(emoji_id) + elif (emoji_name := data.get('emoji_name')) is not None: + emoji = PartialEmoji(name=emoji_name) + + return cls(channel=channel, description=data.get('description', ''), emoji=emoji) + + def _to_dict(self) -> WelcomeScreenChannelPayload: + data = { + 'channel_id': self.channel.id, + 'description': self.description, + 'emoji_id': None, + 'emoji_name': None, + } + if (emoji := self.emoji) is not None: + data['emoji_id'] = emoji.id + data['emoji_name'] = emoji.name + + return data + + +class WelcomeScreen: + """Represents a :class:`Guild`'s welcome screen. + + .. versionadded:: 2.0 + + .. container:: operations + + .. describe:: bool(b) + + Returns whether the welcome screen is enabled. + + Attributes + ----------- + guild: Union[:class:`Guild`, :class:`PartialInviteGuild`] + The guild the welcome screen is for. + description: :class:`str` + The text shown on the welcome screen. + welcome_channels: List[:class:`WelcomeChannel`] + The channels shown on the welcome screen. + """ + + def __init__(self, *, data: WelcomeScreenPayload, guild: Guild) -> None: + self.guild = guild + self._update(data) + + def _update(self, data: WelcomeScreenPayload) -> None: + state = self.guild._state + channels = data.get('welcome_channels', []) + + self.welcome_channels = [WelcomeChannel._from_dict(data=channel, state=state) for channel in channels] + self.description = data.get('description', '') + + def __repr__(self) -> str: + return f'' + + def __bool__(self) -> bool: + return self.enabled + + @property + def enabled(self) -> bool: + """:class:`bool`: Whether the welcome screen is displayed.""" + return 'WELCOME_SCREEN_ENABLED' in self.guild.features + + async def edit( + self, + *, + description: str = MISSING, + welcome_channels: List[WelcomeChannel] = MISSING, + enabled: bool = MISSING, + ): + """|coro| + + Edit the welcome screen. + You must have the :attr:`~Permissions.manage_guild` permission to do this. + + All parameters are optional. + + Parameters + ------------ + enabled: :class:`bool` + Whether the welcome screen will be shown. + description: :class:`str` + The welcome screen's description. + welcome_channels: Optional[List[:class:`WelcomeChannel`]] + The welcome channels (in order). + + Raises + ------- + HTTPException + Editing the welcome screen failed failed. + Forbidden + You don't have permissions to edit the welcome screen. + """ + payload = {} + + if enabled is not MISSING: + payload['enabled'] = enabled + if description is not MISSING: + payload['description'] = description + if welcome_channels is not MISSING: + channels = [channel.to_dict() for channel in welcome_channels] if welcome_channels else [] + payload['welcome_channels'] = channels + + if payload: + guild = self.guild + data = await guild._state.http.edit_welcome_screen(guild.id, payload) + self._update(data)