1
|
|
|
# Copyright Pincer 2021-Present |
|
|
|
|
2
|
|
|
# Full MIT License can be found in `LICENSE` at the project root. |
3
|
|
|
|
4
|
|
|
from __future__ import annotations |
5
|
|
|
|
6
|
|
|
from dataclasses import dataclass, field |
7
|
|
|
from enum import IntEnum |
8
|
|
|
from typing import AsyncGenerator, overload, TYPE_CHECKING |
9
|
|
|
|
10
|
|
|
from .ban import Ban |
11
|
|
|
from .channel import Channel |
|
|
|
|
12
|
|
|
from .member import GuildMember |
13
|
|
|
from ...exceptions import UnavailableGuildError |
14
|
|
|
from ...utils.api_object import APIObject |
|
|
|
|
15
|
|
|
from ...utils.conversion import construct_client_dict |
16
|
|
|
from ...utils.types import MISSING |
17
|
|
|
|
18
|
|
|
if TYPE_CHECKING: |
19
|
|
|
from typing import Any, Dict, List, Optional, Union |
20
|
|
|
|
21
|
|
|
from .features import GuildFeature |
22
|
|
|
from .role import Role |
23
|
|
|
from .stage import StageInstance |
24
|
|
|
from .welcome_screen import WelcomeScreen |
25
|
|
|
from ..events.presence import PresenceUpdateEvent |
26
|
|
|
from ..message.emoji import Emoji |
27
|
|
|
from ..message.sticker import Sticker |
28
|
|
|
from ..user.voice_state import VoiceState |
29
|
|
|
from ...client import Client |
|
|
|
|
30
|
|
|
from ...utils.timestamp import Timestamp |
|
|
|
|
31
|
|
|
from ...utils.types import APINullable |
32
|
|
|
from ...utils.snowflake import Snowflake |
33
|
|
|
|
34
|
|
|
|
35
|
|
|
class PremiumTier(IntEnum): |
36
|
|
|
"""Represents the boost tier of a guild. |
37
|
|
|
Attributes |
38
|
|
|
---------- |
39
|
|
|
NONE: |
40
|
|
|
Guild has not unlocked any Server Boost perks. |
41
|
|
|
TIER_1: |
42
|
|
|
Guild has unlocked Server Boost level 1 perks. |
43
|
|
|
TIER_2: |
44
|
|
|
Guild has unlocked Server Boost level 2 perks. |
45
|
|
|
TIER_3: |
46
|
|
|
Guild has unlocked Server Boost level 3 perks. |
47
|
|
|
""" |
48
|
|
|
|
49
|
|
|
NONE = 0 |
50
|
|
|
TIER_1 = 1 |
51
|
|
|
TIER_2 = 2 |
52
|
|
|
TIER_3 = 3 |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
class GuildNSFWLevel(IntEnum): |
56
|
|
|
"""Represents the NSFW level of a guild. |
57
|
|
|
Attributes |
58
|
|
|
---------- |
59
|
|
|
DEFAULT: |
60
|
|
|
Default NSFW level. |
61
|
|
|
EXPLICIT: |
62
|
|
|
Explicit NSFW level. |
63
|
|
|
SAFE: |
64
|
|
|
SAFE NSFW level. |
65
|
|
|
AGE_RESTRICTED: |
66
|
|
|
Age restricted NSFW level. |
67
|
|
|
""" |
68
|
|
|
|
69
|
|
|
DEFAULT = 0 |
70
|
|
|
EXPLICIT = 1 |
71
|
|
|
SAFE = 2 |
72
|
|
|
AGE_RESTRICTED = 3 |
73
|
|
|
|
74
|
|
|
|
75
|
|
|
class ExplicitContentFilterLevel(IntEnum): |
76
|
|
|
"""Represents the filter content level of a guild. |
77
|
|
|
Attributes |
78
|
|
|
---------- |
79
|
|
|
DISABLED: |
80
|
|
|
Media content will not be scanned. |
81
|
|
|
MEMBERS_WITHOUT_ROLES: |
82
|
|
|
Media content sent by members without roles will be scanned. |
83
|
|
|
ALL_MEMBERS: |
84
|
|
|
Media content sent by all members will be scanned. |
85
|
|
|
""" |
86
|
|
|
|
87
|
|
|
DISABLED = 0 |
88
|
|
|
MEMBERS_WITHOUT_ROLES = 1 |
89
|
|
|
ALL_MEMBERS = 2 |
90
|
|
|
|
91
|
|
|
|
92
|
|
|
class MFALevel(IntEnum): |
93
|
|
|
"""Represents the multi factor authentication level of a guild. |
94
|
|
|
Attributes |
95
|
|
|
---------- |
96
|
|
|
NONE: |
97
|
|
|
Guild has no MFA/2FA requirement for moderation actions. |
98
|
|
|
ELEVATED: |
99
|
|
|
Guild has a 2FA requirement for moderation actions |
100
|
|
|
""" |
101
|
|
|
|
102
|
|
|
NONE = 0 |
103
|
|
|
ELEVATED = 1 |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
class VerificationLevel(IntEnum): |
107
|
|
|
"""Represents the verification level of a guild. |
108
|
|
|
Attributes |
109
|
|
|
---------- |
110
|
|
|
NONE: |
111
|
|
|
Unrestricted. |
112
|
|
|
LOW: |
113
|
|
|
Must have verified email on account. |
114
|
|
|
MEDIUM: |
115
|
|
|
Must be registered on Discord for longer than 5 minutes. |
116
|
|
|
HIGH: |
117
|
|
|
Must be a member of the server for longer than 10 minutes. |
118
|
|
|
VERY_HIGH: |
119
|
|
|
Must have a verified phone number. |
120
|
|
|
""" |
121
|
|
|
|
122
|
|
|
NONE = 0 |
123
|
|
|
LOW = 1 |
124
|
|
|
MEDIUM = 2 |
125
|
|
|
HIGH = 3 |
126
|
|
|
VERY_HIGH = 4 |
127
|
|
|
|
128
|
|
|
|
129
|
|
|
class DefaultMessageNotificationLevel(IntEnum): |
130
|
|
|
"""Represents the default message notification level of a guild. |
131
|
|
|
Attributes |
132
|
|
|
---------- |
133
|
|
|
ALL_MESSAGES: |
134
|
|
|
Members will receive notifications for all messages by default. |
135
|
|
|
ONLY_MENTIONS: |
136
|
|
|
Members will receive notifications only for messages that @mention them by default. |
137
|
|
|
""" |
138
|
|
|
|
139
|
|
|
# noqa: E501 |
140
|
|
|
ALL_MESSAGES = 0 |
141
|
|
|
ONLY_MENTIONS = 1 |
142
|
|
|
|
143
|
|
|
|
144
|
|
|
class SystemChannelFlags(IntEnum): |
145
|
|
|
"""Represents the system channel flags of a guild. |
146
|
|
|
Attributes |
147
|
|
|
---------- |
148
|
|
|
SUPPRESS_JOIN_NOTIFICATIONS: |
149
|
|
|
Suppress member join notifications. |
150
|
|
|
SUPPRESS_PREMIUM_SUBSCRIPTIONS: |
151
|
|
|
Suppress server boost notifications. |
152
|
|
|
SUPPRESS_GUILD_REMINDER_NOTIFICATIONS: |
153
|
|
|
Suppress server setup tips. |
154
|
|
|
SUPPRESS_JOIN_NOTIFICATION_REPLIES: |
155
|
|
|
Hide member join sticker reply buttons |
156
|
|
|
""" |
157
|
|
|
|
158
|
|
|
SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0 |
159
|
|
|
SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1 |
160
|
|
|
SUPPRESS_GUILD_REMINDER_NOTIFICATIONS = 1 << 2 |
161
|
|
|
SUPPRESS_JOIN_NOTIFICATION_REPLIES = 1 << 3 |
162
|
|
|
|
163
|
|
|
|
164
|
|
|
@dataclass |
|
|
|
|
165
|
|
|
class GuildPreview(APIObject): |
166
|
|
|
"""Represents a guild preview. |
167
|
|
|
Attributes |
168
|
|
|
---------- |
169
|
|
|
id: :class:`Snowflake` |
170
|
|
|
The guild ID. |
171
|
|
|
name: :class:`str` |
172
|
|
|
The guild name. |
173
|
|
|
icon: :class:`str` |
174
|
|
|
The guild icon hash. |
175
|
|
|
splash: :class:`str` |
176
|
|
|
The guild splash hash. |
177
|
|
|
discovery_splash: :class:`str` |
178
|
|
|
The guild discovery splash hash. |
179
|
|
|
emojis: :class:`List[Emoji]` |
180
|
|
|
The guild emojis. |
181
|
|
|
features: :class:`List[GuildFeature]` |
182
|
|
|
The guild features. |
183
|
|
|
approximate_member_count: :class:`int` |
184
|
|
|
The approximate member count. |
185
|
|
|
approximate_presence_count: :class:`int` |
186
|
|
|
The approximate number of online members in this guild |
187
|
|
|
description: :class:`str` |
188
|
|
|
The guild description. |
189
|
|
|
""" |
190
|
|
|
|
191
|
|
|
id: Snowflake |
192
|
|
|
name: str |
193
|
|
|
emojis: List[Emoji] |
194
|
|
|
features: List[GuildFeature] |
195
|
|
|
approximate_member_count: int |
196
|
|
|
approximate_presence_count: int |
197
|
|
|
|
198
|
|
|
icon: APINullable[str] = MISSING |
199
|
|
|
splash: APINullable[str] = MISSING |
200
|
|
|
discovery_splash: APINullable[str] = MISSING |
201
|
|
|
description: APINullable[str] = MISSING |
202
|
|
|
|
203
|
|
|
|
204
|
|
|
@dataclass |
|
|
|
|
205
|
|
|
class Guild(APIObject): |
206
|
|
|
"""Represents a Discord guild/server in which your client resides. |
207
|
|
|
Attributes |
208
|
|
|
---------- |
209
|
|
|
afk_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
210
|
|
|
Id of afk channel |
211
|
|
|
afk_timeout: :class:`int` |
212
|
|
|
Afk timeout in seconds |
213
|
|
|
application_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
214
|
|
|
Application id of the guild creator if it is bot-created |
215
|
|
|
banner: Optional[:class:`str`] |
216
|
|
|
Banner hash |
217
|
|
|
default_message_notifications: :class:`~pincer.objects.guild.guild.DefaultMessageNotificationLevel` |
|
|
|
|
218
|
|
|
Default message notifications level |
219
|
|
|
description: Optional[:class:`str`] |
220
|
|
|
The description of a Community guild |
221
|
|
|
discovery_splash: Optional[:class:`str`] |
222
|
|
|
Discovery splash hash; |
223
|
|
|
only present for guilds with the "DISCOVERABLE" feature |
224
|
|
|
emojis: List[:class:`~pincer.objects.message.emoji.Emoji`] |
225
|
|
|
Custom guild emojis |
226
|
|
|
explicit_content_filter: :class:`~pincer.objects.guild.guild.ExplicitContentFilterLevel` |
227
|
|
|
Explicit content filter level |
228
|
|
|
features: List[:class:`~pincer.objects.guild.features.GuildFeature`] |
229
|
|
|
Enabled guild features |
230
|
|
|
id: :class:`~pincer.utils.snowflake.Snowflake` |
231
|
|
|
Guild id |
232
|
|
|
icon: Optional[:class:`str`] |
233
|
|
|
Icon hash |
234
|
|
|
mfa_level: :class:`~pincer.objects.guild.guild.MFALevel` |
235
|
|
|
Required MFA level for the guild |
236
|
|
|
name: :class:`str` |
237
|
|
|
Guild name (2-100 characters, excluding trailing and leading |
238
|
|
|
whitespace) |
239
|
|
|
nsfw_level: :class:`~pincer.objects.guild.guild.NSFWLevel` |
240
|
|
|
Guild NSFW level |
241
|
|
|
owner_id: :class:`~pincer.utils.snowflake.Snowflake` |
242
|
|
|
Id of owner |
243
|
|
|
preferred_locale: :class:`str` |
244
|
|
|
The preferred locale of a Community guild; |
245
|
|
|
used in server discovery and notices from Discord; |
246
|
|
|
defaults to "en-US" |
247
|
|
|
premium_tier: :class:`~pincer.objects.guild.guild.PremiumTier` |
248
|
|
|
Premium tier (Server Boost level) |
249
|
|
|
public_updates_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
250
|
|
|
The id of the channel where admins |
251
|
|
|
and moderators of Community guilds receive notices from Discord |
252
|
|
|
roles: List[:class:`~pincer.objects.guild.role.Role`] |
253
|
|
|
Roles in the guild |
254
|
|
|
rules_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
255
|
|
|
The id of the channel where Community guilds can display rules |
256
|
|
|
and/or guidelines |
257
|
|
|
splash: Optional[:class:`str`] |
258
|
|
|
Splash hash |
259
|
|
|
system_channel_flags: :class:`~pincer.objects.guild.guild.SystemChannelFlags` |
260
|
|
|
System channel flags |
261
|
|
|
system_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
262
|
|
|
The id of the channel where guild notices |
263
|
|
|
such as welcome messages and boost events are posted |
264
|
|
|
vanity_url_code: Optional[:class:`str`] |
265
|
|
|
The vanity url code for the guild |
266
|
|
|
verification_level: :class:`~pincer.objects.guild.guild.VerificationLevel` |
267
|
|
|
Verification level required for the guild |
268
|
|
|
approximate_member_count: APINullable[:class:`int`] |
269
|
|
|
Approximate number of members in this guild, returned from the |
270
|
|
|
`GET /guilds/<id>` endpoint when with_counts is true |
271
|
|
|
approximate_presence_count: APINullable[:class:`int`] |
272
|
|
|
Approximate number of non-offline members in this guild, |
273
|
|
|
returned from the `GET /guilds/<id>` |
274
|
|
|
endpoint when with_counts is true |
275
|
|
|
channels: APINullable[List[:class:`~pincer.objects.guild.channel.Channel`]] |
276
|
|
|
Channels in the guild |
277
|
|
|
icon_hash: APINullable[Optional[:class:`str`]] |
278
|
|
|
Icon hash, returned when in the template object |
279
|
|
|
joined_at: APINullable[:class:`~pincer.utils.timestamp.Timestamp`] |
280
|
|
|
When this guild was joined at |
281
|
|
|
large: APINullable[:class:`bool`] |
282
|
|
|
True if this is considered a large guild |
283
|
|
|
max_members: APINullable[:class:`int`] |
284
|
|
|
The maximum number of members for the guild |
285
|
|
|
max_presences: APINullable[Optional[:class:`int`]] |
286
|
|
|
The maximum number of presences for the guild |
287
|
|
|
(null is always returned, apart from the largest of guilds) |
288
|
|
|
max_video_channel_users: APINullable[:class:`int`] |
289
|
|
|
The maximum amount of users in a video channel |
290
|
|
|
members: APINullable[List[:class:`~pincer.objects.guild.member.GuildMember`]] |
291
|
|
|
Users in the guild |
292
|
|
|
member_count: APINullable[:class:`bool`] |
293
|
|
|
Total number of members in this guild |
294
|
|
|
nsfw: APINullable[:class:`bool`] |
295
|
|
|
Boolean if the server is NSFW |
296
|
|
|
owner: APINullable[:class:`bool`] |
297
|
|
|
True if the user is the owner of the guild |
298
|
|
|
permissions: APINullable[:class:`str`] |
299
|
|
|
Total permissions for the user in the guild |
300
|
|
|
(excludes overwrites) |
301
|
|
|
premium_subscription_count: APINullable[:class:`int`] |
302
|
|
|
The number of boosts this guild currently has |
303
|
|
|
presences: APINullable[List[:class:`~pincer.objects.events.presence.PresenceUpdateEvent`]] |
304
|
|
|
Presences of the members in the guild, |
305
|
|
|
will only include non-offline members if the size is greater |
306
|
|
|
than large threshold |
307
|
|
|
stage_instances: APINullable[List[:class:`~pincer.objects.guild.stage.StageInstance`]] |
308
|
|
|
Stage instances in the guild |
309
|
|
|
stickers: Optional[List[:class:`~pincer.objects.message.sticker.Sticker`]] |
310
|
|
|
Custom guild stickers |
311
|
|
|
region: APINullable[Optional[:class:`str`]] |
312
|
|
|
Voice region id for the guild (deprecated) |
313
|
|
|
threads: APINullable[List[:class:`~pincer.objects.guild.channel.Channel`]] |
314
|
|
|
All active threads in the guild that current user |
315
|
|
|
has permission to view |
316
|
|
|
unavailable: APINullable[:class:`bool`] |
317
|
|
|
True if this guild is unavailable due to an outage |
318
|
|
|
voice_states: APINullable[List[:class:`~pincer.objects.user.voice_state.VoiceState`]] |
319
|
|
|
States of members currently in voice channels; |
320
|
|
|
lacks the guild_id key |
321
|
|
|
widget_enabled: APINullable[:class:`bool`] |
322
|
|
|
True if the server widget is enabled |
323
|
|
|
widget_channel_id: APINullable[Optional[:class:`~pincer.utils.snowflake.Snowflake`]] |
324
|
|
|
The channel id that the widget will generate an invite to, |
325
|
|
|
or null if set to no invite |
326
|
|
|
welcome_screen: APINullable[:class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`] |
327
|
|
|
The welcome screen of a Community guild, shown to new members, |
328
|
|
|
returned in an Invite's guild object |
329
|
|
|
""" |
330
|
|
|
|
331
|
|
|
# noqa: E501 |
332
|
|
|
afk_timeout: int |
333
|
|
|
default_message_notifications: DefaultMessageNotificationLevel |
334
|
|
|
emojis: List[Emoji] |
335
|
|
|
explicit_content_filter: ExplicitContentFilterLevel |
336
|
|
|
features: List[GuildFeature] |
337
|
|
|
id: Snowflake |
338
|
|
|
mfa_level: MFALevel |
339
|
|
|
name: str |
340
|
|
|
nsfw_level: GuildNSFWLevel |
341
|
|
|
owner_id: Snowflake |
342
|
|
|
preferred_locale: str |
343
|
|
|
premium_tier: PremiumTier |
344
|
|
|
roles: List[Role] |
345
|
|
|
system_channel_flags: SystemChannelFlags |
346
|
|
|
verification_level: VerificationLevel |
347
|
|
|
|
348
|
|
|
guild_scheduled_events: APINullable[List] = MISSING |
349
|
|
|
lazy: APINullable[bool] = MISSING |
350
|
|
|
premium_progress_bar_enabled: APINullable[bool] = MISSING |
351
|
|
|
guild_hashes: APINullable[Dict] = MISSING |
352
|
|
|
afk_channel_id: APINullable[Snowflake] = MISSING |
353
|
|
|
application_id: APINullable[Snowflake] = MISSING |
354
|
|
|
embedded_activities: APINullable[List] = MISSING |
355
|
|
|
banner: APINullable[str] = MISSING |
356
|
|
|
description: APINullable[str] = MISSING |
357
|
|
|
discovery_splash: APINullable[str] = MISSING |
358
|
|
|
icon: APINullable[str] = MISSING |
359
|
|
|
public_updates_channel_id: APINullable[Snowflake] = MISSING |
360
|
|
|
rules_channel_id: APINullable[Snowflake] = MISSING |
361
|
|
|
splash: APINullable[str] = MISSING |
362
|
|
|
system_channel_id: APINullable[Snowflake] = MISSING |
363
|
|
|
vanity_url_code: APINullable[str] = MISSING |
364
|
|
|
|
365
|
|
|
application_command_counts: APINullable[Dict] = MISSING |
366
|
|
|
application_command_count: APINullable[int] = MISSING |
367
|
|
|
approximate_member_count: APINullable[int] = MISSING |
368
|
|
|
approximate_presence_count: APINullable[int] = MISSING |
369
|
|
|
channels: APINullable[List[Channel]] = field(default_factory=list) |
370
|
|
|
# TODO: Add type when type is known |
|
|
|
|
371
|
|
|
hub_type: APINullable[Any] = MISSING |
372
|
|
|
icon_hash: APINullable[Optional[str]] = MISSING |
373
|
|
|
joined_at: APINullable[Timestamp] = MISSING |
374
|
|
|
large: APINullable[bool] = MISSING |
375
|
|
|
max_members: APINullable[int] = MISSING |
376
|
|
|
max_presences: APINullable[Optional[int]] = MISSING |
377
|
|
|
max_video_channel_users: APINullable[int] = MISSING |
378
|
|
|
members: APINullable[List[GuildMember]] = MISSING |
379
|
|
|
member_count: APINullable[bool] = MISSING |
380
|
|
|
nsfw: APINullable[bool] = MISSING |
381
|
|
|
# Note: This is missing from discord's docs but in the api |
382
|
|
|
owner: APINullable[bool] = MISSING |
383
|
|
|
permissions: APINullable[str] = MISSING |
384
|
|
|
premium_subscription_count: APINullable[int] = MISSING |
385
|
|
|
presences: APINullable[List[PresenceUpdateEvent]] = MISSING |
386
|
|
|
stage_instances: APINullable[List[StageInstance]] = MISSING |
387
|
|
|
stickers: APINullable[List[Sticker]] = MISSING |
388
|
|
|
region: APINullable[Optional[str]] = MISSING |
389
|
|
|
threads: APINullable[List[Channel]] = MISSING |
390
|
|
|
# Guilds are considered available unless otherwise specified |
391
|
|
|
unavailable: APINullable[bool] = False |
392
|
|
|
voice_states: APINullable[List[VoiceState]] = MISSING |
393
|
|
|
widget_enabled: APINullable[bool] = MISSING |
394
|
|
|
widget_channel_id: APINullable[Optional[Snowflake]] = MISSING |
395
|
|
|
welcome_screen: APINullable[WelcomeScreen] = MISSING |
396
|
|
|
|
397
|
|
|
@classmethod |
398
|
|
|
async def from_id(cls, client: Client, _id: Union[int, Snowflake]) -> Guild: |
399
|
|
|
""" |
400
|
|
|
Parameters |
401
|
|
|
---------- |
402
|
|
|
client : `~pincer.Client` |
403
|
|
|
Client object to use the http gateway from. |
404
|
|
|
_id : :class: `pincer.utils.snowflake.Snowflake` |
405
|
|
|
Guild ID. |
406
|
|
|
Returns |
407
|
|
|
------- |
408
|
|
|
:class: `~pincer.objects.guild.guild.Guild` |
409
|
|
|
The new guild object. |
410
|
|
|
""" |
411
|
|
|
data = await client.http.get(f"/guilds/{_id}") |
412
|
|
|
channel_data = await client.http.get(f"/guilds/{_id}/channels") |
413
|
|
|
|
414
|
|
|
data["channels"]: List[Channel] = [ |
415
|
|
|
Channel.from_dict({**i, "_client": client, "_http": client.http}) |
416
|
|
|
for i in (channel_data or []) |
417
|
|
|
] |
418
|
|
|
|
419
|
|
|
return Guild.from_dict(construct_client_dict(client, data)) |
420
|
|
|
|
421
|
|
|
async def get_member(self, _id: int) -> GuildMember: |
422
|
|
|
"""|coro| |
423
|
|
|
Fetches a GuildMember from its identifier |
424
|
|
|
Parameters |
425
|
|
|
---------- |
426
|
|
|
_id: |
427
|
|
|
The id of the guild member which should be fetched from the Discord |
428
|
|
|
gateway. |
429
|
|
|
Returns |
430
|
|
|
------- |
431
|
|
|
:class:`~pincer.objects.guild.member.GuildMember` |
432
|
|
|
A GuildMember object. |
433
|
|
|
""" |
434
|
|
|
return await GuildMember.from_id(self._client, self.id, _id) |
435
|
|
|
|
436
|
|
|
@overload |
437
|
|
|
async def modify_member( |
438
|
|
|
self, |
|
|
|
|
439
|
|
|
*, |
|
|
|
|
440
|
|
|
_id: int, |
|
|
|
|
441
|
|
|
nick: Optional[str] = None, |
|
|
|
|
442
|
|
|
roles: Optional[List[Snowflake]] = None, |
|
|
|
|
443
|
|
|
mute: Optional[bool] = None, |
|
|
|
|
444
|
|
|
deaf: Optional[bool] = None, |
|
|
|
|
445
|
|
|
channel_id: Optional[Snowflake] = None, |
|
|
|
|
446
|
|
|
) -> GuildMember: |
447
|
|
|
"""|coro| |
448
|
|
|
Modifies a member in the guild from its identifier and based on the |
449
|
|
|
keyword arguments provided. |
450
|
|
|
Parameters |
451
|
|
|
---------- |
452
|
|
|
_id : int |
453
|
|
|
Id of the member to modify |
454
|
|
|
nick : Optional[:class:`str`] |
455
|
|
|
New nickname for the member |default| :data:`None` |
456
|
|
|
roles : Optional[List[:class:`~pincer.utils.snowflake.Snowflake]] |
457
|
|
|
New roles for the member |default| :data:`None` |
458
|
|
|
mute : Optional[:class:`bool`] |
459
|
|
|
Whether the member is muted |default| :data:`None` |
460
|
|
|
deaf : Optional[:class:`bool`] |
461
|
|
|
Whether the member is deafened |default| :data:`None` |
462
|
|
|
channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake] |
463
|
|
|
Voice channel id to move to |default| :data:`None` |
464
|
|
|
Returns |
465
|
|
|
------- |
466
|
|
|
:class:`~pincer.objects.guild.member.GuildMember` |
467
|
|
|
The new member object. |
468
|
|
|
""" |
469
|
|
|
... |
470
|
|
|
|
471
|
|
|
async def modify_member(self, _id: int, **kwargs) -> GuildMember: |
|
|
|
|
472
|
|
|
data = await self._http.patch( |
473
|
|
|
f"guilds/{self.id}/members/{_id}", data=kwargs |
474
|
|
|
) |
475
|
|
|
return GuildMember.from_dict(construct_client_dict(self._client, data)) |
476
|
|
|
|
477
|
|
|
async def ban( |
478
|
|
|
self, |
|
|
|
|
479
|
|
|
member_id: int, |
|
|
|
|
480
|
|
|
reason: str = None, |
|
|
|
|
481
|
|
|
delete_message_days: int = None |
|
|
|
|
482
|
|
|
): |
483
|
|
|
""" |
484
|
|
|
Parameters |
485
|
|
|
---------- |
486
|
|
|
member_id : :class:`int` |
487
|
|
|
ID of the guild member to ban. |
488
|
|
|
reason : Optional[:class:`str`] |
489
|
|
|
Reason for the kick. |
490
|
|
|
delete_message_days : Optional[:class:`int`] |
491
|
|
|
Number of days to delete messages for (0-7) |
492
|
|
|
""" |
493
|
|
|
headers = {} |
494
|
|
|
|
495
|
|
|
if reason is not None: |
496
|
|
|
headers["X-Audit-Log-Reason"] = reason |
497
|
|
|
|
498
|
|
|
data = {} |
499
|
|
|
|
500
|
|
|
if delete_message_days is not None: |
501
|
|
|
data["delete_message_days"] = delete_message_days |
502
|
|
|
|
503
|
|
|
await self._http.put( |
504
|
|
|
f"/guilds/{self.id}/bans/{member_id}", |
505
|
|
|
data=data, |
506
|
|
|
headers=headers |
507
|
|
|
) |
508
|
|
|
|
509
|
|
|
async def kick(self, member_id: int, reason: Optional[str] = None): |
510
|
|
|
"""|coro| |
511
|
|
|
Kicks a guild member. |
512
|
|
|
Parameters |
513
|
|
|
---------- |
514
|
|
|
member_id : :class:`int` |
515
|
|
|
ID of the guild member to kick. |
516
|
|
|
reason : Optional[:class:`str`] |
517
|
|
|
Reason for the kick. |
518
|
|
|
""" |
519
|
|
|
|
520
|
|
|
headers = {} |
521
|
|
|
|
522
|
|
|
if reason is not None: |
523
|
|
|
headers["X-Audit-Log-Reason"] = reason |
524
|
|
|
|
525
|
|
|
await self._http.delete( |
526
|
|
|
f"/guilds/{self.id}/members/{member_id}", |
527
|
|
|
header=headers |
528
|
|
|
) |
529
|
|
|
|
530
|
|
|
async def get_roles(self) -> AsyncGenerator[Role, None]: |
531
|
|
|
"""|coro| |
532
|
|
|
Fetches all the roles in the guild. |
533
|
|
|
|
534
|
|
|
Returns |
535
|
|
|
------- |
536
|
|
|
AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`] |
537
|
|
|
An async generator of Role objects. |
538
|
|
|
""" |
539
|
|
|
data = await self._http.get(f"guilds/{self.id}/roles") |
540
|
|
|
for role_data in data: |
541
|
|
|
yield Role.from_dict(construct_client_dict(self._client, role_data)) |
542
|
|
|
|
543
|
|
|
@overload |
544
|
|
|
async def create_role( |
545
|
|
|
self, |
|
|
|
|
546
|
|
|
reason: Optional[str] = None, |
|
|
|
|
547
|
|
|
*, |
|
|
|
|
548
|
|
|
name: Optional[str] = "new role", |
|
|
|
|
549
|
|
|
permissions: Optional[str] = None, |
|
|
|
|
550
|
|
|
color: Optional[int] = 0, |
|
|
|
|
551
|
|
|
hoist: Optional[bool] = False, |
|
|
|
|
552
|
|
|
icon: Optional[str] = None, |
|
|
|
|
553
|
|
|
unicode_emoji: Optional[str] = None, |
|
|
|
|
554
|
|
|
mentionable: Optional[bool] = False, |
|
|
|
|
555
|
|
|
) -> Role: |
556
|
|
|
"""|coro| |
557
|
|
|
Creates a new role for the guild. |
558
|
|
|
Requires the ``MANAGE_ROLES`` permission. |
559
|
|
|
|
560
|
|
|
Parameters |
561
|
|
|
---------- |
562
|
|
|
reason : Optional[:class:`str`] |
563
|
|
|
Reason for creating the role. |default| :data:`None` |
564
|
|
|
name : Optional[:class:`str`] |
565
|
|
|
name of the role |default| :data:`"new role"` |
566
|
|
|
permissions : Optional[:class:`str`] |
567
|
|
|
bitwise value of the enabled/disabled |
568
|
|
|
permissions, set to @everyone permissions |
569
|
|
|
by default |default| :data:`None` |
570
|
|
|
color : Optional[:class:`int`] |
571
|
|
|
RGB color value |default| :data:`0` |
572
|
|
|
hoist : Optional[:class:`bool`] |
573
|
|
|
whether the role should be displayed |
574
|
|
|
separately in the sidebar |default| :data:`False` |
575
|
|
|
icon : Optional[:class:`str`] |
576
|
|
|
the role's icon image (if the guild has |
577
|
|
|
the ``ROLE_ICONS`` feature) |default| :data:`None` |
578
|
|
|
unicode_emoji : Optional[:class:`str`] |
579
|
|
|
the role's unicode emoji as a standard emoji (if the guild |
580
|
|
|
has the ``ROLE_ICONS`` feature) |default| :data:`None` |
581
|
|
|
mentionable : Optional[:class:`bool`] |
582
|
|
|
whether the role should be mentionable |default| :data:`False` |
583
|
|
|
|
584
|
|
|
Returns |
585
|
|
|
------- |
586
|
|
|
:class:`~pincer.objects.guild.role.Role` |
587
|
|
|
The new role object. |
588
|
|
|
""" |
589
|
|
|
... |
590
|
|
|
|
591
|
|
|
async def create_role( |
|
|
|
|
592
|
|
|
self, |
|
|
|
|
593
|
|
|
reason: Optional[str] = None, |
|
|
|
|
594
|
|
|
**kwargs |
|
|
|
|
595
|
|
|
) -> Role: |
596
|
|
|
return Role.from_dict( |
597
|
|
|
construct_client_dict( |
598
|
|
|
self._client, |
599
|
|
|
await self._http.post( |
600
|
|
|
f"guilds/{self.id}/roles", |
601
|
|
|
data=kwargs, |
602
|
|
|
headers={"X-Audit-Log-Reason": reason} |
603
|
|
|
if reason is not None |
604
|
|
|
else {}, |
605
|
|
|
), |
606
|
|
|
) |
607
|
|
|
) |
608
|
|
|
|
609
|
|
|
async def edit_role_position( |
610
|
|
|
self, |
|
|
|
|
611
|
|
|
id: Snowflake, |
|
|
|
|
612
|
|
|
reason: Optional[str] = None, |
|
|
|
|
613
|
|
|
position: Optional[int] = None |
|
|
|
|
614
|
|
|
) -> AsyncGenerator[Role, None]: |
615
|
|
|
"""|coro| |
616
|
|
|
Edits the position of a role. |
617
|
|
|
|
618
|
|
|
Parameters |
619
|
|
|
---------- |
620
|
|
|
id : :class:`~pincer.utils.snowflake.Snowflake` |
621
|
|
|
The role ID |
622
|
|
|
reason : Optional[:class:`str`] |
623
|
|
|
Reason for editing the role position. |default| :data:`None` |
624
|
|
|
position : Optional[:class:`int`] |
625
|
|
|
Sorting position of the role |default| :data:`None` |
626
|
|
|
|
627
|
|
|
Returns |
628
|
|
|
------- |
629
|
|
|
AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`] |
630
|
|
|
An async generator of all of the guild's role objects. |
631
|
|
|
""" |
632
|
|
|
data = await self._http.patch( |
633
|
|
|
f"guilds/{self.id}/roles", |
634
|
|
|
data={"id": id, "position": position}, |
635
|
|
|
headers={"X-Audit-Log-Reason": reason} |
636
|
|
|
if reason is not None |
637
|
|
|
else {} |
638
|
|
|
) |
639
|
|
|
for role_data in data: |
640
|
|
|
yield Role.from_dict(construct_client_dict(self._client, role_data)) |
641
|
|
|
|
642
|
|
|
@overload |
643
|
|
|
async def edit_role( |
644
|
|
|
self, |
|
|
|
|
645
|
|
|
id: Snowflake, |
|
|
|
|
646
|
|
|
reason: Optional[str] = None, |
|
|
|
|
647
|
|
|
*, |
|
|
|
|
648
|
|
|
name: Optional[str] = None, |
|
|
|
|
649
|
|
|
permissions: Optional[str] = None, |
|
|
|
|
650
|
|
|
color: Optional[int] = None, |
|
|
|
|
651
|
|
|
hoist: Optional[bool] = None, |
|
|
|
|
652
|
|
|
icon: Optional[str] = None, |
|
|
|
|
653
|
|
|
unicode_emoji: Optional[str] = None, |
|
|
|
|
654
|
|
|
mentionable: Optional[bool] = None, |
|
|
|
|
655
|
|
|
) -> Role: |
656
|
|
|
"""|coro| |
657
|
|
|
Edits a role. |
658
|
|
|
Requires the ``MANAGE_ROLES`` permission. |
659
|
|
|
|
660
|
|
|
Parameters |
661
|
|
|
---------- |
662
|
|
|
id : :class:`~pincer.utils.snowflake.Snowflake` |
663
|
|
|
The role ID |
664
|
|
|
reason : Optional[:class:`str`] |
665
|
|
|
Reason for editing the role |default| :data:`None` |
666
|
|
|
name : Optional[:class:`str`] |
667
|
|
|
Name of the role |default| :data:`None` |
668
|
|
|
permissions : Optional[:class:`str`] |
669
|
|
|
Bitwise value of the enabled/disabled |
670
|
|
|
permissions |default| :data:`None` |
671
|
|
|
color : Optional[:class:`int`] |
672
|
|
|
RGB color value |default| :data:`None` |
673
|
|
|
hoist : Optional[:class:`bool`] |
674
|
|
|
Whether the role should be displayed |
675
|
|
|
separately in the sidebar |default| :data:`None` |
676
|
|
|
icon : Optional[:class:`str`] |
677
|
|
|
The role's icon image (if the guild has |
678
|
|
|
the ``ROLE_ICONS`` feature) |default| :data:`None` |
679
|
|
|
unicode_emoji : Optional[:class:`str`] |
680
|
|
|
The role's unicode emoji as a standard emoji (if the guild |
681
|
|
|
has the ``ROLE_ICONS`` feature) |default| :data:`None` |
682
|
|
|
mentionable : Optional[:class:`bool`] |
683
|
|
|
Whether the role should be mentionable |default| :data:`None` |
684
|
|
|
|
685
|
|
|
Returns |
686
|
|
|
------- |
687
|
|
|
:class:`~pincer.objects.guild.role.Role` |
688
|
|
|
The edited role object. |
689
|
|
|
""" |
690
|
|
|
... |
691
|
|
|
|
692
|
|
|
async def edit_role( |
|
|
|
|
693
|
|
|
self, |
|
|
|
|
694
|
|
|
id: Snowflake, |
|
|
|
|
695
|
|
|
reason: Optional[str] = None, |
|
|
|
|
696
|
|
|
**kwargs |
|
|
|
|
697
|
|
|
) -> Role: |
698
|
|
|
return Role.from_dict( |
699
|
|
|
construct_client_dict( |
700
|
|
|
self._client, |
701
|
|
|
await self._http.patch( |
702
|
|
|
f"guilds/{self.id}/roles/{id}", |
703
|
|
|
data=kwargs, |
704
|
|
|
headers={"X-Audit-Log-Reason": reason} |
705
|
|
|
if reason is not None |
706
|
|
|
else {}, |
707
|
|
|
), |
708
|
|
|
) |
709
|
|
|
) |
710
|
|
|
|
711
|
|
|
async def delete_role(self, id: Snowflake, reason: Optional[str] = None): |
|
|
|
|
712
|
|
|
"""|coro| |
713
|
|
|
Deletes a role. |
714
|
|
|
Requires the `MANAGE_ROLES` permission. |
715
|
|
|
|
716
|
|
|
Parameters |
717
|
|
|
---------- |
718
|
|
|
id : :class:`~pincer.utils.snowflake.Snowflake` |
719
|
|
|
The role ID |
720
|
|
|
reason : Optional[:class:`str`] |
721
|
|
|
The reason for deleting the role |default| :data:`None` |
722
|
|
|
""" |
723
|
|
|
await self._http.delete( |
724
|
|
|
f"guilds/{self.id}/roles/{id}", |
725
|
|
|
headers={"X-Audit-Log-Reason": reason} |
726
|
|
|
if reason is not None |
727
|
|
|
else {}, |
728
|
|
|
) |
729
|
|
|
|
730
|
|
|
async def get_bans(self) -> AsyncGenerator[Ban, None]: |
731
|
|
|
"""|coro| |
732
|
|
|
Fetches all the bans in the guild. |
733
|
|
|
|
734
|
|
|
Returns |
735
|
|
|
------- |
736
|
|
|
AsyncGenerator[:class:`~pincer.objects.guild.ban.Ban`, :data:`None`] |
737
|
|
|
An async generator of Ban objects. |
738
|
|
|
""" |
739
|
|
|
data = await self._http.get(f"guilds/{self.id}/bans") |
740
|
|
|
for ban_data in data: |
741
|
|
|
yield Ban.from_dict(construct_client_dict(self._client, ban_data)) |
742
|
|
|
|
743
|
|
|
async def get_ban(self, id: Snowflake) -> Ban: |
|
|
|
|
744
|
|
|
"""|coro| |
745
|
|
|
Fetches a ban from the guild. |
746
|
|
|
|
747
|
|
|
Parameters |
748
|
|
|
---------- |
749
|
|
|
id : :class:`~pincer.utils.snowflake.Snowflake` |
750
|
|
|
The user ID |
751
|
|
|
|
752
|
|
|
Returns |
753
|
|
|
------- |
754
|
|
|
:class:`~pincer.objects.guild.ban.Ban` |
755
|
|
|
The Ban object. |
756
|
|
|
""" |
757
|
|
|
return Ban.from_dict( |
758
|
|
|
construct_client_dict( |
759
|
|
|
self._client, |
760
|
|
|
await self._http.get(f"guilds/{self.id}/bans/{id}"), |
761
|
|
|
) |
762
|
|
|
) |
763
|
|
|
|
764
|
|
|
async def unban(self, id: Snowflake, reason: Optional[str] = None): |
|
|
|
|
765
|
|
|
"""|coro| |
766
|
|
|
Unbans a user from the guild. |
767
|
|
|
|
768
|
|
|
Parameters |
769
|
|
|
---------- |
770
|
|
|
id : :class:`~pincer.utils.snowflake.Snowflake` |
771
|
|
|
The user ID |
772
|
|
|
reason : Optional[:class:`str`] |
773
|
|
|
The reason for unbanning the user |default| :data:`None` |
774
|
|
|
""" |
775
|
|
|
await self._http.delete( |
776
|
|
|
f"guilds/{self.id}/bans/{id}", |
777
|
|
|
headers={"X-Audit-Log-Reason": reason} |
778
|
|
|
if reason is not None |
779
|
|
|
else {} |
780
|
|
|
) |
781
|
|
|
|
782
|
|
|
@overload |
783
|
|
|
async def edit( |
|
|
|
|
784
|
|
|
self, |
|
|
|
|
785
|
|
|
*, |
|
|
|
|
786
|
|
|
name: Optional[str] = None, |
|
|
|
|
787
|
|
|
region: Optional[str] = None, |
|
|
|
|
788
|
|
|
verification_level: Optional[int] = None, |
|
|
|
|
789
|
|
|
default_message_notifications: Optional[int] = None, |
|
|
|
|
790
|
|
|
explicit_content_filter: Optional[int] = None, |
|
|
|
|
791
|
|
|
afk_channel_id: Optional[Snowflake] = None, |
|
|
|
|
792
|
|
|
afk_timeout: Optional[int] = None, |
|
|
|
|
793
|
|
|
icon: Optional[str] = None, |
|
|
|
|
794
|
|
|
owner_id: Optional[Snowflake] = None, |
|
|
|
|
795
|
|
|
splash: Optional[str] = None, |
|
|
|
|
796
|
|
|
discovery_splash: Optional[str] = None, |
|
|
|
|
797
|
|
|
banner: Optional[str] = None, |
|
|
|
|
798
|
|
|
system_channel_id: Optional[Snowflake] = None, |
|
|
|
|
799
|
|
|
system_channel_flags: Optional[int] = None, |
|
|
|
|
800
|
|
|
rules_channel_id: Optional[Snowflake] = None, |
|
|
|
|
801
|
|
|
public_updates_channel_id: Optional[Snowflake] = None, |
|
|
|
|
802
|
|
|
preferred_locale: Optional[str] = None, |
|
|
|
|
803
|
|
|
features: Optional[List[GuildFeature]] = None, |
|
|
|
|
804
|
|
|
description: Optional[str] = None, |
|
|
|
|
805
|
|
|
) -> Guild: |
806
|
|
|
"""|coro| |
807
|
|
|
Modifies the guild |
808
|
|
|
|
809
|
|
|
Parameters |
810
|
|
|
---------- |
811
|
|
|
name : Optional[:class:`str`] |
812
|
|
|
Guild name |default| :data:`None` |
813
|
|
|
region : Optional[:class:`str`] |
814
|
|
|
Guild voice region ID |default| :data:`None` |
815
|
|
|
verification_level : Optional[:class:`int`] |
816
|
|
|
Verification level |default| :data:`None` |
817
|
|
|
default_message_notifications : Optional[:class:`int`] |
818
|
|
|
Default message notification level |default| :data:`None` |
819
|
|
|
explicit_content_filter : Optional[:class:`int`] |
820
|
|
|
Explicit content filter level |default| :data:`None` |
821
|
|
|
afk_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
822
|
|
|
ID for AFK channel |default| :data:`None` |
823
|
|
|
afk_timeout : Optional[:class:`int`] |
824
|
|
|
AFK timeout in seconds |default| :data:`None` |
825
|
|
|
icon : Optional[:class:`str`] |
826
|
|
|
base64 1024x1024 png/jpeg/gif image for the guild icon |
827
|
|
|
(can be animated gif when the server |
828
|
|
|
has the `ANIMATED_ICON` feature) |default| :data:`None` |
829
|
|
|
owner_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
830
|
|
|
User ID to transfer guild ownership to (must be owner) |default| :data:`None` |
831
|
|
|
splash : Optional[:class:`str`] |
832
|
|
|
base64 16:9 png/jpeg image for the guild splash (when the |
833
|
|
|
server has the `INVITE_SPLASH` feature) |default| :data:`None` |
834
|
|
|
discovery_splash : Optional[:class:`str`] |
835
|
|
|
base64 16:9 png/jpeg image for the guild discovery splash |
836
|
|
|
(when the server has the `DISCOVERABLE` feature) |default| :data:`None` |
837
|
|
|
banner : Optional[:class:`str`] |
838
|
|
|
base64 16:9 png/jpeg image for the guild banner (when the |
839
|
|
|
server has the `BANNER` feature) |default| :data:`None` |
840
|
|
|
system_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
841
|
|
|
The ID of the channel where guild notices such as welcome |
842
|
|
|
messages and boost events are posted |default| :data:`None` |
843
|
|
|
system_channel_flags : Optional[:class:`int`] |
844
|
|
|
System channel flags |default| :data:`None` |
845
|
|
|
rules_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
846
|
|
|
The ID of the channel where Community guilds display rules |
847
|
|
|
and/or guidelines |default| :data:`None` |
848
|
|
|
public_updates_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`] |
849
|
|
|
The ID of the channel where admins and moderators of |
850
|
|
|
Community guilds receive notices from Discord |default| :data:`None` |
851
|
|
|
preferred_locale : Optional[:class:`str`] |
852
|
|
|
The preferred locale of a Community guild used in server |
853
|
|
|
discovery and notices from Discord; defaults to "en-US" |default| :data:`None` |
854
|
|
|
features : Optional[List[:class:`GuildFeature`]] |
855
|
|
|
Enabled guild features |default| :data:`None` |
856
|
|
|
description : Optional[:class:`str`] |
857
|
|
|
The description for the guild, if the guild is discoverable |default| :data:`None` |
858
|
|
|
|
859
|
|
|
Returns |
860
|
|
|
------- |
861
|
|
|
:class:`~pincer.objects.guild.Guild` |
862
|
|
|
The modified guild object. |
863
|
|
|
""" |
864
|
|
|
... |
865
|
|
|
|
866
|
|
|
async def edit(self, **kwargs) -> Guild: |
|
|
|
|
867
|
|
|
g = await self._http.patch(f"guilds/{self.id}", data=kwargs) |
868
|
|
|
return Guild.from_dict(construct_client_dict(self._client, g)) |
869
|
|
|
|
870
|
|
|
async def preview(self) -> GuildPreview: |
871
|
|
|
"""|coro| |
872
|
|
|
Previews the guild. |
873
|
|
|
|
874
|
|
|
Returns |
875
|
|
|
------- |
876
|
|
|
:class:`~pincer.objects.guild.guild.GuildPreview` |
877
|
|
|
The guild preview object. |
878
|
|
|
""" |
879
|
|
|
data = await self._http.get(f"guilds/{self.id}/preview") |
880
|
|
|
return GuildPreview.from_dict(data) |
881
|
|
|
|
882
|
|
|
async def delete(self): |
883
|
|
|
"""|coro| |
884
|
|
|
Deletes the guild. Returns `204 No Content` on success. |
885
|
|
|
""" |
886
|
|
|
await self._http.delete(f"guilds/{self.id}") |
887
|
|
|
|
888
|
|
|
@classmethod |
889
|
|
|
def from_dict(cls, data) -> Guild: |
890
|
|
|
""" |
891
|
|
|
Parameters |
892
|
|
|
---------- |
893
|
|
|
data : :class:`Dict` |
894
|
|
|
Guild data received from the discord API. |
895
|
|
|
Returns |
896
|
|
|
------- |
897
|
|
|
:class:`~pincer.objects.guild.guild.Guild` |
898
|
|
|
The new guild object. |
899
|
|
|
Raises |
900
|
|
|
:class:`~pincer.exceptions.UnavailableGuildError` |
901
|
|
|
The guild is unavailable due to a discord outage. |
902
|
|
|
""" |
903
|
|
|
if data.get("unavailable", False): |
904
|
|
|
raise UnavailableGuildError( |
905
|
|
|
f"Guild \"{data['id']}\" is unavailable due to a discord" |
906
|
|
|
" outage." |
907
|
|
|
) |
908
|
|
|
|
909
|
|
|
return super().from_dict(data) |
910
|
|
|
|
911
|
|
|
|
912
|
|
|
@dataclass |
|
|
|
|
913
|
|
|
class UnavailableGuild(APIObject): |
914
|
|
|
id: Snowflake |
915
|
|
|
unavailable: bool = True |
916
|
|
|
|