class CoreSettings(Model): name = fields.CharField(primary_key=True, max_length=64) prefixes = fields.SeparatedValuesField(max_length=256, default=['!']) description = fields.TextField(max_length=512, blank=True, null=True) status = fields.CharField(max_length=128, blank=True, null=True) lang = fields.LanguageField() home = fields.GuildField(blank=True, null=True, on_delete=fields.SET_NULL)
class Guild(DiscordModel): # TODO normalize Guild register_time = fields.DatetimeField(auto_now_add=True) invite_code = fields.CharField(max_length=64, db_index=True) url = fields.CharField(max_length=256, unique=True) is_deleted = fields.BooleanField(default=False) prefix = fields.CharField(max_length=64) lang = fields.LanguageField(default=Languages.default.value) members = fields.ManyUsersField('hero.User', through='hero.Member', forward_key='user', backward_key='guild', related_name='guilds') @property def invite_url(self): 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: raise ValueError("Not a valid invite URL.")
class TextChannel(DiscordModel): id = fields.BigIntegerField(primary_key=True) guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE) language = fields.LanguageField() _discord_cls = discord.TextChannel _discord_converter_cls = converter.TextChannelConverter @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.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.guild.is_fetched: await self.guild.fetch() return discord_text_channel
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
class CoreSettings(AbstractSettings): name = fields.CharField(pk=True, max_length=64) token = fields.CharField(max_length=64) lang = fields.LanguageField(default=Languages.default.value) @property def logging_level(self): if hero.TEST: return logging.DEBUG else: return logging.WARNING
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
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
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
class TextChannel(DiscordModel): guild = fields.GuildField(on_delete=fields.CASCADE) lang = fields.LanguageField(default=Languages.default.value)
class User(DiscordModel): is_staff = fields.BooleanField(default=False, db_index=True) command_count = fields.IntField(default=0) is_active = fields.BooleanField(default=True, db_index=True) lang = fields.LanguageField(default=Languages.default.value) prefers_dm = fields.BooleanField(default=False)
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