Skip to content

Commit 7ca9d17

Browse files
Middledotizxxr
andauthored
Add support for guild scheduled events (#211)
Co-authored-by: Izhar Ahmad <[email protected]>
1 parent 4ddf450 commit 7ca9d17

21 files changed

+1399
-138
lines changed

discord/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
from .commands import *
6464
from .cog import Cog
6565
from .welcome_screen import *
66+
from .scheduled_events import ScheduledEvent, ScheduledEventLocation
6667

6768

6869
class VersionInfo(NamedTuple):

discord/abc.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
runtime_checkable,
4444
)
4545

46+
from .scheduled_events import ScheduledEvent
4647
from .iterators import HistoryIterator
4748
from .context_managers import Typing
4849
from .enums import ChannelType
@@ -1035,6 +1036,7 @@ async def create_invite(
10351036
max_uses: int = 0,
10361037
temporary: bool = False,
10371038
unique: bool = True,
1039+
target_event: Optional[ScheduledEvent] = None,
10381040
target_type: Optional[InviteTarget] = None,
10391041
target_user: Optional[User] = None,
10401042
target_application_id: Optional[int] = None,
@@ -1073,11 +1075,20 @@ async def create_invite(
10731075
10741076
.. versionadded:: 2.0
10751077
1076-
target_application_id:: Optional[:class:`int`]
1078+
target_application_id: Optional[:class:`int`]
10771079
The id of the embedded application for the invite, required if `target_type` is `TargetType.embedded_application`.
10781080
10791081
.. versionadded:: 2.0
10801082
1083+
target_event: Optional[:class:`ScheduledEvent`]
1084+
The scheduled event object to link to the event.
1085+
Shortcut to :meth:`Invite.set_scheduled_event`
1086+
1087+
See :meth:`Invite.set_scheduled_event` for more
1088+
info on event invite linking.
1089+
1090+
.. versionadded:: 2.0
1091+
10811092
Raises
10821093
-------
10831094
~discord.HTTPException
@@ -1103,7 +1114,10 @@ async def create_invite(
11031114
target_user_id=target_user.id if target_user else None,
11041115
target_application_id=target_application_id,
11051116
)
1106-
return Invite.from_incomplete(data=data, state=self._state)
1117+
invite = Invite.from_incomplete(data=data, state=self._state)
1118+
if target_event:
1119+
invite.set_scheduled_event(target_event)
1120+
return invite
11071121

11081122
async def invites(self) -> List[Invite]:
11091123
"""|coro|

discord/audit_logs.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
from .stage_instance import StageInstance
6262
from .sticker import GuildSticker
6363
from .threads import Thread
64+
from .scheduled_events import ScheduledEvent
65+
from .state import ConnectionState
6466

6567

6668
def _transform_permissions(entry: AuditLogEntry, data: str) -> Permissions:
@@ -147,7 +149,7 @@ def _transform(entry: AuditLogEntry, data: int) -> T:
147149

148150
return _transform
149151

150-
def _transform_type(entry: AuditLogEntry, data: Union[int]) -> Union[enums.ChannelType, enums.StickerType]:
152+
def _transform_type(entry: AuditLogEntry, data: int) -> Union[enums.ChannelType, enums.StickerType]:
151153
if entry.action.name.startswith('sticker_'):
152154
return enums.try_enum(enums.StickerType, data)
153155
else:
@@ -210,14 +212,16 @@ class AuditLogChanges:
210212
'privacy_level': (None, _enum_transformer(enums.StagePrivacyLevel)),
211213
'format_type': (None, _enum_transformer(enums.StickerFormatType)),
212214
'type': (None, _transform_type),
215+
'status': (None, _enum_transformer(enums.ScheduledEventStatus)),
216+
'entity_type': ('location_type', _enum_transformer(enums.ScheduledEventLocationType)),
213217
}
214218
# fmt: on
215219

216-
def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]):
220+
def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload], *, state: ConnectionState):
217221
self.before = AuditLogDiff()
218222
self.after = AuditLogDiff()
219223

220-
for elem in data:
224+
for elem in sorted(data, key=lambda i: i['key']):
221225
attr = elem['key']
222226

223227
# special cases for role add/remove
@@ -246,6 +250,14 @@ def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]):
246250
if transformer:
247251
before = transformer(entry, before)
248252

253+
if attr == 'location':
254+
if hasattr(self.before, 'location_type'):
255+
from .scheduled_events import ScheduledEventLocation
256+
if self.before.location_type is enums.ScheduledEventLocationType.external:
257+
before = ScheduledEventLocation(state=state, location=before)
258+
elif hasattr(self.before, 'channel'):
259+
before = ScheduledEventLocation(state=state, location=self.before.channel)
260+
249261
setattr(self.before, attr, before)
250262

251263
try:
@@ -256,6 +268,14 @@ def __init__(self, entry: AuditLogEntry, data: List[AuditLogChangePayload]):
256268
if transformer:
257269
after = transformer(entry, after)
258270

271+
if attr == 'location':
272+
if hasattr(self.after, 'location_type'):
273+
from .scheduled_events import ScheduledEventLocation
274+
if self.after.location_type is enums.ScheduledEventLocationType.external:
275+
after = ScheduledEventLocation(state=state, location=after)
276+
elif hasattr(self.after, 'channel'):
277+
after = ScheduledEventLocation(state=state, location=self.after.channel)
278+
259279
setattr(self.after, attr, after)
260280

261281
# add an alias
@@ -463,7 +483,7 @@ def category(self) -> enums.AuditLogActionCategory:
463483
@utils.cached_property
464484
def changes(self) -> AuditLogChanges:
465485
""":class:`AuditLogChanges`: The list of changes this entry has."""
466-
obj = AuditLogChanges(self, self._changes)
486+
obj = AuditLogChanges(self, self._changes, state=self._state)
467487
del self._changes
468488
return obj
469489

@@ -523,3 +543,6 @@ def _convert_target_sticker(self, target_id: int) -> Union[GuildSticker, Object]
523543

524544
def _convert_target_thread(self, target_id: int) -> Union[Thread, Object]:
525545
return self.guild.get_thread(target_id) or Object(id=target_id)
546+
547+
def _convert_target_scheduled_event(self, target_id: int) -> Union[ScheduledEvent, None]:
548+
return self.guild.get_scheduled_event(target_id) or Object(id=target_id)

discord/client.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ async def fetch_stage_instance(self, channel_id: int, /) -> StageInstance:
13371337

13381338
# Invite management
13391339

1340-
async def fetch_invite(self, url: Union[Invite, str], *, with_counts: bool = True, with_expiration: bool = True) -> Invite:
1340+
async def fetch_invite(self, url: Union[Invite, str], *, with_counts: bool = True, with_expiration: bool = True, event_id: Snowflake = None) -> Invite:
13411341
"""|coro|
13421342
13431343
Gets an :class:`.Invite` from a discord.gg URL or ID.
@@ -1361,6 +1361,13 @@ async def fetch_invite(self, url: Union[Invite, str], *, with_counts: bool = Tru
13611361
:attr:`.Invite.expires_at` field.
13621362
13631363
.. versionadded:: 2.0
1364+
event_id: :class:`int`
1365+
The ID of the scheduled event to be associated with the event.
1366+
1367+
See :meth:`Invite.set_scheduled_event` for more
1368+
info on event invite linking.
1369+
1370+
..versionadded:: 2.0
13641371
13651372
Raises
13661373
-------
@@ -1376,7 +1383,7 @@ async def fetch_invite(self, url: Union[Invite, str], *, with_counts: bool = Tru
13761383
"""
13771384

13781385
invite_id = utils.resolve_invite(url)
1379-
data = await self.http.get_invite(invite_id, with_counts=with_counts, with_expiration=with_expiration)
1386+
data = await self.http.get_invite(invite_id, with_counts=with_counts, with_expiration=with_expiration, guild_scheduled_event_id=event_id)
13801387
return Invite.from_incomplete(state=self._connection, data=data)
13811388

13821389
async def delete_invite(self, invite: Union[Invite, str]) -> None:

discord/commands/errors.py

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,64 @@
1-
"""
2-
The MIT License (MIT)
3-
4-
Copyright (c) 2021-present Pycord Development
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a
7-
copy of this software and associated documentation files (the "Software"),
8-
to deal in the Software without restriction, including without limitation
9-
the rights to use, copy, modify, merge, publish, distribute, sublicense,
10-
and/or sell copies of the Software, and to permit persons to whom the
11-
Software is furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in
14-
all copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21-
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22-
DEALINGS IN THE SOFTWARE.
23-
"""
24-
25-
from ..errors import DiscordException
26-
27-
__all__ = (
28-
"ApplicationCommandError",
29-
"CheckFailure",
30-
"ApplicationCommandInvokeError",
31-
)
32-
33-
class ApplicationCommandError(DiscordException):
34-
r"""The base exception type for all application command related errors.
35-
36-
This inherits from :exc:`discord.DiscordException`.
37-
38-
This exception and exceptions inherited from it are handled
39-
in a special way as they are caught and passed into a special event
40-
from :class:`.Bot`\, :func:`.on_command_error`.
41-
"""
42-
pass
43-
44-
class CheckFailure(ApplicationCommandError):
45-
"""Exception raised when the predicates in :attr:`.Command.checks` have failed.
46-
47-
This inherits from :exc:`ApplicationCommandError`
48-
"""
49-
pass
50-
51-
class ApplicationCommandInvokeError(ApplicationCommandError):
52-
"""Exception raised when the command being invoked raised an exception.
53-
54-
This inherits from :exc:`ApplicationCommandError`
55-
56-
Attributes
57-
-----------
58-
original: :exc:`Exception`
59-
The original exception that was raised. You can also get this via
60-
the ``__cause__`` attribute.
61-
"""
62-
def __init__(self, e: Exception) -> None:
63-
self.original: Exception = e
64-
super().__init__(f'Application Command raised an exception: {e.__class__.__name__}: {e}')
1+
"""
2+
The MIT License (MIT)
3+
4+
Copyright (c) 2021-present Pycord Development
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a
7+
copy of this software and associated documentation files (the "Software"),
8+
to deal in the Software without restriction, including without limitation
9+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
and/or sell copies of the Software, and to permit persons to whom the
11+
Software is furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+
DEALINGS IN THE SOFTWARE.
23+
"""
24+
25+
from ..errors import DiscordException
26+
27+
__all__ = (
28+
"ApplicationCommandError",
29+
"CheckFailure",
30+
"ApplicationCommandInvokeError",
31+
)
32+
33+
class ApplicationCommandError(DiscordException):
34+
r"""The base exception type for all application command related errors.
35+
36+
This inherits from :exc:`discord.DiscordException`.
37+
38+
This exception and exceptions inherited from it are handled
39+
in a special way as they are caught and passed into a special event
40+
from :class:`.Bot`\, :func:`.on_command_error`.
41+
"""
42+
pass
43+
44+
class CheckFailure(ApplicationCommandError):
45+
"""Exception raised when the predicates in :attr:`.Command.checks` have failed.
46+
47+
This inherits from :exc:`ApplicationCommandError`
48+
"""
49+
pass
50+
51+
class ApplicationCommandInvokeError(ApplicationCommandError):
52+
"""Exception raised when the command being invoked raised an exception.
53+
54+
This inherits from :exc:`ApplicationCommandError`
55+
56+
Attributes
57+
-----------
58+
original: :exc:`Exception`
59+
The original exception that was raised. You can also get this via
60+
the ``__cause__`` attribute.
61+
"""
62+
def __init__(self, e: Exception) -> None:
63+
self.original: Exception = e
64+
super().__init__(f'Application Command raised an exception: {e.__class__.__name__}: {e}')

0 commit comments

Comments
 (0)