Beispiel #1
0
class DoublesGame(models.Model):
    match = fields.ForeignKey(DoublesMatch,
                              null=True,
                              on_delete=fields.SET_NULL)
    number = fields.SmallIntegerField()
    guild = fields.GuildField(null=True, on_delete=fields.SET_NULL)
    first_to_strike = fields.ForeignKey(ParticipantTeam,
                                        null=True,
                                        on_delete=fields.SET_NULL)
    striking_message = fields.MessageField(null=True,
                                           on_delete=fields.SET_NULL)
    striked_stages = fields.SeparatedValuesField(default=[], max_length=64)
    suggested_stage = fields.SmallIntegerField(null=True, blank=True)
    suggested_by = fields.ForeignKey(ParticipantTeam,
                                     null=True,
                                     on_delete=fields.SET_NULL)
    suggestion_accepted = fields.BooleanField(null=True, blank=True)
    picked_stage = fields.SmallIntegerField(null=True, blank=True)
    winner = fields.ForeignKey(ParticipantTeam,
                               null=True,
                               blank=True,
                               on_delete=fields.SET_NULL)
    needs_confirmation_by = fields.ForeignKey(ParticipantTeam,
                                              null=True,
                                              blank=True,
                                              on_delete=fields.SET_NULL)
Beispiel #2
0
class Game(models.Model):
    match = fields.ForeignKey(Match, null=True, on_delete=fields.SET_NULL)
    number = fields.SmallIntegerField()
    guild = fields.GuildField(null=True,
                              db_index=True,
                              on_delete=fields.SET_NULL)
    player_1_fighter = fields.SmallIntegerField(null=True, blank=True)
    player_2_fighter = fields.SmallIntegerField(null=True, blank=True)
    first_to_strike = fields.UserField(null=True, on_delete=fields.SET_NULL)
    striking_message = fields.MessageField(null=True,
                                           on_delete=fields.SET_NULL)
    striked_stages = fields.SeparatedValuesField(default=[],
                                                 max_length=64,
                                                 converter=Stage.parse,
                                                 serializer=Stage.serialize)
    suggested_stage = fields.SmallIntegerField(null=True, blank=True)
    suggested_by = fields.UserField(null=True, on_delete=fields.SET_NULL)
    suggestion_accepted = fields.BooleanField(null=True, blank=True)
    picked_stage = fields.SmallIntegerField(null=True, blank=True)
    winner = fields.UserField(null=True, blank=True, on_delete=fields.SET_NULL)
    needs_confirmation_by = fields.UserField(null=True,
                                             blank=True,
                                             on_delete=fields.SET_NULL)

    def is_striked(self, stage):
        return stage in self.striked_stages
Beispiel #3
0
class MatchmakingSetup(models.Model):
    channel = fields.OneToOneField(models.TextChannel,
                                   primary_key=True,
                                   on_delete=fields.CASCADE)
    name = fields.CharField(max_length=64)
    matchmaking_message = fields.MessageField(on_delete=fields.CASCADE)
    ruleset = fields.ForeignKey(Ruleset,
                                null=True,
                                blank=True,
                                on_delete=fields.SET_NULL)
    looking_role = fields.RoleField(on_delete=fields.CASCADE)
    available_role = fields.RoleField(on_delete=fields.CASCADE)
    ranked = fields.BooleanField(default=False)
Beispiel #4
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
Beispiel #5
0
class Match(models.Model):
    class Meta:
        get_latest_by = 'started_at'

    id = fields.BigAutoField(primary_key=True)
    channel = fields.TextChannelField(null=True,
                                      blank=True,
                                      db_index=True,
                                      unique=True,
                                      on_delete=fields.SET_NULL)
    guild = fields.GuildField(on_delete=fields.CASCADE)
    voice_channel = fields.VoiceChannelField(null=True,
                                             blank=True,
                                             on_delete=fields.SET_NULL)
    # if tournament is None, it's a matchmaking match
    tournament = fields.ForeignKey(Tournament,
                                   null=True,
                                   blank=True,
                                   on_delete=fields.CASCADE)
    setup = fields.ForeignKey(MatchmakingSetup,
                              null=True,
                              blank=True,
                              on_delete=fields.SET_NULL)
    management_message = fields.MessageField(null=True,
                                             blank=True,
                                             on_delete=fields.SET_NULL)
    ranked = fields.BooleanField()
    in_dms = fields.BooleanField()
    # if matchmaking match, looking is player_1, offering is player_2
    player_1 = fields.UserField(db_index=True, on_delete=fields.CASCADE)
    player_1_rating = fields.IntegerField(null=True, blank=True)
    player_1_deviation = fields.IntegerField(null=True, blank=True)
    player_1_volatility = fields.FloatField(null=True, blank=True)
    player_1_global_rating = fields.IntegerField(null=True, blank=True)
    player_1_global_deviation = fields.IntegerField(null=True, blank=True)
    player_1_global_volatility = fields.FloatField(null=True, blank=True)
    player_1_score = fields.SmallIntegerField(default=0)
    player_2 = fields.UserField(db_index=True, on_delete=fields.CASCADE)
    player_2_rating = fields.IntegerField(null=True, blank=True)
    player_2_deviation = fields.IntegerField(null=True, blank=True)
    player_2_volatility = fields.FloatField(null=True, blank=True)
    player_2_global_rating = fields.IntegerField(null=True, blank=True)
    player_2_global_deviation = fields.IntegerField(null=True, blank=True)
    player_2_global_volatility = fields.FloatField(null=True, blank=True)
    player_2_score = fields.SmallIntegerField(default=0)
    current_game = fields.SmallIntegerField(default=1)
    wins_required = fields.SmallIntegerField(default=3)
    ruleset = fields.ForeignKey(Ruleset,
                                null=True,
                                blank=True,
                                on_delete=fields.PROTECT)
    # if winner is None, match is active / ongoing
    # if winner is Purah, it was a friendly match
    winner = fields.UserField(null=True,
                              blank=True,
                              db_index=True,
                              on_delete=fields.CASCADE)
    started_at = fields.DateTimeField(auto_now_add=True, db_index=True)
    ended_at = fields.DateTimeField(null=True, blank=True)
    spectating_message = fields.MessageField(null=True,
                                             blank=True,
                                             on_delete=fields.SET_NULL)
    match_end_message = fields.MessageField(null=True,
                                            blank=True,
                                            on_delete=fields.SET_NULL)

    @classmethod
    def ranked_matches_today_qs(cls, player_1, player_2, guild=None):
        one_day_ago = datetime.now() - timedelta(hours=18)  # let's be generous
        if guild is None:
            qs = (cls.objects.filter(started_at__gt=one_day_ago,
                                     tournament=None,
                                     ranked=True,
                                     player_1=player_1,
                                     player_2=player_2)
                  | cls.objects.filter(started_at__gt=one_day_ago,
                                       tournament=None,
                                       ranked=True,
                                       player_1=player_2,
                                       player_2=player_1))
        else:
            qs = (cls.objects.filter(guild=guild,
                                     started_at__gt=one_day_ago,
                                     tournament=None,
                                     ranked=True,
                                     player_1=player_1,
                                     player_2=player_2)
                  | cls.objects.filter(guild=guild,
                                       started_at__gt=one_day_ago,
                                       tournament=None,
                                       ranked=True,
                                       player_1=player_2,
                                       player_2=player_1))
        return qs

    @async_using_db
    def get_match_participants(self):
        if self.tournament is None:
            return None, None
        member_1 = models.Member.objects.get(user=self.player_1,
                                             guild=self.guild)
        participant_1 = Participant.objects.get(pk=member_1)
        member_2 = models.Member.objects.get(user=self.player_2,
                                             guild=self.guild)
        participant_2 = Participant.objects.get(pk=member_2)
        return participant_1, participant_2
Beispiel #6
0
class Context(models.DiscordModel):
    message = fields.MessageField(null=True,
                                  blank=True,
                                  on_delete=fields.SET_NULL)
    prefix = fields.CharField(max_length=64, null=True, blank=True)
    command_name = fields.CharField(max_length=256, null=True, blank=True)
    args_and_kwargs = JSONField(default={})

    _discord_cls = Context

    @property
    def args(self):
        if self.is_fetched:
            return self._discord_obj.args
        return self.args_and_kwargs['args']

    @property
    def kwargs(self):
        if self.is_fetched:
            return self._discord_obj.kwargs
        kwargs = self.args_and_kwargs.copy()
        if 'args' in kwargs:
            kwargs.pop('args')
        return kwargs

    @property
    def _state(self):
        if hasattr(self, '_discord_obj'):
            return self._discord_obj._state
        else:
            return self.message._state

    @classmethod
    @async_using_db
    def from_discord_obj(cls, discord_obj):
        message = async_to_sync(
            models.Message.from_discord_obj(discord_obj.message))
        prefix = discord_obj.prefix
        command_name = discord_obj.command_name
        args_and_kwargs = discord_obj.kwargs or {}
        if discord_obj.args:
            args_and_kwargs['args'] = discord_obj.args
        if not args_and_kwargs:
            args_and_kwargs = None
        return cls(message=message,
                   prefix=prefix,
                   command_name=command_name,
                   args_and_kwargs=args_and_kwargs)

    async def fetch(self):
        if self.message is not None:
            message = await self.message.fetch()
            args_and_kwargs = self.args_and_kwargs.copy()
            self._core: hero.Core
            command = self._core.get_command(self.command_name)
            ctx = hero.Context(message=message,
                               bot=self._core,
                               args=args_and_kwargs.pop('args', []),
                               kwargs=args_and_kwargs,
                               prefix=self.prefix,
                               command=command)
            self._discord_obj = ctx
            return ctx
        else:
            return False
Beispiel #7
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
Beispiel #8
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