From 8ec591e46b85bdd39663aa27ca85a70c0db7bf72 Mon Sep 17 00:00:00 2001 From: Wasi Master <63045920+wasi-master@users.noreply.github.com> Date: Mon, 30 Aug 2021 11:43:03 +0600 Subject: [PATCH 1/2] Add support for creating parties --- discord/channel.py | 58 +++++++++++++++++++++++++++++++++++++++- discord/enums.py | 9 +++++++ discord/http.py | 18 +++++++++++++ discord/types/channel.py | 7 +++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/discord/channel.py b/discord/channel.py index be3315cf..9b4eeee7 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -45,7 +45,7 @@ import datetime import discord.abc from .permissions import PermissionOverwrite, Permissions -from .enums import ChannelType, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode +from .enums import ChannelType, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode, PartyType from .mixins import Hashable from .object import Object from . import utils @@ -1037,6 +1037,38 @@ class VoiceChannel(VocalGuildChannel): # the payload will always be the proper channel payload return self.__class__(state=self._state, guild=self.guild, data=payload) # type: ignore + async def create_party(self, application_id: PartyType, max_age: int = 86400 , max_uses: int = 0): + """|coro| + Creates a party in this voice channel. + + .. versionadded:: 2.0 + + Parameters + ---------- + application_id : :class:`PartyType` + The id of the application the party belongs to. currently any of + ``PartyType.youtube``, ``PartyType.poker``, ``PartyType.betrayal``, ``PartyType.fishing``, ``PartyType.chess``. + max_age : :class:`int`, optional + Duration in seconds after which the invite expires, by default 1 day. + max_uses : :class:`int`, optional + maximum number of times this invite can be used, by default unlimited. + + Raises + ------- + Forbidden + You do not have permissions to create a party. + HTTPException + Party creation failed. + + Returns + -------- + :class:`Party` + The created party. + """ + return Party(await self._state.http.create_party(self.id, application_id.value, max_age=max_age, max_uses=max_uses)) + + + class StageChannel(VocalGuildChannel): """Represents a Discord guild stage channel. @@ -2038,6 +2070,30 @@ class PartialMessageable(discord.abc.Messageable, Hashable): return PartialMessage(channel=self, id=message_id) +class Party: + """Represents a party in a voice channel.""" + + __slots__ = ('code', 'uses', 'max_uses', 'max_age', 'temporary', 'created_at') + + def __init__(self, data): + self.code: str = data['code'] + self.uses: Optional[int] = data.get('uses') + self.max_uses: Optional[int] = data.get('max_uses') + self.max_age: Optional[int] = data.get('max_age') + self.temporary: Optional[bool] = data.get('temporary') + self.created_at: Optional[datetime.datetime] = utils.parse_time(data.get('created_at')) + # TODO: add more fields here such as guild. raw data: https://mystb.in/AdvertisersExperiencesMothers.json + + def __repr__(self): + return f'' + + def __str__(self): + return f'https://discord.gg/{self.code}' + + def __eq__(self, other): + return isinstance(other, Party) and self.code == other.code + + def _guild_channel_factory(channel_type: int): value = try_enum(ChannelType, channel_type) diff --git a/discord/enums.py b/discord/enums.py index af8ee2b0..6a26bc41 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -30,6 +30,7 @@ __all__ = ( 'Enum', 'ChannelType', 'MessageType', + 'PartyType', 'VoiceRegion', 'SpeakingState', 'VerificationLevel', @@ -214,6 +215,14 @@ class MessageType(Enum): guild_invite_reminder = 22 +class PartyType(Enum): + youtube = 755600276941176913 + poker = 755827207812677713 + betrayal = 773336526917861400 + fishing = 814288819477020702 + chess = 832012774040141894 + + class VoiceRegion(Enum): us_west = 'us-west' us_east = 'us-east' diff --git a/discord/http.py b/discord/http.py index 7a4c2adc..625a206d 100644 --- a/discord/http.py +++ b/discord/http.py @@ -880,6 +880,24 @@ class HTTPClient: ) -> Response[None]: return self.request(Route('DELETE', '/channels/{channel_id}', channel_id=channel_id), reason=reason) + def create_party( + self, + channel_id: Snowflake, + application_id: Snowflake, + max_age: int, + max_uses: int, + ) -> Response[channel.Party]: + payload = { + 'max_age': max_age, + 'max_uses': max_uses, + 'target_application_id': application_id, + 'target_type': 2, + 'temporary': False, + 'validate': None + } + return self.request(Route("POST", "/channels/{channel_id}/invites", channel_id=channel_id), json=payload) + + # Thread management def start_thread_with_message( diff --git a/discord/types/channel.py b/discord/types/channel.py index a35351e4..c2b0b524 100644 --- a/discord/types/channel.py +++ b/discord/types/channel.py @@ -155,3 +155,10 @@ class StageInstance(TypedDict): topic: str privacy_level: PrivacyLevel discoverable_disabled: bool + +class Party(TypedDict): + uses: int + max_uses: int + max_age: int + temporary: bool + created_at: str -- 2.47.2 From 30da1159e1ae38370bbd6c57a3faec4d5d026f3e Mon Sep 17 00:00:00 2001 From: Wasi Master <63045920+wasi-master@users.noreply.github.com> Date: Tue, 31 Aug 2021 11:04:09 +0600 Subject: [PATCH 2/2] Fix documentation for VoiceChannel.create_party --- discord/channel.py | 48 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/discord/channel.py b/discord/channel.py index 9b4eeee7..d741015c 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -2071,7 +2071,50 @@ class PartialMessageable(discord.abc.Messageable, Hashable): return PartialMessage(channel=self, id=message_id) class Party: - """Represents a party in a voice channel.""" + """Represents a party in a voice channel. + + .. container:: operations + + .. describe:: x == y + + Checks if two party are equal. + + .. describe:: x != y + + Checks if two party are not equal. + + .. describe:: hash(x) + + Returns the party hash. + + .. describe:: str(x) + + Returns the party URL. + + Attributes + ----------- + code: :class:`str` + The URL fragment used for the party. + uses: :class:`int` + How many times the invite has been used. + max_uses: :class:`int` + How many times the invite can be used. + A value of ``0`` indicates that it has unlimited uses. + max_age: :class:`int` + How long before the party expires in seconds. + A value of ``0`` indicates that it doesn't expire. + temporary: :class:`bool` + Indicates that the invite grants temporary membership. + If ``True``, members who joined via this invite will be kicked upon disconnect. + created_at: :class:`datetime.datetime` + An aware UTC datetime object denoting the time the invite was created. + + Note + ---- + Parties are still in BETA so there are some limitations. + Currently this BETA feature is only supported on web and updated PC app versions of Discord and is not supported on mobile. + First someone has to to click the blue link itself and not the join button, then everyone else can click the join button normally. + """ __slots__ = ('code', 'uses', 'max_uses', 'max_age', 'temporary', 'created_at') @@ -2090,6 +2133,9 @@ class Party: def __str__(self): return f'https://discord.gg/{self.code}' + def __hash__(self) -> int: + return hash(self.code) + def __eq__(self, other): return isinstance(other, Party) and self.code == other.code -- 2.47.2