Passed
Pull Request — main (#248)
by Yohann
01:54
created

Guild.get_integrations()   A

Complexity

Conditions 2

Size

Total Lines 13
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 2
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, remove_none
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
430
        Parameters
431
        ----------
432
        _id: int
433
            The id of the guild member which should be fetched from the Discord
434
            gateway.
435
        Returns
436
        -------
437
        :class:`~pincer.objects.guild.member.GuildMember`
438
            A GuildMember object.
439
        """
440
        return await GuildMember.from_id(self._client, self.id, _id)
441
442
    @overload
443
    async def modify_member(
444
        self,
445
        *,
446
        _id: int,
447
        nick: Optional[str] = None,
448
        roles: Optional[List[Snowflake]] = None,
449
        mute: Optional[bool] = None,
450
        deaf: Optional[bool] = None,
451
        channel_id: Optional[Snowflake] = None,
452
    ) -> GuildMember:
453
        """|coro|
454
        Modifies a member in the guild from its identifier and based on the
455
        keyword arguments provided.
456
        Parameters
457
        ----------
458
        _id : int
459
            Id of the member to modify
460
        nick : Optional[:class:`str`]
461
            New nickname for the member |default| :data:`None`
462
        roles : Optional[List[:class:`~pincer.utils.snowflake.Snowflake]]
463
            New roles for the member |default| :data:`None`
464
        mute : Optional[:class:`bool`]
465
            Whether the member is muted |default| :data:`None`
466
        deaf : Optional[:class:`bool`]
467
            Whether the member is deafened |default| :data:`None`
468
        channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake]
469
            Voice channel id to move to |default| :data:`None`
470
        Returns
471
        -------
472
        :class:`~pincer.objects.guild.member.GuildMember`
473
            The new member object.
474
        """
475
        ...
476
477
    async def modify_member(self, _id: int, **kwargs) -> GuildMember:
478
        data = await self._http.patch(
479
            f"guilds/{self.id}/members/{_id}", data=kwargs
480
        )
481
        return GuildMember.from_dict(construct_client_dict(self._client, data))
482
483
    async def ban(
484
        self,
485
        member_id: int,
486
        reason: str = None,
487
        delete_message_days: int = None
488
    ):
489
        """
490
        Parameters
491
        ----------
492
        member_id : :class:`int`
493
            ID of the guild member to ban.
494
        reason : Optional[:class:`str`]
495
            Reason for the kick.
496
        delete_message_days : Optional[:class:`int`]
497
            Number of days to delete messages for (0-7)
498
        """
499
        headers = {}
500
501
        if reason is not None:
502
            headers["X-Audit-Log-Reason"] = reason
503
504
        data = {}
505
506
        if delete_message_days is not None:
507
            data["delete_message_days"] = delete_message_days
508
509
        await self._http.put(
510
            f"/guilds/{self.id}/bans/{member_id}",
511
            data=data,
512
            headers=headers
513
        )
514
515
    async def kick(self, member_id: int, reason: Optional[str] = None):
516
        """|coro|
517
        Kicks a guild member.
518
        Parameters
519
        ----------
520
        member_id : :class:`int`
521
            ID of the guild member to kick.
522
        reason : Optional[:class:`str`]
523
            Reason for the kick.
524
        """
525
526
        headers = {}
527
528
        if reason is not None:
529
            headers["X-Audit-Log-Reason"] = reason
530
531
        await self._http.delete(
532
            f"/guilds/{self.id}/members/{member_id}",
533
            header=headers
534
        )
535
536
    async def get_roles(self) -> AsyncGenerator[Role, None]:
537
        """|coro|
538
        Fetches all the roles in the guild.
539
540
        Returns
541
        -------
542
        AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`]
543
            An async generator of Role objects.
544
        """
545
        data = await self._http.get(f"guilds/{self.id}/roles")
546
        for role_data in data:
547
            yield Role.from_dict(construct_client_dict(self._client, role_data))
548
549
    @overload
550
    async def create_role(
551
        self,
552
        reason: Optional[str] = None,
553
        *,
554
        name: Optional[str] = "new role",
555
        permissions: Optional[str] = None,
556
        color: Optional[int] = 0,
557
        hoist: Optional[bool] = False,
558
        icon: Optional[str] = None,
559
        unicode_emoji: Optional[str] = None,
560
        mentionable: Optional[bool] = False,
561
    ) -> Role:
562
        """|coro|
563
        Creates a new role for the guild.
564
        Requires the ``MANAGE_ROLES`` permission.
565
566
        Parameters
567
        ----------
568
        reason : Optional[:class:`str`]
569
            Reason for creating the role. |default| :data:`None`
570
        name : Optional[:class:`str`]
571
            name of the role |default| :data:`"new role"`
572
        permissions : Optional[:class:`str`]
573
            bitwise value of the enabled/disabled
574
            permissions, set to @everyone permissions
575
            by default |default| :data:`None`
576
        color : Optional[:class:`int`]
577
            RGB color value |default| :data:`0`
578
        hoist : Optional[:class:`bool`]
579
            whether the role should be displayed
580
            separately in the sidebar |default| :data:`False`
581
        icon : Optional[:class:`str`]
582
            the role's icon image (if the guild has
583
            the ``ROLE_ICONS`` feature) |default| :data:`None`
584
        unicode_emoji : Optional[:class:`str`]
585
            the role's unicode emoji as a standard emoji (if the guild
586
            has the ``ROLE_ICONS`` feature) |default| :data:`None`
587
        mentionable : Optional[:class:`bool`]
588
            whether the role should be mentionable |default| :data:`False`
589
590
        Returns
591
        -------
592
        :class:`~pincer.objects.guild.role.Role`
593
            The new role object.
594
        """
595
        ...
596
597
    async def create_role(
598
        self,
599
        reason: Optional[str] = None,
600
        **kwargs
601
    ) -> Role:
602
        return Role.from_dict(
603
            construct_client_dict(
604
                self._client,
605
                await self._http.post(
606
                    f"guilds/{self.id}/roles",
607
                    data=kwargs,
608
                    headers=remove_none({"X-Audit-Log-Reason": reason})
609
                ),
610
            )
611
        )
612
613
    async def edit_role_position(
614
        self,
615
        id: Snowflake,
616
        reason: Optional[str] = None,
617
        position: Optional[int] = None
618
    ) -> AsyncGenerator[Role, None]:
619
        """|coro|
620
        Edits the position of a role.
621
622
        Parameters
623
        ----------
624
        id : :class:`~pincer.utils.snowflake.Snowflake`
625
            The role ID
626
        reason : Optional[:class:`str`]
627
            Reason for editing the role position. |default| :data:`None`
628
        position : Optional[:class:`int`]
629
            Sorting position of the role |default| :data:`None`
630
631
        Returns
632
        -------
633
        AsyncGenerator[:class:`~pincer.objects.guild.role.Role`, :data:`None`]
634
            An async generator of all of the guild's role objects.
635
        """
636
        data = await self._http.patch(
637
            f"guilds/{self.id}/roles",
638
            data={"id": id, "position": position},
639
            headers=remove_none({"X-Audit-Log-Reason": reason})
640
        )
641
        for role_data in data:
642
            yield Role.from_dict(construct_client_dict(self._client, role_data))
643
644
    @overload
645
    async def edit_role(
646
        self,
647
        id: Snowflake,
648
        reason: Optional[str] = None,
649
        *,
650
        name: Optional[str] = None,
651
        permissions: Optional[str] = None,
652
        color: Optional[int] = None,
653
        hoist: Optional[bool] = None,
654
        icon: Optional[str] = None,
655
        unicode_emoji: Optional[str] = None,
656
        mentionable: Optional[bool] = None,
657
    ) -> Role:
658
        """|coro|
659
        Edits a role.
660
        Requires the ``MANAGE_ROLES`` permission.
661
662
        Parameters
663
        ----------
664
        id : :class:`~pincer.utils.snowflake.Snowflake`
665
            The role ID
666
        reason : Optional[:class:`str`]
667
            Reason for editing the role |default| :data:`None`
668
        name : Optional[:class:`str`]
669
            Name of the role |default| :data:`None`
670
        permissions : Optional[:class:`str`]
671
            Bitwise value of the enabled/disabled
672
            permissions |default| :data:`None`
673
        color : Optional[:class:`int`]
674
            RGB color value |default| :data:`None`
675
        hoist : Optional[:class:`bool`]
676
            Whether the role should be displayed
677
            separately in the sidebar |default| :data:`None`
678
        icon : Optional[:class:`str`]
679
            The role's icon image (if the guild has
680
            the ``ROLE_ICONS`` feature) |default| :data:`None`
681
        unicode_emoji : Optional[:class:`str`]
682
            The role's unicode emoji as a standard emoji (if the guild
683
            has the ``ROLE_ICONS`` feature) |default| :data:`None`
684
        mentionable : Optional[:class:`bool`]
685
            Whether the role should be mentionable |default| :data:`None`
686
687
        Returns
688
        -------
689
        :class:`~pincer.objects.guild.role.Role`
690
            The edited role object.
691
        """
692
        ...
693
694
    async def edit_role(
695
        self,
696
        id: Snowflake,
697
        reason: Optional[str] = None,
698
        **kwargs
699
    ) -> Role:
700
        return Role.from_dict(
701
            construct_client_dict(
702
                self._client,
703
                await self._http.patch(
704
                    f"guilds/{self.id}/roles/{id}",
705
                    data=kwargs,
706
                    headers=remove_none({"X-Audit-Log-Reason": reason})
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=remove_none({"X-Audit-Log-Reason": reason})
726
        )
727
728
    async def get_bans(self) -> AsyncGenerator[Ban, None]:
729
        """|coro|
730
        Fetches all the bans in the guild.
731
732
        Returns
733
        -------
734
        AsyncGenerator[:class:`~pincer.objects.guild.ban.Ban`, :data:`None`]
735
            An async generator of Ban objects.
736
        """
737
        data = await self._http.get(f"guilds/{self.id}/bans")
738
        for ban_data in data:
739
            yield Ban.from_dict(construct_client_dict(self._client, ban_data))
740
741
    async def get_ban(self, id: Snowflake) -> Ban:
742
        """|coro|
743
        Fetches a ban from the guild.
744
745
        Parameters
746
        ----------
747
        id : :class:`~pincer.utils.snowflake.Snowflake`
748
            The user ID
749
750
        Returns
751
        -------
752
        :class:`~pincer.objects.guild.ban.Ban`
753
            The Ban object.
754
        """
755
        return Ban.from_dict(
756
            construct_client_dict(
757
                self._client,
758
                await self._http.get(f"guilds/{self.id}/bans/{id}"),
759
            )
760
        )
761
762
    async def unban(self, id: Snowflake, reason: Optional[str] = None):
763
        """|coro|
764
        Unbans a user from the guild.
765
766
        Parameters
767
        ----------
768
        id : :class:`~pincer.utils.snowflake.Snowflake`
769
            The user ID
770
        reason : Optional[:class:`str`]
771
            The reason for unbanning the user |default| :data:`None`
772
        """
773
        await self._http.delete(
774
            f"guilds/{self.id}/bans/{id}",
775
            headers=remove_none({"X-Audit-Log-Reason": reason})
776
        )
777
778
    @overload
779
    async def edit(
780
        self,
781
        *,
782
        name: Optional[str] = None,
783
        region: Optional[str] = None,
784
        verification_level: Optional[int] = None,
785
        default_message_notifications: Optional[int] = None,
786
        explicit_content_filter: Optional[int] = None,
787
        afk_channel_id: Optional[Snowflake] = None,
788
        afk_timeout: Optional[int] = None,
789
        icon: Optional[str] = None,
790
        owner_id: Optional[Snowflake] = None,
791
        splash: Optional[str] = None,
792
        discovery_splash: Optional[str] = None,
793
        banner: Optional[str] = None,
794
        system_channel_id: Optional[Snowflake] = None,
795
        system_channel_flags: Optional[int] = None,
796
        rules_channel_id: Optional[Snowflake] = None,
797
        public_updates_channel_id: Optional[Snowflake] = None,
798
        preferred_locale: Optional[str] = None,
799
        features: Optional[List[GuildFeature]] = None,
800
        description: Optional[str] = None,
801
    ) -> Guild:
802
        """|coro|
803
        Modifies the guild
804
805
        Parameters
806
        ----------
807
        name : Optional[:class:`str`]
808
            Guild name |default| :data:`None`
809
        region : Optional[:class:`str`]
810
            Guild voice region ID |default| :data:`None`
811
        verification_level : Optional[:class:`int`]
812
            Verification level |default| :data:`None`
813
        default_message_notifications : Optional[:class:`int`]
814
            Default message notification level |default| :data:`None`
815
        explicit_content_filter : Optional[:class:`int`]
816
            Explicit content filter level |default| :data:`None`
817
        afk_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
818
            ID for AFK channel |default| :data:`None`
819
        afk_timeout : Optional[:class:`int`]
820
            AFK timeout in seconds |default| :data:`None`
821
        icon : Optional[:class:`str`]
822
            base64 1024x1024 png/jpeg/gif image for the guild icon
823
            (can be animated gif when the server
824
            has the `ANIMATED_ICON` feature) |default| :data:`None`
825
        owner_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
826
            User ID to transfer guild ownership to (must be owner) |default| :data:`None`
827
        splash : Optional[:class:`str`]
828
            base64 16:9 png/jpeg image for the guild splash (when the
829
            server has the `INVITE_SPLASH` feature) |default| :data:`None`
830
        discovery_splash : Optional[:class:`str`]
831
            base64 16:9 png/jpeg image for the guild discovery splash
832
            (when the server has the `DISCOVERABLE` feature) |default| :data:`None`
833
        banner : Optional[:class:`str`]
834
            base64 16:9 png/jpeg image for the guild banner (when the
835
            server has the `BANNER` feature) |default| :data:`None`
836
        system_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
837
            The ID of the channel where guild notices such as welcome
838
            messages and boost events are posted |default| :data:`None`
839
        system_channel_flags : Optional[:class:`int`]
840
            System channel flags |default| :data:`None`
841
        rules_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
842
            The ID of the channel where Community guilds display rules
843
            and/or guidelines |default| :data:`None`
844
        public_updates_channel_id : Optional[:class:`~pincer.utils.snowflake.Snowflake`]
845
            The ID of the channel where admins and moderators of
846
            Community guilds receive notices from Discord |default| :data:`None`
847
        preferred_locale : Optional[:class:`str`]
848
            The preferred locale of a Community guild used in server
849
            discovery and notices from Discord; defaults to "en-US" |default| :data:`None`
850
        features : Optional[List[:class:`GuildFeature`]]
851
            Enabled guild features |default| :data:`None`
852
        description : Optional[:class:`str`]
853
            The description for the guild, if the guild is discoverable |default| :data:`None`
854
855
        Returns
856
        -------
857
        :class:`~pincer.objects.guild.Guild`
858
            The modified guild object.
859
        """
860
        ...
861
862
    async def edit(self, **kwargs) -> Guild:
863
        g = await self._http.patch(f"guilds/{self.id}", data=kwargs)
864
        return Guild.from_dict(construct_client_dict(self._client, g))
865
866
    async def preview(self) -> GuildPreview:
867
        """|coro|
868
        Previews the guild.
869
870
        Returns
871
        -------
872
        :class:`~pincer.objects.guild.guild.GuildPreview`
873
            The guild preview object.
874
        """
875
        data = await self._http.get(f"guilds/{self.id}/preview")
876
        return GuildPreview.from_dict(data)
877
878
    async def delete(self):
879
        """|coro|
880
        Deletes the guild. Returns `204 No Content` on success.
881
        """
882
        await self._http.delete(f"guilds/{self.id}")
883
884
    async def prune_count(
885
        self,
886
        days: Optional[int] = 7,
887
        include_roles: Optional[str] = None
888
    ) -> int:
889
        """|coro|
890
        Returns the number of members that
891
        would be removed in a prune operation.
892
        Requires the ``KICK_MEMBERS`` permission.
893
894
        Parameters
895
        ----------
896
        days : Optional[:class:`int`]
897
            Number of days to count prune for (1-30) |default| :data:`7`
898
        include_roles : Optional[:class:`str`]
899
            Comma-delimited array of Snowflakes;
900
            role(s) to include |default| :data:`None`
901
902
        Returns
903
        -------
904
        :class:`int`
905
            The number of members that would be removed.
906
        """
907
        return await self._http.get(
908
            f"guilds/{self.id}/prune?{days=}&{include_roles=!s}"
909
        )["pruned"]
910
911
    async def prune(
912
        self,
913
        days: Optional[int] = 7,
914
        compute_prune_days: Optional[bool] = True,
915
        include_roles: Optional[List[Snowflake]] = None,
916
        reason: Optional[str] = None
917
    ) -> int:
918
        """|coro|
919
        Prunes members from the guild. Requires the ``KICK_MEMBERS`` permission.
920
921
        Parameters
922
923
        Parameters
924
        ----------
925
        days : Optional[:class:`int`]
926
            Number of days to prune (1-30) |default| :data:`7`
927
        compute_prune_days : Optional[:class:`bool`]
928
            Whether ``pruned`` is returned, discouraged for large guilds
929
            |default| :data:`True`
930
        include_roles : Optional[List[:class:`~pincer.utils.snowflake.Snowflake`]]
931
            role(s) to include |default| :data:`None`
932
        reason : Optional[:class:`str`]
933
            Reason for the prune |default| :data:`None`
934
935
        Returns
936
        -------
937
        :class:`int`
938
            The number of members that were removed.
939
        """
940
        return await self._http.post(
941
            f"guilds/{self.id}/prune",
942
            data={
943
                "days": days,
944
                "compute_prune_days": compute_prune_days,
945
                "include_roles": include_roles
946
            },
947
            headers=remove_none({"X-Audit-Log-Reason": reason})
948
        )["pruned"]
949
950
    async def get_voice_regions(self) -> AsyncGenerator[VoiceRegion, None]:
951
        """|coro|
952
        Returns an async generator of voice regions.
953
954
        Returns
955
        -------
956
        AsyncGenerator[:class:`~pincer.objects.voice.VoiceRegion`, :data:`None`]
957
            An async generator of voice regions.
958
        """
959
        data = await self._http.get(f"guilds/{self.id}/regions")
960
        for voice_region_data in data:
961
            yield VoiceRegion.from_dict(construct_client_dict(self._client, voice_region_data))
962
963
    async def get_invites(self) -> AsyncGenerator[Invite, None]:
964
        """|coro|
965
        Returns an async generator of invites for the guild.
966
        Requires the ``MANAGE_GUILD`` permission.
967
968
        Returns
969
        -------
970
        AsyncGenerator[:class:`~pincer.objects.invite.Invite`, :data:`None`]
971
            An async generator of invites.
972
        """
973
        data = await self._http.get(f"guilds/{self.id}/invites")
974
        for invite_data in data:
975
            yield Invite.from_dict(construct_client_dict(self._client, invite_data))
976
977
    async def get_integrations(self) -> AsyncGenerator[Integration, None]:
978
        """|coro|
979
        Returns an async generator of integrations for the guild.
980
        Requires the ``MANAGE_GUILD`` permission.
981
982
        Returns
983
        -------
984
        AsyncGenerator[:class:`~pincer.objects.integration.Integration`, :data:`None`]
985
            An async generator of integrations.
986
        """
987
        data = await self._http.get(f"guilds/{self.id}/integrations")
988
        for integration_data in data:
989
            yield Integration.from_dict(construct_client_dict(self._client, integration_data))
990
991
    async def delete_integration(
992
        self,
993
        integration: Integration,
994
        reason: Optional[str] = None
995
    ):
996
        """|coro|
997
        Deletes an integration.
998
        Requires the ``MANAGE_GUILD`` permission.
999
1000
        Parameters
1001
        ----------
1002
        integration : :class:`~pincer.objects.integration.Integration`
1003
            The integration to delete.
1004
        reason : Optional[:class:`str`]
1005
            Reason for the deletion |default| :data:`None`
1006
        """
1007
        await self._http.delete(
1008
            f"guilds/{self.id}/integrations/{integration.id}",
1009
            headers=remove_none({"X-Audit-Log-Reason": reason})
1010
        )
1011
1012
    async def get_widget_settings(self) -> GuildWidget:
1013
        """|coro|
1014
        Returns the guild widget settings.
1015
        Requires the ``MANAGE_GUILD`` permission.
1016
1017
        Returns
1018
        -------
1019
        :class:`~pincer.objects.guild.widget.GuildWidget`
1020
            The guild widget settings.
1021
        """
1022
        return GuildWidget.from_dict(
1023
            construct_client_dict(
1024
                self._client,
1025
                await self._http.get(f"guilds/{self.id}/widget")
1026
            )
1027
        )
1028
1029
    async def modify_widget(
1030
        self,
1031
        reason: Optional[str] = None,
1032
        **kwargs
1033
    ) -> GuildWidget:
1034
        """|coro|
1035
        Modifies the guild widget for the guild.
1036
        Requires the ``MANAGE_GUILD`` permission.
1037
1038
        Parameters
1039
        ----------
1040
        reason : Optional[:class:`str`]
1041
            Reason for the modification |default| :data:`None`
1042
        \\*\\*kwargs
1043
            The widget settings to modify
1044
1045
        Returns
1046
        -------
1047
        :class:`~pincer.objects.guild.widget.GuildWidget`
1048
            The updated GuildWidget object
1049
        """
1050
        data = await self._http.patch(
1051
            f"guilds/{self.id}/widget",
1052
            data=kwargs,
1053
            headers=remove_none({"X-Audit-Log-Reason": reason})
1054
        )
1055
        return GuildWidget.from_dict(construct_client_dict(self._client, data))
1056
1057
    async def get_widget(self) -> Dict[str, JSONSerializable]:
1058
        """|coro|
1059
        Returns the widget for the guild
1060
        """
1061
        return await self._http.get(f"guilds/{self.id}/widget.json")
1062
1063
    @property
1064
    async def vanity_url(self) -> Invite:
1065
        """|coro|
1066
        Returns the Vanity URL for the guild.
1067
        Requires the ``MANAGE_GUILD`` permission.
1068
        ``code`` will be null if a vanity URL has not been set.
1069
1070
        Returns
1071
        -------
1072
        :class:`~pincer.objects.guild.invite.Invite`
1073
            The vanity url for the guild.
1074
        """
1075
        data = await self._http.get(f"guilds/{self.id}/vanity-url")
1076
        return Invite.from_dict(construct_client_dict(self._client, data))
1077
1078
    async def get_widget_image(self, style: Optional[str] = "shield") -> str:  # TODO Replace str with ImageURL object
1079
        """|coro|
1080
        Returns a PNG image widget for the guild.
1081
        Requires no permissions or authentication.
1082
1083
        Widget Style Options
1084
        -------------------
1085
        * [``shield``](https://discord.com/api/guilds/81384788765712384/widget.png?style=shield)
1086
          shield style widget with Discord icon and guild members online count
1087
        * [``banner1``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner1)
1088
          large image with guild icon, name and online count.
1089
          "POWERED BY DISCORD" as the footer of the widget
1090
        * [``banner2``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner2)
1091
          smaller widget style with guild icon, name and online count.
1092
          Split on the right with Discord logo
1093
        * [``banner3``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner3)
1094
          large image with guild icon, name and online count.
1095
          In the footer, Discord logo on the
1096
          left and "Chat Now" on the right
1097
        * [``banner4``](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4)
1098
          large Discord logo at the top of the widget.
1099
          Guild icon, name and online count in the middle portion
1100
          of the widget and a "JOIN MY SERVER" button at the bottom
1101
1102
        Parameters
1103
        ----------
1104
        style : Optional[:class:`str`]
1105
            Style of the widget image returned |default| :data:`"shield"`
1106
1107
        Returns
1108
        -------
1109
        :class:`str`
1110
            A PNG image of the guild widget.
1111
        """
1112
        return await self._http.get(f"guilds/{self.id}/widget.png?{style=!s}")
1113
1114
    async def get_welcome_screen(self) -> WelcomeScreen:
1115
        """Returns the welcome screen for the guild.
1116
1117
        Returns
1118
        -------
1119
        :class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`
1120
            The welcome screen for the guild.
1121
        """
1122
        data = await self._http.get(f"guilds/{self.id}/welcome-screen")
1123
        return WelcomeScreen.from_dict(construct_client_dict(self._client, data))
1124
1125
    async def modify_welcome_screen(
1126
        self,
1127
        enabled: Optional[bool] = None,
1128
        welcome_channels: Optional[List[WelcomeScreenChannel]] = None,
1129
        description: Optional[str] = None,
1130
        reason: Optional[str] = None
1131
    ) -> WelcomeScreen:
1132
        """|coro|
1133
        Modifies the guild's Welcome Screen.
1134
        Requires the ``MANAGE_GUILD`` permission.
1135
1136
        Parameters
1137
        ----------
1138
        enabled : Optional[:class:`bool`]
1139
            Whether the welcome screen is enabled |default| :data:`None`
1140
        welcome_channels : Optional[List[:class:`~pincer.objects.guild.welcome_screen.WelcomeScreenChannel`]]
1141
            Channels linked in the welcome screen and
1142
            their display options |default| :data:`None`
1143
        description : Optional[:class:`str`]
1144
            The server description to show
1145
            in the welcome screen |default| :data:`None`
1146
        reason : Optional[:class:`str`]
1147
            Reason for the modification |default| :data:`None`
1148
1149
        Returns
1150
        -------
1151
        :class:`~pincer.objects.guild.welcome_screen.WelcomeScreen`
1152
            The updated WelcomeScreen object
1153
        """
1154
        data = await self._http.patch(
1155
            f"guilds/{self.id}/welcome-screen",
1156
            data={
1157
                "enabled": enabled,
1158
                "welcome_channels": welcome_channels,
1159
                "description": description
1160
            },
1161
            headers=remove_none({"X-Audit-Log-Reason": reason})
1162
        )
1163
        return WelcomeScreen.from_dict(construct_client_dict(self._client, data))
1164
1165
    async def modify_curent_user_voice_state(
1166
        self,
1167
        channel_id: Snowflake,
1168
        suppress: Optional[bool] = None,
1169
        request_to_speak_timestamp: Optional[Timestamp] = None
1170
    ):
1171
        """|coro|
1172
        Updates the current user's voice state.
1173
1174
        There are currently several caveats for this endpoint:
1175
        * ``channel_id`` must currently point to a stage channel
1176
        * current user must already have joined ``channel_id``
1177
        * You must have the ``MUTE_MEMBERS`` permission to
1178
          unsuppress yourself. You can always suppress yourself.
1179
        * You must have the ``REQUEST_TO_SPEAK`` permission to request
1180
          to speak. You can always clear your own request to speak.
1181
        * You are able to set ``request_to_speak_timestamp`` to any
1182
          present or future time.
1183
1184
        Parameters
1185
        ----------
1186
        channel_id : :class:`~pincer.utils.snowflake.Snowflake`
1187
            The ID of the channel the user is currently in
1188
        suppress : Optional[:class:`bool`]
1189
            Toggles the user's suppress state |default| :data:`None`
1190
        request_to_speak_timestamp : Optional[:class:`~pincer.utils.timestamp.Timestamp`]
1191
            Sets the user's request to speak
1192
        """
1193
        await self._http.patch(
1194
            f"guilds/{self.id}/voice-states/@me",
1195
            data={
1196
                "channel_id": channel_id,
1197
                "suppress": suppress,
1198
                "request_to_speak_timestamp": request_to_speak_timestamp
1199
            }
1200
        )
1201
1202
    async def modify_user_voice_state(
1203
        self,
1204
        user: User,
1205
        channel_id: Snowflake,
1206
        suppress: Optional[bool] = None
1207
    ):
1208
        """|coro|
1209
        Updates another user's voice state.
1210
1211
        There are currently several caveats for this endpoint:
1212
        * ``channel_id`` must currently point to a stage channel
1213
        * User must already have joined ``channel_id``
1214
        * You must have the ``MUTE_MEMBERS`` permission.
1215
          (Since suppression is the only thing that is available currently.)
1216
        * When unsuppressed, non-bot users will have their
1217
          ``request_to_speak_timestamp`` set to the current time.
1218
          Bot users will not.
1219
        * When suppressed, the user will have their
1220
          ``request_to_speak_timestamp`` removed.
1221
1222
        Parameters
1223
        ----------
1224
        user : :class:`~pincer.objects.guild.member.Member`
1225
            The user to update
1226
        channel_id : :class:`~pincer.utils.snowflake.Snowflake`
1227
            The ID of the channel the user is currently in
1228
        suppress : Optional[:class:`bool`]
1229
            Toggles the user's suppress state |default| :data:`None`
1230
        """
1231
        await self._http.patch(
1232
            f"guilds/{self.id}/voice-states/{user.id}",
1233
            data={
1234
                "channel_id": channel_id,
1235
                "suppress": suppress
1236
            }
1237
        )
1238
1239
    @classmethod
1240
    def from_dict(cls, data) -> Guild:
1241
        """
1242
        Parameters
1243
        ----------
1244
        data : :class:`Dict`
1245
            Guild data received from the discord API.
1246
        Returns
1247
        -------
1248
        :class:`~pincer.objects.guild.guild.Guild`
1249
            The new guild object.
1250
        Raises
1251
        :class:`~pincer.exceptions.UnavailableGuildError`
1252
            The guild is unavailable due to a discord outage.
1253
        """
1254
        if data.get("unavailable", False):
1255
            raise UnavailableGuildError(
1256
                f"Guild \"{data['id']}\" is unavailable due to a discord"
1257
                " outage."
1258
            )
1259
1260
        return super().from_dict(data)
1261
1262
1263
@dataclass
1264
class UnavailableGuild(APIObject):
1265
    id: Snowflake
1266
    unavailable: bool = True
1267