Passed
Pull Request — main (#248)
by
unknown
01:55
created

Guild.get_widget_settings()   A

Complexity

Conditions 1

Size

Total Lines 12
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
# Copyright Pincer 2021-Present
0 ignored issues
show
introduced by
invalid syntax (<fstring>, line 1)
Loading history...
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 ...exceptions import UnavailableGuildError
11
from ...utils.api_object import APIObject
12
from ...utils.conversion import construct_client_dict
13
from ...utils.types import MISSING
14
15
if TYPE_CHECKING:
16
    from typing import Any, Dict, List, Optional, Union
17
18
    from .ban import Ban
19
    from .channel import Channel
20
    from .invite import Invite
21
    from .member import GuildMember
22
    from .widget import GuildWidget
23
    from .features import GuildFeature
24
    from .role import Role
25
    from .stage import StageInstance
26
    from .welcome_screen import WelcomeScreen, WelcomeScreenChannel
27
    from ..user.user import User
28
    from ..user.integration import Integration
29
    from ..voice.region import VoiceRegion
30
    from ..events.presence import PresenceUpdateEvent
31
    from ..message.emoji import Emoji
32
    from ..message.sticker import Sticker
33
    from ..user.voice_state import VoiceState
34
    from ...client import Client
35
    from ...utils.timestamp import Timestamp
36
    from ...utils.types import APINullable, JSONSerializable
37
    from ...utils.snowflake import Snowflake
38
39
40
class PremiumTier(IntEnum):
41
    """Represents the boost tier of a guild.
42
    Attributes
43
    ----------
44
    NONE:
45
        Guild has not unlocked any Server Boost perks.
46
    TIER_1:
47
        Guild has unlocked Server Boost level 1 perks.
48
    TIER_2:
49
        Guild has unlocked Server Boost level 2 perks.
50
    TIER_3:
51
        Guild has unlocked Server Boost level 3 perks.
52
    """
53
54
    NONE = 0
55
    TIER_1 = 1
56
    TIER_2 = 2
57
    TIER_3 = 3
58
59
60
class GuildNSFWLevel(IntEnum):
61
    """Represents the NSFW level of a guild.
62
    Attributes
63
    ----------
64
    DEFAULT:
65
        Default NSFW level.
66
    EXPLICIT:
67
        Explicit NSFW level.
68
    SAFE:
69
        SAFE NSFW level.
70
    AGE_RESTRICTED:
71
        Age restricted NSFW level.
72
    """
73
74
    DEFAULT = 0
75
    EXPLICIT = 1
76
    SAFE = 2
77
    AGE_RESTRICTED = 3
78
79
80
class ExplicitContentFilterLevel(IntEnum):
81
    """Represents the filter content level of a guild.
82
    Attributes
83
    ----------
84
    DISABLED:
85
        Media content will not be scanned.
86
    MEMBERS_WITHOUT_ROLES:
87
        Media content sent by members without roles will be scanned.
88
    ALL_MEMBERS:
89
        Media content sent by all members will be scanned.
90
    """
91
92
    DISABLED = 0
93
    MEMBERS_WITHOUT_ROLES = 1
94
    ALL_MEMBERS = 2
95
96
97
class MFALevel(IntEnum):
98
    """Represents the multi factor authentication level of a guild.
99
    Attributes
100
    ----------
101
    NONE:
102
        Guild has no MFA/2FA requirement for moderation actions.
103
    ELEVATED:
104
        Guild has a 2FA requirement for moderation actions
105
    """
106
107
    NONE = 0
108
    ELEVATED = 1
109
110
111
class VerificationLevel(IntEnum):
112
    """Represents the verification level of a guild.
113
    Attributes
114
    ----------
115
    NONE:
116
        Unrestricted.
117
    LOW:
118
        Must have verified email on account.
119
    MEDIUM:
120
        Must be registered on Discord for longer than 5 minutes.
121
    HIGH:
122
        Must be a member of the server for longer than 10 minutes.
123
    VERY_HIGH:
124
        Must have a verified phone number.
125
    """
126
127
    NONE = 0
128
    LOW = 1
129
    MEDIUM = 2
130
    HIGH = 3
131
    VERY_HIGH = 4
132
133
134
class DefaultMessageNotificationLevel(IntEnum):
135
    """Represents the default message notification level of a guild.
136
    Attributes
137
    ----------
138
    ALL_MESSAGES:
139
        Members will receive notifications for all messages by default.
140
    ONLY_MENTIONS:
141
        Members will receive notifications only for messages that @mention them by default.
142
    """
143
144
    # noqa: E501
145
    ALL_MESSAGES = 0
146
    ONLY_MENTIONS = 1
147
148
149
class SystemChannelFlags(IntEnum):
150
    """Represents the system channel flags of a guild.
151
    Attributes
152
    ----------
153
    SUPPRESS_JOIN_NOTIFICATIONS:
154
        Suppress member join notifications.
155
    SUPPRESS_PREMIUM_SUBSCRIPTIONS:
156
        Suppress server boost notifications.
157
    SUPPRESS_GUILD_REMINDER_NOTIFICATIONS:
158
        Suppress server setup tips.
159
    SUPPRESS_JOIN_NOTIFICATION_REPLIES:
160
        Hide member join sticker reply buttons
161
    """
162
163
    SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0
164
    SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1
165
    SUPPRESS_GUILD_REMINDER_NOTIFICATIONS = 1 << 2
166
    SUPPRESS_JOIN_NOTIFICATION_REPLIES = 1 << 3
167
168
169
@dataclass
170
class GuildPreview(APIObject):
171
    """Represents a guild preview.
172
    Attributes
173
    ----------
174
    id: :class:`Snowflake`
175
        The guild ID.
176
    name: :class:`str`
177
        The guild name.
178
    icon: :class:`str`
179
        The guild icon hash.
180
    splash: :class:`str`
181
        The guild splash hash.
182
    discovery_splash: :class:`str`
183
        The guild discovery splash hash.
184
    emojis: :class:`List[Emoji]`
185
        The guild emojis.
186
    features: :class:`List[GuildFeature]`
187
        The guild features.
188
    approximate_member_count: :class:`int`
189
        The approximate member count.
190
    approximate_presence_count: :class:`int`
191
        The approximate number of online members in this guild
192
    description: :class:`str`
193
        The guild description.
194
    """
195
196
    id: Snowflake
197
    name: str
198
    emojis: List[Emoji]
199
    features: List[GuildFeature]
200
    approximate_member_count: int
201
    approximate_presence_count: int
202
203
    icon: APINullable[str] = MISSING
204
    splash: APINullable[str] = MISSING
205
    discovery_splash: APINullable[str] = MISSING
206
    description: APINullable[str] = MISSING
207
208
209
@dataclass
210
class Guild(APIObject):
211
    """Represents a Discord guild/server in which your client resides.
212
    Attributes
213
    ----------
214
    afk_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
215
        Id of afk channel
216
    afk_timeout: :class:`int`
217
        Afk timeout in seconds
218
    application_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
219
        Application id of the guild creator if it is bot-created
220
    banner: Optional[:class:`str`]
221
        Banner hash
222
    default_message_notifications: :class:`~pincer.objects.guild.guild.DefaultMessageNotificationLevel`
223
        Default message notifications level
224
    description: Optional[:class:`str`]
225
        The description of a Community guild
226
    discovery_splash: Optional[:class:`str`]
227
        Discovery splash hash;
228
        only present for guilds with the "DISCOVERABLE" feature
229
    emojis: List[:class:`~pincer.objects.message.emoji.Emoji`]
230
        Custom guild emojis
231
    explicit_content_filter: :class:`~pincer.objects.guild.guild.ExplicitContentFilterLevel`
232
        Explicit content filter level
233
    features: List[:class:`~pincer.objects.guild.features.GuildFeature`]
234
        Enabled guild features
235
    id: :class:`~pincer.utils.snowflake.Snowflake`
236
        Guild id
237
    icon: Optional[:class:`str`]
238
        Icon hash
239
    mfa_level: :class:`~pincer.objects.guild.guild.MFALevel`
240
        Required MFA level for the guild
241
    name: :class:`str`
242
        Guild name (2-100 characters, excluding trailing and leading
243
        whitespace)
244
    nsfw_level: :class:`~pincer.objects.guild.guild.NSFWLevel`
245
        Guild NSFW level
246
    owner_id: :class:`~pincer.utils.snowflake.Snowflake`
247
        Id of owner
248
    preferred_locale: :class:`str`
249
        The preferred locale of a Community guild;
250
        used in server discovery and notices from Discord;
251
        defaults to "en-US"
252
    premium_tier: :class:`~pincer.objects.guild.guild.PremiumTier`
253
        Premium tier (Server Boost level)
254
    public_updates_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
255
        The id of the channel where admins
256
        and moderators of Community guilds receive notices from Discord
257
    roles: List[:class:`~pincer.objects.guild.role.Role`]
258
        Roles in the guild
259
    rules_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
260
        The id of the channel where Community guilds can display rules
261
        and/or guidelines
262
    splash: Optional[:class:`str`]
263
        Splash hash
264
    system_channel_flags: :class:`~pincer.objects.guild.guild.SystemChannelFlags`
265
        System channel flags
266
    system_channel_id: Optional[:class:`~pincer.utils.snowflake.Snowflake`]
267
        The id of the channel where guild notices
268
        such as welcome messages and boost events are posted
269
    vanity_url_code: Optional[:class:`str`]
270
        The vanity url code for the guild
271
    verification_level: :class:`~pincer.objects.guild.guild.VerificationLevel`
272
        Verification level required for the guild
273
    approximate_member_count: APINullable[:class:`int`]
274
        Approximate number of members in this guild, returned from the
275
        `GET /guilds/<id>` endpoint when with_counts is true
276
    approximate_presence_count: APINullable[:class:`int`]
277
        Approximate number of non-offline members in this guild,
278
        returned from the `GET /guilds/<id>`
279
        endpoint when with_counts is true
280
    channels: APINullable[List[:class:`~pincer.objects.guild.channel.Channel`]]
281
        Channels in the guild
282
    icon_hash: APINullable[Optional[:class:`str`]]
283
        Icon hash, returned when in the template object
284
    joined_at: APINullable[:class:`~pincer.utils.timestamp.Timestamp`]
285
        When this guild was joined at
286
    large: APINullable[:class:`bool`]
287
        True if this is considered a large guild
288
    max_members: APINullable[:class:`int`]
289
        The maximum number of members for the guild
290
    max_presences: APINullable[Optional[:class:`int`]]
291
        The maximum number of presences for the guild
292
        (null is always returned, apart from the largest of guilds)
293
    max_video_channel_users: APINullable[:class:`int`]
294
        The maximum amount of users in a video channel
295
    members: APINullable[List[:class:`~pincer.objects.guild.member.GuildMember`]]
296
        Users in the guild
297
    member_count: APINullable[:class:`bool`]
298
        Total number of members in this guild
299
    nsfw: APINullable[:class:`bool`]
300
        Boolean if the server is NSFW
301
    owner: APINullable[:class:`bool`]
302
        True if the user is the owner of the guild
303
    permissions: APINullable[:class:`str`]
304
        Total permissions for the user in the guild
305
        (excludes overwrites)
306
    premium_subscription_count: APINullable[:class:`int`]
307
        The number of boosts this guild currently has
308
    presences: APINullable[List[:class:`~pincer.objects.events.presence.PresenceUpdateEvent`]]
309
        Presences of the members in the guild,
310
        will only include non-offline members if the size is greater
311
        than large threshold
312
    stage_instances: APINullable[List[:class:`~pincer.objects.guild.stage.StageInstance`]]
313
        Stage instances in the guild
314
    stickers: Optional[List[:class:`~pincer.objects.message.sticker.Sticker`]]
315
        Custom guild stickers
316
    region: APINullable[Optional[:class:`str`]]
317
        Voice region id for the guild (deprecated)
318
    threads: APINullable[List[:class:`~pincer.objects.guild.channel.Channel`]]
319
        All active threads in the guild that current user
320
        has permission to view
321
    unavailable: APINullable[:class:`bool`]
322
        True if this guild is unavailable due to an outage
323
    voice_states: APINullable[List[:class:`~pincer.objects.user.voice_state.VoiceState`]]
324
        States of members currently in voice channels;
325
        lacks the guild_id key
326
    widget_enabled: APINullable[:class:`bool`]
327
        True if the server widget is enabled
328
    widget_channel_id: APINullable[Optional[:class:`~pincer.utils.snowflake.Snowflake`]]
329
        The channel id that the widget will generate an invite to,
330
        or null if set to no invite
331
    welcome_screen: APINullable[:class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`]
332
        The welcome screen of a Community guild, shown to new members,
333
        returned in an Invite's guild object
334
    """
335
336
    # noqa: E501
337
    afk_timeout: int
338
    default_message_notifications: DefaultMessageNotificationLevel
339
    emojis: List[Emoji]
340
    explicit_content_filter: ExplicitContentFilterLevel
341
    features: List[GuildFeature]
342
    id: Snowflake
343
    mfa_level: MFALevel
344
    name: str
345
    nsfw_level: GuildNSFWLevel
346
    owner_id: Snowflake
347
    preferred_locale: str
348
    premium_tier: PremiumTier
349
    roles: List[Role]
350
    system_channel_flags: SystemChannelFlags
351
    verification_level: VerificationLevel
352
353
    guild_scheduled_events: APINullable[List] = MISSING
354
    lazy: APINullable[bool] = MISSING
355
    premium_progress_bar_enabled: APINullable[bool] = MISSING
356
    guild_hashes: APINullable[Dict] = MISSING
357
    afk_channel_id: APINullable[Snowflake] = MISSING
358
    application_id: APINullable[Snowflake] = MISSING
359
    embedded_activities: APINullable[List] = MISSING
360
    banner: APINullable[str] = MISSING
361
    description: APINullable[str] = MISSING
362
    discovery_splash: APINullable[str] = MISSING
363
    icon: APINullable[str] = MISSING
364
    public_updates_channel_id: APINullable[Snowflake] = MISSING
365
    rules_channel_id: APINullable[Snowflake] = MISSING
366
    splash: APINullable[str] = MISSING
367
    system_channel_id: APINullable[Snowflake] = MISSING
368
    vanity_url_code: APINullable[str] = MISSING
369
370
    application_command_counts: APINullable[Dict] = MISSING
371
    application_command_count: APINullable[int] = MISSING
372
    approximate_member_count: APINullable[int] = MISSING
373
    approximate_presence_count: APINullable[int] = MISSING
374
    channels: APINullable[List[Channel]] = field(default_factory=list)
375
    # TODO: Add type when type is known
376
    hub_type: APINullable[Any] = MISSING
377
    icon_hash: APINullable[Optional[str]] = MISSING
378
    joined_at: APINullable[Timestamp] = MISSING
379
    large: APINullable[bool] = MISSING
380
    max_members: APINullable[int] = MISSING
381
    max_presences: APINullable[Optional[int]] = MISSING
382
    max_video_channel_users: APINullable[int] = MISSING
383
    members: APINullable[List[GuildMember]] = MISSING
384
    member_count: APINullable[bool] = MISSING
385
    nsfw: APINullable[bool] = MISSING
386
    # Note: This is missing from discord's docs but in the api
387
    owner: APINullable[bool] = MISSING
388
    permissions: APINullable[str] = MISSING
389
    premium_subscription_count: APINullable[int] = MISSING
390
    presences: APINullable[List[PresenceUpdateEvent]] = MISSING
391
    stage_instances: APINullable[List[StageInstance]] = MISSING
392
    stickers: APINullable[List[Sticker]] = MISSING
393
    region: APINullable[Optional[str]] = MISSING
394
    threads: APINullable[List[Channel]] = MISSING
395
    # Guilds are considered available unless otherwise specified
396
    unavailable: APINullable[bool] = False
397
    voice_states: APINullable[List[VoiceState]] = MISSING
398
    widget_enabled: APINullable[bool] = MISSING
399
    widget_channel_id: APINullable[Optional[Snowflake]] = MISSING
400
    welcome_screen: APINullable[WelcomeScreen] = MISSING
401
402
    @classmethod
403
    async def from_id(cls, client: Client, _id: Union[int, Snowflake]) -> Guild:
404
        """
405
        Parameters
406
        ----------
407
        client : `~pincer.Client`
408
            Client object to use the http gateway from.
409
        _id : :class: `pincer.utils.snowflake.Snowflake`
410
            Guild ID.
411
        Returns
412
        -------
413
        :class: `~pincer.objects.guild.guild.Guild`
414
            The new guild object.
415
        """
416
        data = await client.http.get(f"/guilds/{_id}")
417
        channel_data = await client.http.get(f"/guilds/{_id}/channels")
418
419
        data["channels"]: List[Channel] = [
420
            Channel.from_dict({**i, "_client": client, "_http": client.http})
421
            for i in (channel_data or [])
422
        ]
423
424
        return Guild.from_dict(construct_client_dict(client, data))
425
426
    async def get_member(self, _id: int) -> GuildMember:
427
        """|coro|
428
        Fetches a GuildMember from its identifier
429
        Parameters
430
        ----------
431
        _id:
432
            The id of the guild member which should be fetched from the Discord
433
            gateway.
434
        Returns
435
        -------
436
        :class:`~pincer.objects.guild.member.GuildMember`
437
            A GuildMember object.
438
        """
439
        return await GuildMember.from_id(self._client, self.id, _id)
440
441
    @overload
442
    async def modify_member(
443
        self,
444
        *,
445
        _id: int,
446
        nick: Optional[str] = None,
447
        roles: Optional[List[Snowflake]] = None,
448
        mute: Optional[bool] = None,
449
        deaf: Optional[bool] = None,
450
        channel_id: Optional[Snowflake] = None,
451
    ) -> GuildMember:
452
        """|coro|
453
        Modifies a member in the guild from its identifier and based on the
454
        keyword arguments provided.
455
        Parameters
456
        ----------
457
        _id : int
458
            Id of the member to modify
459
        nick : Optional[:class:`str`]
460
            New nickname for the member |default| :data:`None`
461
        roles : Optional[List[:class:`~pincer.utils.snowflake.Snowflake]]
462
            New roles for the member |default| :data:`None`
463
        mute : Optional[:class:`bool`]
464
            Whether the member is muted |default| :data:`None`
465
        deaf : Optional[:class:`bool`]
466
            Whether the member is deafened |default| :data:`None`
467
        channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake]
468
            Voice channel id to move to |default| :data:`None`
469
        Returns
470
        -------
471
        :class:`~pincer.objects.guild.member.GuildMember`
472
            The new member object.
473
        """
474
        ...
475
476
    async def modify_member(self, _id: int, **kwargs) -> GuildMember:
477
        data = await self._http.patch(
478
            f"guilds/{self.id}/members/{_id}", data=kwargs
479
        )
480
        return GuildMember.from_dict(construct_client_dict(self._client, data))
481
482
    async def ban(
483
        self,
484
        member_id: int,
485
        reason: str = None,
486
        delete_message_days: int = None
487
    ):
488
        """
489
        Parameters
490
        ----------
491
        member_id : :class:`int`
492
            ID of the guild member to ban.
493
        reason : Optional[:class:`str`]
494
            Reason for the kick.
495
        delete_message_days : Optional[:class:`int`]
496
            Number of days to delete messages for (0-7)
497
        """
498
        headers = {}
499
500
        if reason is not None:
501
            headers["X-Audit-Log-Reason"] = reason
502
503
        data = {}
504
505
        if delete_message_days is not None:
506
            data["delete_message_days"] = delete_message_days
507
508
        await self._http.put(
509
            f"/guilds/{self.id}/bans/{member_id}",
510
            data=data,
511
            headers=headers
512
        )
513
514
    async def kick(self, member_id: int, reason: Optional[str] = None):
515
        """|coro|
516
        Kicks a guild member.
517
        Parameters
518
        ----------
519
        member_id : :class:`int`
520
            ID of the guild member to kick.
521
        reason : Optional[:class:`str`]
522
            Reason for the kick.
523
        """
524
525
        headers = {}
526
527
        if reason is not None:
528
            headers["X-Audit-Log-Reason"] = reason
529
530
        await self._http.delete(
531
            f"/guilds/{self.id}/members/{member_id}",
532
            header=headers
533
        )
534
535
    async def get_roles(self) -> AsyncGenerator[Role, None]:
536
        """|coro|
537
        Fetches all the roles in the guild.
538
539
        Returns
540
        -------
541
        AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`]
542
            An async generator of Role objects.
543
        """
544
        data = await self._http.get(f"guilds/{self.id}/roles")
545
        for role_data in data:
546
            yield Role.from_dict(construct_client_dict(self._client, role_data))
547
548
    @overload
549
    async def create_role(
550
        self,
551
        reason: Optional[str] = None,
552
        *,
553
        name: Optional[str] = "new role",
554
        permissions: Optional[str] = None,
555
        color: Optional[int] = 0,
556
        hoist: Optional[bool] = False,
557
        icon: Optional[str] = None,
558
        unicode_emoji: Optional[str] = None,
559
        mentionable: Optional[bool] = False,
560
    ) -> Role:
561
        """|coro|
562
        Creates a new role for the guild.
563
        Requires the ``MANAGE_ROLES`` permission.
564
565
        Parameters
566
        ----------
567
        reason : Optional[:class:`str`]
568
            Reason for creating the role. |default| :data:`None`
569
        name : Optional[:class:`str`]
570
            name of the role |default| :data:`"new role"`
571
        permissions : Optional[:class:`str`]
572
            bitwise value of the enabled/disabled
573
            permissions, set to @everyone permissions
574
            by default |default| :data:`None`
575
        color : Optional[:class:`int`]
576
            RGB color value |default| :data:`0`
577
        hoist : Optional[:class:`bool`]
578
            whether the role should be displayed
579
            separately in the sidebar |default| :data:`False`
580
        icon : Optional[:class:`str`]
581
            the role's icon image (if the guild has
582
            the ``ROLE_ICONS`` feature) |default| :data:`None`
583
        unicode_emoji : Optional[:class:`str`]
584
            the role's unicode emoji as a standard emoji (if the guild
585
            has the ``ROLE_ICONS`` feature) |default| :data:`None`
586
        mentionable : Optional[:class:`bool`]
587
            whether the role should be mentionable |default| :data:`False`
588
589
        Returns
590
        -------
591
        :class:`~pincer.objects.guild.role.Role`
592
            The new role object.
593
        """
594
        ...
595
596
    async def create_role(
597
        self,
598
        reason: Optional[str] = None,
599
        **kwargs
600
    ) -> Role:
601
        return Role.from_dict(
602
            construct_client_dict(
603
                self._client,
604
                await self._http.post(
605
                    f"guilds/{self.id}/roles",
606
                    data=kwargs,
607
                    headers={"X-Audit-Log-Reason": reason}
608
                    if reason is not None
609
                    else {},
610
                ),
611
            )
612
        )
613
614
    async def edit_role_position(
615
        self,
616
        id: Snowflake,
617
        reason: Optional[str] = None,
618
        position: Optional[int] = None
619
    ) -> AsyncGenerator[Role, None]:
620
        """|coro|
621
        Edits the position of a role.
622
623
        Parameters
624
        ----------
625
        id : :class:`~pincer.utils.snowflake.Snowflake`
626
            The role ID
627
        reason : Optional[:class:`str`]
628
            Reason for editing the role position. |default| :data:`None`
629
        position : Optional[:class:`int`]
630
            Sorting position of the role |default| :data:`None`
631
632
        Returns
633
        -------
634
        AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`]
635
            An async generator of all of the guild's role objects.
636
        """
637
        data = await self._http.patch(
638
            f"guilds/{self.id}/roles",
639
            data={"id": id, "position": position},
640
            headers={"X-Audit-Log-Reason": reason}
641
            if reason is not None
642
            else {}
643
        )
644
        for role_data in data:
645
            yield Role.from_dict(construct_client_dict(self._client, role_data))
646
647
    @overload
648
    async def edit_role(
649
        self,
650
        id: Snowflake,
651
        reason: Optional[str] = None,
652
        *,
653
        name: Optional[str] = None,
654
        permissions: Optional[str] = None,
655
        color: Optional[int] = None,
656
        hoist: Optional[bool] = None,
657
        icon: Optional[str] = None,
658
        unicode_emoji: Optional[str] = None,
659
        mentionable: Optional[bool] = None,
660
    ) -> Role:
661
        """|coro|
662
        Edits a role.
663
        Requires the ``MANAGE_ROLES`` permission.
664
665
        Parameters
666
        ----------
667
        id : :class:`~pincer.utils.snowflake.Snowflake`
668
            The role ID
669
        reason : Optional[:class:`str`]
670
            Reason for editing the role |default| :data:`None`
671
        name : Optional[:class:`str`]
672
            Name of the role |default| :data:`None`
673
        permissions : Optional[:class:`str`]
674
            Bitwise value of the enabled/disabled
675
            permissions |default| :data:`None`
676
        color : Optional[:class:`int`]
677
            RGB color value |default| :data:`None`
678
        hoist : Optional[:class:`bool`]
679
            Whether the role should be displayed
680
            separately in the sidebar |default| :data:`None`
681
        icon : Optional[:class:`str`]
682
            The role's icon image (if the guild has
683
            the ``ROLE_ICONS`` feature) |default| :data:`None`
684
        unicode_emoji : Optional[:class:`str`]
685
            The role's unicode emoji as a standard emoji (if the guild
686
            has the ``ROLE_ICONS`` feature) |default| :data:`None`
687
        mentionable : Optional[:class:`bool`]
688
            Whether the role should be mentionable |default| :data:`None`
689
690
        Returns
691
        -------
692
        :class:`~pincer.objects.guild.role.Role`
693
            The edited role object.
694
        """
695
        ...
696
697
    async def edit_role(
698
        self,
699
        id: Snowflake,
700
        reason: Optional[str] = None,
701
        **kwargs
702
    ) -> Role:
703
        return Role.from_dict(
704
            construct_client_dict(
705
                self._client,
706
                await self._http.patch(
707
                    f"guilds/{self.id}/roles/{id}",
708
                    data=kwargs,
709
                    headers={"X-Audit-Log-Reason": reason}
710
                    if reason is not None
711
                    else {},
712
                ),
713
            )
714
        )
715
716
    async def delete_role(self, id: Snowflake, reason: Optional[str] = None):
717
        """|coro|
718
        Deletes a role.
719
        Requires the `MANAGE_ROLES` permission.
720
721
        Parameters
722
        ----------
723
        id : :class:`~pincer.utils.snowflake.Snowflake`
724
            The role ID
725
        reason : Optional[:class:`str`]
726
            The reason for deleting the role |default| :data:`None`
727
        """
728
        await self._http.delete(
729
            f"guilds/{self.id}/roles/{id}",
730
            headers={"X-Audit-Log-Reason": reason}
731
            if reason is not None
732
            else {},
733
        )
734
735
    async def get_bans(self) -> AsyncGenerator[Ban, None]:
736
        """|coro|
737
        Fetches all the bans in the guild.
738
739
        Returns
740
        -------
741
        AsyncGenerator[:class:`~pincer.objects.guild.ban.Ban`, :data:`None`]
742
            An async generator of Ban objects.
743
        """
744
        data = await self._http.get(f"guilds/{self.id}/bans")
745
        for ban_data in data:
746
            yield Ban.from_dict(construct_client_dict(self._client, ban_data))
747
748
    async def get_ban(self, id: Snowflake) -> Ban:
749
        """|coro|
750
        Fetches a ban from the guild.
751
752
        Parameters
753
        ----------
754
        id : :class:`~pincer.utils.snowflake.Snowflake`
755
            The user ID
756
757
        Returns
758
        -------
759
        :class:`~pincer.objects.guild.ban.Ban`
760
            The Ban object.
761
        """
762
        return Ban.from_dict(
763
            construct_client_dict(
764
                self._client,
765
                await self._http.get(f"guilds/{self.id}/bans/{id}"),
766
            )
767
        )
768
769
    async def unban(self, id: Snowflake, reason: Optional[str] = None):
770
        """|coro|
771
        Unbans a user from the guild.
772
773
        Parameters
774
        ----------
775
        id : :class:`~pincer.utils.snowflake.Snowflake`
776
            The user ID
777
        reason : Optional[:class:`str`]
778
            The reason for unbanning the user |default| :data:`None`
779
        """
780
        await self._http.delete(
781
            f"guilds/{self.id}/bans/{id}",
782
            headers={"X-Audit-Log-Reason": reason}
783
            if reason is not None
784
            else {}
785
        )
786
787
    @overload
788
    async def edit(
789
        self,
790
        *,
791
        name: Optional[str] = None,
792
        region: Optional[str] = None,
793
        verification_level: Optional[int] = None,
794
        default_message_notifications: Optional[int] = None,
795
        explicit_content_filter: Optional[int] = None,
796
        afk_channel_id: Optional[Snowflake] = None,
797
        afk_timeout: Optional[int] = None,
798
        icon: Optional[str] = None,
799
        owner_id: Optional[Snowflake] = None,
800
        splash: Optional[str] = None,
801
        discovery_splash: Optional[str] = None,
802
        banner: Optional[str] = None,
803
        system_channel_id: Optional[Snowflake] = None,
804
        system_channel_flags: Optional[int] = None,
805
        rules_channel_id: Optional[Snowflake] = None,
806
        public_updates_channel_id: Optional[Snowflake] = None,
807
        preferred_locale: Optional[str] = None,
808
        features: Optional[List[GuildFeature]] = None,
809
        description: Optional[str] = None,
810
    ) -> Guild:
811
        """|coro|
812
        Modifies the guild
813
814
        Parameters
815
        ----------
816
        name : Optional[:class:`str`]
817
            Guild name |default| :data:`None`
818
        region : Optional[:class:`str`]
819
            Guild voice region ID |default| :data:`None`
820
        verification_level : Optional[:class:`int`]
821
            Verification level |default| :data:`None`
822
        default_message_notifications : Optional[:class:`int`]
823
            Default message notification level |default| :data:`None`
824
        explicit_content_filter : Optional[:class:`int`]
825
            Explicit content filter level |default| :data:`None`
826
        afk_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
827
            ID for AFK channel |default| :data:`None`
828
        afk_timeout : Optional[:class:`int`]
829
            AFK timeout in seconds |default| :data:`None`
830
        icon : Optional[:class:`str`]
831
            base64 1024x1024 png/jpeg/gif image for the guild icon
832
            (can be animated gif when the server
833
            has the `ANIMATED_ICON` feature) |default| :data:`None`
834
        owner_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
835
            User ID to transfer guild ownership to (must be owner) |default| :data:`None`
836
        splash : Optional[:class:`str`]
837
            base64 16:9 png/jpeg image for the guild splash (when the
838
            server has the `INVITE_SPLASH` feature) |default| :data:`None`
839
        discovery_splash : Optional[:class:`str`]
840
            base64 16:9 png/jpeg image for the guild discovery splash
841
            (when the server has the `DISCOVERABLE` feature) |default| :data:`None`
842
        banner : Optional[:class:`str`]
843
            base64 16:9 png/jpeg image for the guild banner (when the
844
            server has the `BANNER` feature) |default| :data:`None`
845
        system_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
846
            The ID of the channel where guild notices such as welcome
847
            messages and boost events are posted |default| :data:`None`
848
        system_channel_flags : Optional[:class:`int`]
849
            System channel flags |default| :data:`None`
850
        rules_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
851
            The ID of the channel where Community guilds display rules
852
            and/or guidelines |default| :data:`None`
853
        public_updates_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
854
            The ID of the channel where admins and moderators of
855
            Community guilds receive notices from Discord |default| :data:`None`
856
        preferred_locale : Optional[:class:`str`]
857
            The preferred locale of a Community guild used in server
858
            discovery and notices from Discord; defaults to "en-US" |default| :data:`None`
859
        features : Optional[List[:class:`GuildFeature`]]
860
            Enabled guild features |default| :data:`None`
861
        description : Optional[:class:`str`]
862
            The description for the guild, if the guild is discoverable |default| :data:`None`
863
864
        Returns
865
        -------
866
        :class:`~pincer.objects.guild.Guild`
867
            The modified guild object.
868
        """
869
        ...
870
871
    async def edit(self, **kwargs) -> Guild:
872
        g = await self._http.patch(f"guilds/{self.id}", data=kwargs)
873
        return Guild.from_dict(construct_client_dict(self._client, g))
874
875
    async def preview(self) -> GuildPreview:
876
        """|coro|
877
        Previews the guild.
878
879
        Returns
880
        -------
881
        :class:`~pincer.objects.guild.guild.GuildPreview`
882
            The guild preview object.
883
        """
884
        data = await self._http.get(f"guilds/{self.id}/preview")
885
        return GuildPreview.from_dict(data)
886
887
    async def delete(self):
888
        """|coro|
889
        Deletes the guild. Returns `204 No Content` on success.
890
        """
891
        await self._http.delete(f"guilds/{self.id}")
892
893
    async def prune_count(
894
        self,
895
        days: Optional[int] = 7,
896
        include_roles: Optional[str] = None
897
    ) -> Dict[str, int]:
898
        """|coro|
899
        Returns the number of members that
900
        would be removed in a prune operation.
901
        Requires the ``KICK_MEMBERS`` permission.
902
903
        Parameters
904
        ----------
905
        days : Optional[:class:`int`]
906
            Number of days to count prune for (1-30) |default| :data:`7`
907
        include_roles : Optional[:class:`str`]
908
            Comma-delimited array of Snowflakes;
909
            role(s) to include |default| :data:`None`
910
911
        Returns
912
        -------
913
        Dict[:class:`str`, :class:`int`]
914
            An object with one 'pruned' key indicating
915
            the number of members that would be removed.
916
        """
917
        return await self._http.get(
918
            f"guilds/{self.id}/prune?{days=}&{include_roles=!s}"
919
        )
920
921
    async def prune(
922
        self,
923
        days: Optional[int] = 7,
924
        compute_prune_days: Optional[bool] = True,
925
        include_roles: Optional[List[Snowflake]] = None,
926
        reason: Optional[str] = None
927
    ) -> Dict[str, int]:
928
        """|coro|
929
        Prunes members from the guild. Requires the ``KICK_MEMBERS`` permission.
930
931
        Parameters
932
933
        Parameters
934
        ----------
935
        days : Optional[:class:`int`]
936
            Number of days to prune (1-30) |default| :data:`7`
937
        compute_prune_days : Optional[:class:`bool`]
938
            Whether 'pruned' is returned, discouraged for large guilds
939
            |default| :data:`True`
940
        include_roles : Optional[List[:class:`~pincer.utils.snowflake.Snowflake`]]
941
            role(s) to include |default| :data:`None`
942
        reason : Optional[:class:`str`]
943
            Reason for the prune |default| :data:`None`
944
945
        Returns
946
        -------
947
        Dict[:class:`str`, :class:`int`]
948
            An object with one 'pruned' key indicating
949
            the number of members that were removed.
950
        """
951
        return await self._http.post(
952
            f"guilds/{self.id}/prune",
953
            data={
954
                "days": days,
955
                "compute_prune_days": compute_prune_days,
956
                "include_roles": include_roles
957
            },
958
            headers={"X-Audit-Log-Reason": reason}
959
            if reason is not None
960
            else {}
961
        )
962
963
    async def get_voice_regions(self) -> AsyncGenerator[VoiceRegion, None]:
964
        """|coro|
965
        Returns an async generator of voice regions.
966
967
        Returns
968
        -------
969
        AsyncGenerator[:class:`~pincer.objects.voice.VoiceRegion`, :data:`None`]
970
            An async generator of voice regions.
971
        """
972
        data = await self._http.get(f"guilds/{self.id}/regions")
973
        for voice_region_data in data:
974
            yield VoiceRegion.from_dict(construct_client_dict(self._client, voice_region_data))
975
976
    async def get_invites(self) -> AsyncGenerator[Invite, None]:
977
        """|coro|
978
        Returns an async generator of invites for the guild.
979
        Requires the ``MANAGE_GUILD`` permission.
980
981
        Returns
982
        -------
983
        AsyncGenerator[:class:`~pincer.objects.invite.Invite`, :data:`None`]
984
            An async generator of invites.
985
        """
986
        data = await self._http.get(f"guilds/{self.id}/invites")
987
        for invite_data in data:
988
            yield Invite.from_dict(construct_client_dict(self._client, invite_data))
989
990
    async def get_integrations(self) -> AsyncGenerator[Integration, None]:
991
        """|coro|
992
        Returns an async generator of integrations for the guild.
993
        Requires the ``MANAGE_GUILD`` permission.
994
995
        Returns
996
        -------
997
        AsyncGenerator[:class:`~pincer.objects.integration.Integration`, :data:`None`]
998
            An async generator of integrations.
999
        """
1000
        data = await self._http.get(f"guilds/{self.id}/integrations")
1001
        for integration_data in data:
1002
            yield Integration.from_dict(construct_client_dict(self._client, integration_data))
1003
1004
    async def delete_integration(
1005
        self,
1006
        integration: Integration,
1007
        reason: Optional[str] = None
1008
    ):
1009
        """|coro|
1010
        Deletes an integration.
1011
        Requires the ``MANAGE_GUILD`` permission.
1012
1013
        Parameters
1014
        ----------
1015
        integration : :class:`~pincer.objects.integration.Integration`
1016
            The integration to delete.
1017
        reason : Optional[:class:`str`]
1018
            Reason for the deletion |default| :data:`None`
1019
        """
1020
        await self._http.delete(
1021
            f"guilds/{self.id}/integrations/{integration.id}",
1022
            headers={"X-Audit-Log-Reason": reason}
1023
            if reason is not None
1024
            else {}
1025
        )
1026
1027
    async def get_widget_settings(self) -> GuildWidget:
1028
        """|coro|
1029
        Returns the guild widget settings.
1030
        Requires the ``MANAGE_GUILD`` permission.
1031
1032
        Returns
1033
        -------
1034
        :class:`~pincer.objects.guild.widget.GuildWidget`
1035
            The guild widget settings.
1036
        """
1037
        data = await self._http.get(f"guilds/{self.id}/widget")
1038
        return GuildWidget.from_dict(construct_client_dict(self._client, data))
1039
1040
    async def modify_widget(
1041
        self,
1042
        reason: Optional[str] = None,
1043
        **kwargs
1044
    ) -> GuildWidget:
1045
        """|coro|
1046
        Modifies the guild widget for the guild.
1047
        Requires the ``MANAGE_GUILD`` permission.
1048
1049
        Parameters
1050
        ----------
1051
        reason : Optional[:class:`str`]
1052
            Reason for the modification |default| :data:`None`
1053
        \\*\\*kwargs
1054
            The widget settings to modify
1055
1056
        Returns
1057
        -------
1058
        :class:`~pincer.objects.guild.widget.GuildWidget`
1059
            The updated GuildWidget object
1060
        """
1061
        data = await self._http.patch(
1062
            f"guilds/{self.id}/widget",
1063
            data=kwargs,
1064
            headers={"X-Audit-Log-Reason": reason}
1065
            if reason is not None
1066
            else {}
1067
        )
1068
        return GuildWidget.from_dict(construct_client_dict(self._client, data))
1069
1070
    async def get_widget(self) -> Dict[str, JSONSerializable]:
1071
        """|coro|
1072
        Returns the widget for the guild
1073
        """
1074
        return await self._http.get(f"guilds/{self.id}/widget.json")
1075
1076
    async def get_vanity_url(self) -> Invite:
1077
        """|coro|
1078
        Returns the Vanity URL for the guild.
1079
        Requires the ``MANAGE_GUILD`` permission.
1080
        ``code`` will be null if a vanity URL has not been set.
1081
1082
        Returns
1083
        -------
1084
        :class:`~pincer.objects.guild.invite.Invite`
1085
            The vanity url for the guild.
1086
        """
1087
        data = await self._http.get(f"guilds/{self.id}/vanity-url")
1088
        return Invite.from_dict(construct_client_dict(self._client, data))
1089
1090
    async def get_widget_image(self, style: Optional[str] = "shield") -> str:  # TODO Replace str with ImageURL object
1091
        """|coro|
1092
        Returns a PNG image widget for the guild.
1093
        Requires no permissions or authentication.
1094
1095
        Widget Style Options
1096
        -------------------
1097
        * [``shield``](https://discord.com/api/guilds/81384788765712384/widget.png?style=shield)
1098
          shield style widget with Discord icon and guild members online count
1099
        * [``banner1``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner1)
1100
          large image with guild icon, name and online count.
1101
          "POWERED BY DISCORD" as the footer of the widget
1102
        * [``banner2``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner2)
1103
          smaller widget style with guild icon, name and online count.
1104
          Split on the right with Discord logo
1105
        * [``banner3``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner3)
1106
          large image with guild icon, name and online count.
1107
          In the footer, Discord logo on the
1108
          left and "Chat Now" on the right
1109
        * [``banner4``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4)
1110
          large Discord logo at the top of the widget.
1111
          Guild icon, name and online count in the middle portion
1112
          of the widget and a "JOIN MY SERVER" button at the bottom
1113
1114
        Parameters
1115
        ----------
1116
        style : Optional[:class:`str`]
1117
            Style of the widget image returned |default| :data:`"shield"`
1118
1119
        Returns
1120
        -------
1121
        :class:`str`
1122
            A PNG image of the guild widget.
1123
        """
1124
        return await self._http.get(f"guilds/{self.id}/widget.png?{style=!s}")
1125
1126
    async def get_welcome_screen(self) -> WelcomeScreen:
1127
        """Returns the welcome screen for the guild.
1128
1129
        Returns
1130
        -------
1131
        :class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`
1132
            The welcome screen for the guild.
1133
        """
1134
        data = await self._http.get(f"guilds/{self.id}/welcome-screen")
1135
        return WelcomeScreen.from_dict(construct_client_dict(self._client, data))
1136
1137
    async def modify_welcome_screen(
1138
        self,
1139
        enabled: Optional[bool] = None,
1140
        welcome_channels: Optional[List[WelcomeScreenChannel]] = None,
1141
        description: Optional[str] = None,
1142
        reason: Optional[str] = None
1143
    ) -> WelcomeScreen:
1144
        """|coro|
1145
        Modifies the guild's Welcome Screen.
1146
        Requires the ``MANAGE_GUILD`` permission.
1147
1148
        Parameters
1149
        ----------
1150
        enabled : Optional[:class:`bool`]
1151
            Whether the welcome screen is enabled |default| :data:`None`
1152
        welcome_channels : Optional[List[:class:`~pincer.objects.guild.welcome_screen.WelcomeScreenChannel`]]
1153
            Channels linked in the welcome screen and
1154
            their display options |default| :data:`None`
1155
        description : Optional[:class:`str`]
1156
            The server description to show
1157
            in the welcome screen |default| :data:`None`
1158
        reason : Optional[:class:`str`]
1159
            Reason for the modification |default| :data:`None`
1160
1161
        Returns
1162
        -------
1163
        :class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`
1164
            The updated WelcomeScreen object
1165
        """
1166
        data = await self._http.patch(
1167
            f"guilds/{self.id}/welcome-screen",
1168
            data={
1169
                "enabled": enabled,
1170
                "welcome_channels": welcome_channels,
1171
                "description": description
1172
            },
1173
            headers={"X-Audit-Log-Reason": reason}
1174
            if reason is not None
1175
            else {}
1176
        )
1177
        return WelcomeScreen.from_dict(construct_client_dict(self._client, data))
1178
1179
    async def modify_curent_user_voice_state(
1180
        self,
1181
        channel_id: Snowflake,
1182
        suppress: Optional[bool] = None,
1183
        request_to_speak_timestamp: Optional[Timestamp] = None
1184
    ):
1185
        """|coro|
1186
        Updates the current user's voice state.
1187
1188
        There are currently several caveats for this endpoint:
1189
        * ``channel_id`` must currently point to a stage channel
1190
        * current user must already have joined ``channel_id``
1191
        * You must have the ``MUTE_MEMBERS`` permission to
1192
          unsuppress yourself. You can always suppress yourself.
1193
        * You must have the ``REQUEST_TO_SPEAK`` permission to request
1194
          to speak. You can always clear your own request to speak.
1195
        * You are able to set ``request_to_speak_timestamp`` to any
1196
          present or future time.
1197
1198
        Parameters
1199
        ----------
1200
        channel_id : :class:`~pincer.utils.snowflake.Snowflake`
1201
            The ID of the channel the user is currently in
1202
        suppress : Optional[:class:`bool`]
1203
            Toggles the user's suppress state |default| :data:`None`
1204
        request_to_speak_timestamp : Optional[:class:`~pincer.utils.timestamp.Timestamp`]
1205
            Sets the user's request to speak
1206
        """
1207
        await self._http.patch(
1208
            f"guilds/{self.id}/voice-states/@me",
1209
            data={
1210
                "channel_id": channel_id,
1211
                "suppress": suppress,
1212
                "request_to_speak_timestamp": request_to_speak_timestamp
1213
            }
1214
        )
1215
1216
    async def modify_user_voice_state(
1217
        self,
1218
        user: User,
1219
        channel_id: Snowflake,
1220
        suppress: Optional[bool] = None
1221
    ):
1222
        """|coro|
1223
        Updates another user's voice state.
1224
1225
        There are currently several caveats for this endpoint:
1226
        * ``channel_id`` must currently point to a stage channel
1227
        * User must already have joined ``channel_id``
1228
        * You must have the ``MUTE_MEMBERS`` permission.
1229
          (Since suppression is the only thing that is available currently.)
1230
        * When unsuppressed, non-bot users will have their
1231
          ``request_to_speak_timestamp`` set to the current time.
1232
          Bot users will not.
1233
        * When suppressed, the user will have their
1234
          ``request_to_speak_timestamp`` removed.
1235
1236
        Parameters
1237
        ----------
1238
        user : :class:`~pincer.objects.guild.member.Member`
1239
            The user to update
1240
        channel_id : :class:`~pincer.utils.snowflake.Snowflake`
1241
            The ID of the channel the user is currently in
1242
        suppress : Optional[:class:`bool`]
1243
            Toggles the user's suppress state |default| :data:`None`
1244
        """
1245
        await self._http.patch(
1246
            f"guilds/{self.id}/voice-states/{user.id}",
1247
            data={
1248
                "channel_id": channel_id,
1249
                "suppress": suppress
1250
            }
1251
        )
1252
1253
    @classmethod
1254
    def from_dict(cls, data) -> Guild:
1255
        """
1256
        Parameters
1257
        ----------
1258
        data : :class:`Dict`
1259
            Guild data received from the discord API.
1260
        Returns
1261
        -------
1262
        :class:`~pincer.objects.guild.guild.Guild`
1263
            The new guild object.
1264
        Raises
1265
        :class:`~pincer.exceptions.UnavailableGuildError`
1266
            The guild is unavailable due to a discord outage.
1267
        """
1268
        if data.get("unavailable", False):
1269
            raise UnavailableGuildError(
1270
                f"Guild \"{data['id']}\" is unavailable due to a discord"
1271
                " outage."
1272
            )
1273
1274
        return super().from_dict(data)
1275
1276
1277
@dataclass
1278
class UnavailableGuild(APIObject):
1279
    id: Snowflake
1280
    unavailable: bool = True
1281