Exemple #1
0
class CategoryChannel(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE)

    _discord_cls = discord.CategoryChannel
    _discord_converter_cls = converter.CategoryChannelConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj, create_if_new=True):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")
        if create_if_new:
            guild, _ = Guild.sync_from_discord_obj(discord_obj.guild,
                                                   create_if_new=create_if_new)
            obj, created = cls.objects.get_or_create(id=discord_obj.id,
                                                     guild=guild)
        else:
            obj = cls.objects.get(id=discord_obj.id)
            # obj.guild._discord_obj = discord_obj.guild
            created = False

        obj._discord_obj = discord_obj
        return obj, not created

    async def fetch(self) -> discord.CategoryChannel:
        discord_category_channel = self._core.get_channel(self.id)
        if discord_category_channel is None:
            discord_category_channel = await self._core.fetch_channel(self.id)
        self._discord_obj = discord_category_channel
        # if not self.guild.is_fetched:
        #     await self.guild.fetch()
        return discord_category_channel
Exemple #2
0
class Player(models.Model):
    user = fields.OneToOneField(models.User,
                                primary_key=True,
                                on_delete=fields.CASCADE)
    challonge_username = fields.CharField(null=True,
                                          blank=True,
                                          unique=True,
                                          max_length=64)
    challonge_user_id = fields.BigIntegerField(null=True,
                                               blank=True,
                                               unique=True)
    region = RegionField(null=True, blank=True, db_index=True)
    rating = fields.IntegerField(db_index=True, default=1500)
    deviation = fields.IntegerField(default=350)
    volatility = fields.FloatField(default=0.06)

    @async_using_db
    def get_last_ranked_match(self):
        from ..models import Match
        user = self.user
        qs = (Match.objects.filter(
            guild__guildsetup__verified=True, ranked=True, player_1=user)
              | Match.objects.filter(guild__guildsetup__verified=True,
                                     ranked=True,
                                     player_2=user))
        return qs.latest()
Exemple #3
0
class Role(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE)

    _discord_cls = discord.Role
    _discord_converter_cls = converter.RoleConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")
        guild, _ = Guild.sync_from_discord_obj(discord_obj.guild)
        obj = cls(id=discord_obj.id, guild=guild)
        obj._discord_obj = discord_obj
        try:
            obj.load()
            existed_already = True
        except cls.DoesNotExist:
            existed_already = False
        return obj, existed_already

    async def fetch(self) -> discord.Role:
        if not self.guild.is_fetched:
            await self.guild.fetch()
        discord_role = self.guild.get_role(self.id)
        if discord_role is None:
            discord_role = await self.guild.fetch_role(self.id)
        self._discord_obj = discord_role
        return discord_role
Exemple #4
0
class Guild(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    home = fields.BooleanField(default=False)
    # shard_id = fields.SmallIntegerField(db_index=True)
    register_time = fields.DateTimeField(auto_now_add=True)
    invite_code = fields.CharField(null=True,
                                   blank=True,
                                   max_length=64,
                                   db_index=True)
    prefix = fields.CharField(null=True, blank=True, max_length=64)
    notifications_channel = fields.OneToOneField(
        'TextChannel',
        related_name='notifying_guild',
        null=True,
        blank=True,
        on_delete=fields.SET_NULL)
    language = fields.LanguageField()
    members = fields.ManyToManyField('User', through='Member')

    _discord_cls = discord.Guild

    @property
    def invite_url(self):
        if self.invite_code is None:
            return None
        return f'https://discord.gg/{self.invite_code}'

    @invite_url.setter
    def invite_url(self, value: str):
        if not isinstance(value, str):
            raise TypeError("invite_url must be a str")
        try:
            self.invite_code = value.split('://discord.gg/')[1]
        except IndexError:
            try:
                self.invite_code = value.split('://discordapp.com/invite/')[1]
            except IndexError:
                try:
                    self.invite_code = value.split('://discord.com/invite/')[1]
                except IndexError:
                    raise ValueError("Not a valid invite URL.")

    async def notify(self, content: str = None, **send_kwargs):
        notifications_channel = await self.notifications_channel
        if notifications_channel is None:
            raise ValueError("notifications_channel needs to be set first")
        dest = await notifications_channel.fetch()
        msg = await dest.send(content, **send_kwargs)
        return msg

    async def fetch(self) -> discord.Guild:
        discord_guild = self._core.get_guild(self.id)
        if discord_guild is None:
            discord_guild = await self._core.fetch_guild(self.id)
        self._discord_obj = discord_guild
        return discord_guild
Exemple #5
0
class Message(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    channel = fields.TextChannelField(db_index=True, on_delete=fields.CASCADE)
    author = fields.UserField(db_index=True, on_delete=fields.CASCADE)

    _discord_cls = discord.Message
    _discord_converter_cls = converter.MessageConverter

    @property
    def guild(self):
        return self.channel.guild

    @guild.setter
    def guild(self, value):
        if self.channel.is_dm:
            raise AttributeError("Cannot set guild of private message")
        self.channel.guild = value

    @classmethod
    def sync_from_discord_obj(cls, discord_obj, create_if_new=True):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")
        channel, _ = TextChannel.sync_from_discord_obj(
            discord_obj.channel, create_if_new=create_if_new)
        if discord_obj.guild:
            user = discord_obj.author._user
        else:
            user = discord_obj.author
        author, _ = User.sync_from_discord_obj(user)
        if create_if_new:
            obj, created = cls.objects.get_or_create(id=discord_obj.id,
                                                     channel=channel,
                                                     author=author)
        else:
            obj = cls.objects.get(id=discord_obj.id)
            created = False
        # obj.channel._discord_obj = discord_obj.channel
        # obj.user._discord_obj = user
        obj._discord_obj = discord_obj
        return obj, not created

    async def fetch(self) -> discord.Message:
        # if not self.channel.is_fetched:
        #     await self.channel.fetch()
        channel = await self.channel
        await channel.fetch()
        discord_message = await channel.fetch_message(self.id)
        self._discord_obj = discord_message
        return discord_message
Exemple #6
0
class Emoji(DiscordModel):
    id = fields.BigIntegerField(primary_key=True, auto_created=True)
    name = fields.CharField(max_length=64)
    animated = fields.BooleanField(default=False)
    is_custom = fields.BooleanField()

    _discord_cls = discord.PartialEmoji
    _discord_converter_cls = converter.PartialEmojiConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, (cls._discord_cls, discord.Emoji)):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"or discord.Emoji"
                f"but a {type(discord_obj).__name__} was passed")
        if isinstance(discord_obj, discord.Emoji):
            discord_obj = discord.PartialEmoji(name=discord_obj.name,
                                               animated=discord_obj.animated,
                                               id=discord_obj.id)
        obj = cls(id=discord_obj.id,
                  name=discord_obj.name,
                  animated=discord_obj.animated)
        obj._discord_obj = discord_obj
        try:
            obj.load()
            existed_already = True
        except cls.DoesNotExist:
            existed_already = False
        return obj, existed_already

    async def fetch(self) -> discord.PartialEmoji:
        if self.is_custom:
            if not self.guild.is_fetched:
                await self.guild.fetch()
            self.guild: discord.Guild
            emoji = await self.guild.fetch_emoji(self.id)
            discord_emoji = discord.PartialEmoji(name=emoji.name,
                                                 animated=emoji.animated,
                                                 id=emoji.id)
            if self.name != emoji.name:
                self.name = emoji.name
                await self.async_save()
        else:
            discord_emoji = discord.PartialEmoji(name=self.name)
        self._discord_obj = discord_emoji

        return discord_emoji
Exemple #7
0
class Guild(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    home = fields.BooleanField(default=False)
    shard_id = fields.SmallIntegerField(db_index=True)
    register_time = fields.DateTimeField(auto_now_add=True)
    invite_code = fields.CharField(null=True,
                                   blank=True,
                                   max_length=64,
                                   db_index=True)
    prefix = fields.CharField(null=True, blank=True, max_length=64)
    language = fields.LanguageField()
    members = fields.ManyToManyField(to='User', through='Member')
    moderating_guild = fields.GuildField(null=True,
                                         blank=True,
                                         on_delete=fields.SET_NULL)

    _discord_cls = discord.Guild

    @property
    def invite_url(self):
        if self.invite_code is None:
            return None
        return f'https://discord.gg/{self.invite_code}'

    @invite_url.setter
    def invite_url(self, value: str):
        if not isinstance(value, str):
            raise TypeError("invite_url must be a str")
        try:
            self.invite_code = value.split('://discord.gg/')[1]
        except IndexError:
            try:
                self.invite_code = value.split('://discordapp.com/invite/')[1]
            except IndexError:
                try:
                    self.invite_code = value.split('://discord.com/invite/')[1]
                except IndexError:
                    raise ValueError("Not a valid invite URL.")

    async def fetch(self) -> discord.Guild:
        discord_guild = self._core.get_guild(self.id)
        if discord_guild is None:
            discord_guild = await self._core.fetch_guild(self.id)
        self._discord_obj = discord_guild
        return discord_guild
Exemple #8
0
class Message(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    channel = fields.TextChannelField(db_index=True, on_delete=fields.CASCADE)
    author = fields.UserField(db_index=True, on_delete=fields.CASCADE)

    _discord_cls = discord.Message
    _discord_converter_cls = converter.MessageConverter

    @property
    def guild(self):
        return self.channel.guild

    @guild.setter
    def guild(self, value):
        self.channel.guild = value

    @classmethod
    def sync_from_discord_obj(cls, discord_obj):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")
        channel, _ = TextChannel.sync_from_discord_obj(discord_obj.channel)
        author, _ = Member.sync_from_discord_obj(discord_obj.author)
        obj = cls(id=discord_obj.id, channel=channel, author=author)
        obj._discord_obj = discord_obj
        try:
            obj.load()
            existed_already = True
        except cls.DoesNotExist:
            existed_already = False
        return obj, existed_already

    async def fetch(self) -> discord.Message:
        if not self.channel.is_fetched:
            await self.channel.fetch()
        discord_message = self.channel.get_message(self.id)
        if discord_message is None:
            discord_message = await self.channel.fetch_message(self.id)
        self._discord_obj = discord_message
        return discord_message
class DoublesMatch(models.Model):
    id = fields.BigIntegerField(primary_key=True)
    channel = fields.TextChannelField(null=True,
                                      db_index=True,
                                      on_delete=fields.SET_NULL)
    guild = fields.GuildField(on_delete=fields.CASCADE)
    # if tournament is None, it's a matchmaking match
    tournament = fields.ForeignKey(Tournament,
                                   null=True,
                                   blank=True,
                                   on_delete=fields.CASCADE)
    in_dms = fields.BooleanField()
    team_1 = fields.ForeignKey(ParticipantTeam, on_delete=fields.CASCADE)
    team_2 = fields.ForeignKey(ParticipantTeam, on_delete=fields.CASCADE)
    team_1_score = fields.SmallIntegerField(default=0)
    team_2_score = fields.SmallIntegerField(default=0)
    current_game = fields.SmallIntegerField(default=1)
    last_game_won_by = fields.SmallIntegerField(null=True)
    wins_required = fields.SmallIntegerField(default=2)
    winner = fields.ForeignKey(ParticipantTeam,
                               null=True,
                               blank=True,
                               on_delete=fields.CASCADE)
Exemple #10
0
class TextChannel(DiscordModel):
    # can also be a DMChannel
    id = fields.BigIntegerField(primary_key=True)
    guild = fields.GuildField(db_index=True,
                              null=True,
                              blank=True,
                              on_delete=fields.CASCADE)
    is_dm = fields.BooleanField(default=False)
    language = fields.LanguageField()

    _discord_cls = discord.TextChannel
    _discord_converter_cls = converter.TextChannelConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj, create_if_new=True):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            if isinstance(discord_obj, discord.DMChannel):
                is_dm = True
            else:
                raise TypeError(
                    f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                    f"or a discord.DMChannel "
                    f"but a {type(discord_obj).__name__} was passed")
        else:
            is_dm = False

        if is_dm:
            if create_if_new:
                obj, created = cls.objects.get_or_create(id=discord_obj.id,
                                                         is_dm=True)
            else:
                obj = cls.objects.get(id=discord_obj.id)
                created = False
        else:
            if create_if_new:
                guild, _ = Guild.sync_from_discord_obj(discord_obj.guild)
                obj, created = cls.objects.get_or_create(id=discord_obj.id,
                                                         guild=guild,
                                                         is_dm=False)
            else:
                obj = cls.objects.get(id=discord_obj.id)
                # obj.guild._discord_obj = discord_obj.guild
                created = False

        obj._discord_obj = discord_obj
        return obj, not created

    @classmethod
    async def convert(cls, ctx, argument):
        converter = cls._discord_converter_cls()
        discord_obj = await converter.convert(ctx, argument)
        obj, existed_already = await cls.from_discord_obj(discord_obj)
        if not existed_already:
            await obj.async_save()
        return obj

    async def fetch(self) -> discord.TextChannel:
        discord_text_channel = self._core.get_channel(self.id)
        if discord_text_channel is None:
            discord_text_channel = await self._core.fetch_channel(self.id)
        self._discord_obj = discord_text_channel

        # if not self.is_dm and not self.guild.is_fetched:
        #     await self.guild.fetch()
        return discord_text_channel
Exemple #11
0
class User(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    is_staff = fields.BooleanField(default=False, db_index=True)
    is_active = fields.BooleanField(default=True, db_index=True)
    register_message = fields.MessageField(blank=True,
                                           null=True,
                                           on_delete=fields.SET_NULL)
    language = fields.LanguageField()

    _discord_cls = discord.User
    _discord_converter_cls = converter.UserConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj, create_if_new=True):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj,
                          (cls._discord_cls, discord.ClientUser,
                           discord.Member, MockMember, discord.Object)):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")

        if not isinstance(discord_obj, (MockMember, discord.Object)):
            # if self
            if discord_obj.id == discord_obj._state.user.id:
                obj, _ = cls.objects.get_or_create(id=discord_obj.id)
                return obj, True

            if discord_obj.bot:
                raise ValueError("Bot users cannot be stored in the database")
        qs = cls.objects.filter(id=discord_obj.id)
        existed_already = qs.exists()
        if not existed_already:
            raise UserDoesNotExist(user_id=discord_obj.id)
        obj = qs.first()
        if not obj.is_active:
            raise InactiveUser(user_id=discord_obj.id)
        if isinstance(discord_obj, discord.Member):
            discord_obj = discord_obj._user
        if not isinstance(discord_obj, (MockMember, discord.Object)):
            obj._discord_obj = discord_obj
        return obj, existed_already

    @async_using_db
    def async_delete(self, using=None, keep_parents=False):
        self.delete(using=using, keep_parents=keep_parents)

    def delete(self, using=None, keep_parents=False):
        _id = self.id
        name = self.name
        super().delete(using=using, keep_parents=keep_parents)
        new_user = User(id=_id, is_active=False)
        new_user.save()

    @async_using_db
    def async_load(self, prefetch_related=True):
        self.load(prefetch_related=prefetch_related)

    def load(self, prefetch_related=True):
        super().load(prefetch_related=True)
        if not self.is_active:
            raise InactiveUser(f"The user {self.id} is inactive")

    @async_using_db
    def _get_register_message(self):
        # allows internals to bypass GDPR checks to make the GDPR functionality
        # itself work, e.g. to handle register reactions
        super().load(prefetch_related=False)
        return self.register_message

    async def fetch(self) -> discord.User:
        if not self._is_loaded:
            await self.async_load()
        discord_user = self._core.get_user(self.id)
        if discord_user is None:
            discord_user = await self._core.fetch_user(self.id)
        self._discord_obj = discord_user
        return discord_user
Exemple #12
0
class Tournament(models.Model):
    id = fields.BigIntegerField(primary_key=True)  # Challonge ID
    key = fields.CharField(max_length=128, unique=True)
    name = fields.CharField(max_length=128)
    series = fields.ForeignKey(TournamentSeries,
                               null=True,
                               blank=True,
                               db_index=True,
                               on_delete=fields.SET_NULL)
    ranked = fields.BooleanField()
    signup_message = fields.MessageField(unique=True,
                                         db_index=True,
                                         null=True,
                                         on_delete=fields.SET_NULL)
    checkin_message = fields.MessageField(unique=True,
                                          db_index=True,
                                          null=True,
                                          blank=True,
                                          on_delete=fields.SET_NULL)
    # signup_emoji = fields.EmojiField(default=get_default_emoji, on_delete=fields.SET_DEFAULT)
    # checkin_emoji = fields.EmojiField(default=get_default_emoji, on_delete=fields.SET_DEFAULT)
    guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE)
    announcements_channel = fields.TextChannelField(null=True,
                                                    on_delete=fields.SET_NULL)
    talk_channel = fields.TextChannelField(null=True,
                                           blank=True,
                                           on_delete=fields.SET_NULL)
    participant_role = fields.RoleField(null=True,
                                        unique=True,
                                        on_delete=fields.SET_NULL)
    organizer_role = fields.RoleField(null=True, on_delete=fields.SET_NULL)
    streamer_role = fields.RoleField(null=True,
                                     blank=True,
                                     on_delete=fields.SET_NULL)
    doubles = fields.BooleanField(db_index=True)
    format = FormatField(default=Formats.double_elimination)
    allow_matches_in_dms = fields.BooleanField()
    # don't hard delete rulesets that have already been used
    # instead, the ruleset should be swapped out with the updated version
    # and only for upcoming and ongoing tournaments
    ruleset = fields.ForeignKey(Ruleset, on_delete=fields.PROTECT)
    start_time = fields.DateTimeField(db_index=True)
    delay_start = fields.SmallIntegerField(null=True, blank=True)  # minutes
    start_task = fields.ForeignKey(ScheduledTask,
                                   null=True,
                                   on_delete=fields.SET_NULL)
    start_checkin_task = fields.ForeignKey(ScheduledTask,
                                           null=True,
                                           on_delete=fields.SET_NULL)
    check_reactions_task = fields.ForeignKey(ScheduledTask,
                                             null=True,
                                             on_delete=fields.SET_NULL)
    ended = fields.BooleanField(db_index=True, default=False)

    @property
    def full_challonge_url(self):
        return f"https://challonge.com/{self.key}"

    async def get_challonge_tournament(self):
        core = self._core
        extension_name = self._meta.app_label
        ssbu = core.get_controller(extension_name)
        return await ssbu.get_challonge_tournament(self.id)

    @classmethod
    async def convert(cls, ctx, argument):
        try:
            argument = int(argument)
            # argument is Challonge ID
            tournament = await cls.async_get(pk=argument)
        except ValueError:
            # argument is URL key
            tournament = await cls.async_get(key=argument)
        return tournament
Exemple #13
0
class User(DiscordModel):
    id = fields.BigIntegerField(primary_key=True)
    is_staff = fields.BooleanField(default=False, db_index=True)
    is_active = fields.BooleanField(default=True, db_index=True)
    register_message = fields.MessageField(blank=True,
                                           null=True,
                                           on_delete=fields.SET_NULL)
    language = fields.LanguageField()

    _discord_cls = discord.User
    _discord_converter_cls = converter.UserConverter

    @classmethod
    def sync_from_discord_obj(cls, discord_obj):
        """Create a Hero object from a Discord object"""
        if not isinstance(discord_obj, cls._discord_cls):
            raise TypeError(
                f"discord_obj has to be a discord.{cls._discord_cls.__name__} "
                f"but a {type(discord_obj).__name__} was passed")
        qs = cls.objects.filter(id=discord_obj.id)
        existed_already = qs.exists()
        if not existed_already:
            raise UserDoesNotExist(user_id=discord_obj.id)
        obj = qs.first()
        if not obj.is_active:
            raise InactiveUser(user_id=discord_obj.id)
        obj._discord_obj = discord_obj
        return obj, existed_already

    @async_using_db
    def async_delete(self, using=None, keep_parents=False):
        self.delete(using=using, keep_parents=keep_parents)

    def delete(self, using=None, keep_parents=False):
        _id = self.id
        name = self.name
        super().delete(using=using, keep_parents=keep_parents)
        if not User.objects.filter(id=_id).exists():
            print("Deleted user", name)
        new_user = User(id=_id, is_active=False)
        new_user.save()

    @async_using_db
    def async_load(self, prefetch_related=True):
        super().load(prefetch_related=False)
        if not self.is_active:
            raise self.InactiveUser(f"The user {self.id} is inactive")

    def load(self, prefetch_related=True):
        super().load(prefetch_related=True)
        if not self.is_active:
            raise self.InactiveUser(f"The user {self.id} is inactive")

    async def fetch(self) -> discord.User:
        if not self._is_loaded:
            self.load()
        discord_user = self._core.get_user(self.id)
        if discord_user is None:
            discord_user = await self._core.fetch_user(self.id)
        self._discord_obj = discord_user
        return discord_user