Source code for discord.guild

# -*- coding: utf-8 -*-
# cython: language_level=3
# Copyright (c) 2021-present VincentRPS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE
"""Represents a Discord Guild.

ref: https://discord.dev/resources/guild
"""
from typing import Dict, List, Optional, Union

from .assets import Emoji, Sticker
from .channels import channel_parse
from .enums import FormatType, ScheduledEventStatusType, ScheduledEventType
from .http import RESTFactory
from .member import Member
from .user import User

__all__: List[str] = [
    'Guild',
    'Role',
    'WelcomeScreen',
    'WelcomeChannel',
    'ScheduledEvent',
    'ScheduledEventMetadata',
]


class BanObject:
    """Represents a Discord Ban Object

    .. versionadded:: 1.0

    Parameters
    ----------
    data: :class:`dict`
        The raw data
    """

    def __init__(self, data: Dict):
        self.from_dict = data

    @property
    def reason(self) -> str:
        """The reason of the ban

        Returns
        -------
        :class:`str`
        """
        return self.from_dict['reason']

    @property
    def user(self) -> User:
        """The user which invoked the ban

        Returns
        -------
        :class:`User`
        """
        return User(self.from_dict['user'])


def parse_guild_hash(type: int, id, hash: str):

    if type == 1:
        return f"https://cdn.discordapp.com/icons/{id}/{hash}"

    elif type == 2:
        return f"https://cdn.discordapp.com/splashes/{id}/{hash}"

    elif type == 3:
        return f'https://cdn.discordapp.com/discovery-splashes/{id}/{hash}'


class GuildPreview:
    """Represents a Discord Guild Preview.

    .. versionadded:: 1.0

    Parameters
    ----------
    data: :class:`dict`
        The raw preview data
    """

    def __init__(self, data: dict):
        self.from_dict = data

    @property
    def id(self) -> int:
        return self.from_dict['id']

    @property
    def name(self) -> str:
        return self.from_dict['name']

    @property
    def icon_url(self) -> str:
        return parse_guild_hash(1, self.id, self.from_dict['icon'])

    @property
    def splash_url(self) -> str:
        return parse_guild_hash(2, self.id, self.from_dict['splash'])

    @property
    def discovery_splash_url(self) -> str:
        return parse_guild_hash(3, self.id, self.from_dict['discovery_splash'])

    @property
    def emojis(self) -> List[Emoji]:
        return [Emoji(emoji) for emoji in self.from_dict['emojis']]

    def features(self) -> str:
        return self.from_dict['features']

    def approximate_member_count(self) -> int:
        return self.from_dict['approximate_member_count']

    def approximate_presence_count(self) -> int:
        return self.from_dict['approximate_presence_count']

    @property
    def description(self) -> str:
        return self.from_dict.get('description')

    @property
    def stickers(self) -> List[Sticker]:
        return [Sticker(sticker) for sticker in self.from_dict['stickers']]


[docs]class Guild: """Represents a Discord Guild. .. versionadded:: 0.6.0 Parameters ---------- guild The raw guild object rest_factory The current :class:`RESTFactory` """ # cache helpers for guilds. def __init__(self, guild: Dict, rest_factory): self.from_dict = guild self._factory: RESTFactory = rest_factory def __repr__(self): return f"<Guild guild_id={self.id!r}>" @property def joined_at(self) -> str: return self.from_dict['joined_at']
[docs] def vanity(self) -> str: """The vanity url, if None returns None Returns ------- :class:`str` """ return self.from_dict['vanity_url_code']
[docs] def splash(self) -> str: """The splash screen, if None returns None Returns ------- :class:`str` """ return self.from_dict['splash']
[docs] def discovery_splash(self) -> str: """The discovery splash, if None returns None Returns ------- :class:`str` """ return self.from_dict['discovery_splash']
[docs] def sub_count(self) -> int: """The subscription count, returns a int Returns ------- :class:`int` """ return self.from_dict['premium_subscription_count']
[docs] def emojis(self) -> List[Emoji]: """A list of :class:`Emoji` Returns ------- list[:class:`Emoji`] """ return [Emoji(emoji) for emoji in self.from_dict['emojis']]
@property def id(self) -> int: """The guild id Returns ------- :class:`int` """ return self.from_dict['id']
[docs] async def get_member(self, id: int): """Gets a member and returns a :class:`Member` object Parameters ---------- id The members id Returns ------- :class:`Member` """ unparsed = await self._factory.guilds.get_guild_member(self.id, id) return Member(unparsed, self.id, self._factory)
[docs] async def change_voice_state( self, *, channel: Optional[int] = None, self_mute: Optional[bool] = False, self_deaf: Optional[bool] = False, ): """Changes the bot's voice state in a guild .. versionadded:: 0.8.0 Parameters ---------- channel: :class:`int` The channel to connect to, None if to disconnect. self_mute Should the bot be muted? self_deaf Should the bot be deaf? """ await self._factory.state.app.gateway.voice_state( guild=self.id, channel=channel, mute=self_mute, deaf=self_deaf )
[docs] def leave(self): """Leaves the guild""" return self._factory.guilds.leave_guild(self.id)
[docs] async def get_channels(self): """Gives a list of Channel objects Returns ------- List[Union[:class:`TextChannel`, :class:`VoiceChannel`, :class:`Category`, :class:`DMChannel`, :class:`GroupDMChannel`, :class:`Thread`]] """ raw = await self._factory.channels.get_guild_channels(self.id) return [ channel_parse(channel['type'], channel, self._factory.state) for channel in raw ]
[docs] async def get_bans(self): """Gives a list of :class:`Ban` Returns ------- List[:class:`Ban`] """ raw = await self._factory.guilds.get_guild_bans(self.id) return [BanObject(ban) for ban in raw]
[docs] async def get_ban(self, user_id: int): """Gets a ban for a user Parameters ---------- user_id: :class:`int` The user id to get the ban for Returns ------- :class:`Ban` """ raw = await self._factory.guilds.get_guild_ban(self.id, user_id) return BanObject(raw)
async def get_preview(self): raw = await self._factory.guilds.get_guild_preview(self.id) return GuildPreview(raw)
def parse_role_icon(format: FormatType, role_id: int, role_icon: str) -> str: return f'https://cdn.discordapp.com/role-icons/{role_id}/{role_icon}.{format}' def _parse_tags(data: Dict) -> str: return data.get('bot_id') or data.get('integration_id') or ''
[docs]class Role: """Represents a Discord Role .. versionadded:: 0.8.0 Parameters ---------- data: :class:`dict` The raw role data """ def __init__(self, data: Dict, factory): self.from_dict = data self.factory: RESTFactory = factory @property def id(self) -> int: """Gives the snowflake id of the role Returns ------- :class:`int` """ return self.from_dict['id'] @property def name(self) -> str: """Gives the name of the role Returns ------- :class:`str` """ return self.from_dict['name'] @property def color(self) -> int: """Returns the roles color Returns ------- :class:`int` """ return self.from_dict['color']
[docs] def hoist(self) -> bool: """If the role is hoisted or not Returns ------- :class:`bool` """ return self.from_dict['hoist']
[docs] def icon(self, format: Optional[FormatType] = FormatType.PNG) -> str: """Gives the role icon's link, if any. Returns ------- :class:`str` """ return parse_role_icon( format=format, role_id=self.id, role_icon=self.from_dict['icon'] )
[docs] def unicode_emoji(self) -> str: """Gives the roles unicode emoji Returns ------- :class:`str` """ return self.from_dict['unicode_emoji']
@property def position(self) -> int: """Gives the current role position Returns ------- :class:`int` """ return self.from_dict['position']
[docs] def permissions(self) -> str: """Gives the role permissions Returns ------- :class:`str` """ # TODO: Replace with a Permission class? return self.from_dict['permissions']
[docs] def managed(self) -> bool: """If the role is managed or not Returns ------- :class:`bool` """ return self.from_dict['managed']
[docs] def mentionable(self) -> bool: """If the role is publicly mentionable Returns ------- :class:`bool` """ return self.from_dict['mentionable']
[docs] def tags(self) -> str: """The current role' tags Returns ------- :class:`str` """ return _parse_tags(self.from_dict)
[docs] def give_to(self, user_id: int, reason: Optional[str] = None): """Gives a user the role""" return self.factory.guilds.give_user_role(user_id, self.id, reason=reason)
[docs] def remove_from( self, user_id: int, reason: Optional[str] = None, ): """Removes a user from the role""" return self.factory.guilds.remove_user_role(user_id, self.id, reason=reason)
def parse_event_banner(format: FormatType, event_id: int, event_hash: str) -> str: return f'https://cdn.discordapp.com/guild-events/{event_id}/{event_hash}.{format}'
[docs]class ScheduledEvent: """Represents a Discord Guild Scheduled Event .. versionadded:: 0.8.0 Parameters ---------- data: :class:`dict` The raw event data """ def __init__(self, data: Dict, factory): self.from_dict = data self.factory: RESTFactory = factory @property def id(self) -> int: """Gives the scheduled events snowflake id Returns ------- :class:`int` """ return self.from_dict['id']
[docs] def guild_id(self) -> int: """Gives the scheduled event' current guild Returns ------- :class:`int` """ return self.from_dict['guild_id']
[docs] def channel_id(self) -> Union[int, None]: """Gives the scheduled events current channel, if any Returns ------- :class:`int` :class:`None` """ return self.from_dict.get('channel_id')
@property def creator(self) -> User: """Gives a :class:`User` of the creator of this scheduled event Returns ------- :class:`User` """ return User(self.from_dict['creator']) @property def name(self) -> str: """Gives the scheduled event' name Returns ------- :class:`str` """ return self.from_dict['name'] @property def description(self) -> str: """Gives the description of the scheduled event Returns ------- :class:`str` """ return self.from_dict['description']
[docs] def start_time(self) -> str: """Gives the start time of the scheduled event Returns ------- :class:`str` """ return self.from_dict['scheduled_start_time']
[docs] def status(self) -> ScheduledEventStatusType: """Gives the current event' status Returns ------- :class:`int` """ if self.from_dict['status'] == ScheduledEventStatusType.ACTIVE: return ScheduledEventStatusType.ACTIVE elif self.from_dict['status'] == ScheduledEventStatusType.COMPLETED: return ScheduledEventStatusType.COMPLETED elif self.from_dict['status'] == ScheduledEventStatusType.SCHEDULED: return ScheduledEventStatusType.SCHEDULED elif self.from_dict['status'] == ScheduledEventStatusType.CANCELED: return ScheduledEventStatusType.CANCELED
[docs] def end_time(self) -> Union[None, str]: """Gives the endtime of the scheduled event Returns ------- :class:`str` :class:`None` """ return self.from_dict.get('scheduled_end_time')
[docs] def type(self) -> ScheduledEventType: """Gives the type of scheduled event Returns ------- :class:`int` """ if self.from_dict['entity_type'] == ScheduledEventType.STAGE_INSTANCE: return ScheduledEventType.STAGE_INSTANCE elif self.from_dict['entity_type'] == ScheduledEventType.VOICE: return ScheduledEventType.VOICE elif self.from_dict['entity_type'] == ScheduledEventType.EXTERNAL: return ScheduledEventType.EXTERNAL
[docs] def entity_id(self) -> int: """Gives the current entity id Returns ------- :class:`int` """ return self.from_dict['entity_id']
@property def metadata(self) -> 'ScheduledEventMetadata': """Gives the scheduled event metadata Returns ------- :class:`ScheduledEventMetadata` """ return ScheduledEventMetadata(self.from_dict['entity_metadata'])
[docs] def joined(self) -> int: """Gives the amount of users which joined the event Returns ------- :class:`int` """ return self.from_dict['user_count']
[docs] def image(self, format: FormatType = FormatType.PNG): """Gives the url of the scheduled event' banner Returns ------- :class:`str` """ return parse_event_banner( format=format, event_id=self.id, event_hash=self.from_dict['image'] )
[docs]class ScheduledEventMetadata: """Represents a Discord Scheduled event metadata .. versionadded:: 0.8.0 Parameters ---------- data: :class:`dict` The raw metadata' data """ def __init__(self, data: Dict): self.from_dict = data @property def location(self) -> Union[str, None]: """Gives the location the event is happening in Returns ------- :class:`str` :class:`None` """ return self.from_dict['location']
[docs]class WelcomeScreen: """Represents a Discord Guild WelcomeScreen .. versionadded:: 0.8.0 Parameters ---------- data: :class:`dict` """ def __init__(self, data: Dict, factory): self.from_dict = data self.factory: RESTFactory = factory @property def description(self) -> str: """Gives the WelcomeScreen' description Returns ------- :class:`str` """ return self.from_dict['description']
[docs] def channels(self) -> List['WelcomeChannel']: """Gives a list of :class:`WelcomeChannel` Returns ------- list[:class:`WelcomeChannel`] """ return [ WelcomeChannel(channel) for channel in self.from_dict['welcome_channels'] ]
[docs]class WelcomeChannel: """Represents a Discord WelcomeScreen Channel .. versionadded:: 0.8.0 Parameters ---------- data: :class:`dict` The raw WelcomeChannel data """ def __init__(self, data: Dict): self.from_dict = data @property def channel_id(self) -> int: """Gives the Channel' id Returns ------- :class:`int` """ return self.from_dict['channel_id'] @property def description(self) -> str: """Gives the description of the channel Returns ------- :class:`str` """ return self.from_dict['description']
[docs] def emoji_id(self) -> Union[int, None]: """Gives the Emoji id, if any Returns ------- :class:`int` :class:`None` """ return self.from_dict.get('emoji_id')
[docs] def emoji_name(self) -> Union[str, None]: """Gives the Emoji name, if any Returns ------- :class:`str` :class:`None` """ return self.from_dict.get('emoji_name')