319 lines
8.3 KiB
Python
319 lines
8.3 KiB
Python
# -*- encoding: utf-8 -*-
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class SavedGuilds(models.Model):
|
|
"""
|
|
Represents a saved Discord Guild (aka Server).
|
|
These are shown in the UI on the sidebar, and can be selected
|
|
to see associated Subscriptions.
|
|
"""
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
|
|
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
|
|
# issue that rounds down the value
|
|
# https://github.com/sequelize/sequelize/issues/9335
|
|
guild_id = models.CharField(
|
|
verbose_name=_("guild id"),
|
|
max_length=128,
|
|
help_text=_("Discord snowflake ID for the represented guild.")
|
|
)
|
|
|
|
name = models.CharField(
|
|
max_length=128,
|
|
help_text=_("Name of the represented guild.")
|
|
)
|
|
|
|
icon = models.CharField(
|
|
max_length=128,
|
|
help_text=_("Hash for the represented guild's icon.")
|
|
)
|
|
|
|
added_by = models.ForeignKey(
|
|
verbose_name=_("added by"),
|
|
to="authentication.DiscordUser",
|
|
on_delete=models.CASCADE,
|
|
help_text=_("The user who added created this instance.")
|
|
)
|
|
|
|
permissions = models.CharField(
|
|
max_length=64,
|
|
help_text=_("Guild permissions for the user who added this instance.")
|
|
)
|
|
|
|
owner = models.BooleanField(
|
|
default=False,
|
|
help_text=_("Does the 'added by' user own this guild?")
|
|
)
|
|
|
|
class Meta:
|
|
"""
|
|
Metadata for the SavedGuilds Model.
|
|
"""
|
|
|
|
verbose_name = "saved guild"
|
|
verbose_name_plural = "saved guilds"
|
|
get_latest_by = "-creation_datetime"
|
|
|
|
constraints = [
|
|
# Prevent servers from having subscriptions with duplicate names
|
|
models.UniqueConstraint(
|
|
fields=["added_by", "guild_id"],
|
|
name="unique added_by & guild_id pair"
|
|
)
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
|
|
class SubChannel(models.Model):
|
|
"""
|
|
Represents a Discord TextChannel, saved against a Subscription.
|
|
SubChannels are used as targets to send content from Subscriptions.
|
|
"""
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
|
|
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
|
|
# issue that rounds down the value
|
|
# https://github.com/sequelize/sequelize/issues/9335
|
|
channel_id = models.CharField(
|
|
verbose_name=_("channel id"),
|
|
max_length=128,
|
|
help_text=_("Discord snowflake ID for the represented Channel.")
|
|
)
|
|
|
|
subscription = models.ForeignKey(
|
|
to="home.Subscription",
|
|
on_delete=models.CASCADE,
|
|
help_text=_("The linked Subscription, must be unique.")
|
|
)
|
|
|
|
class Meta:
|
|
"""
|
|
Metadata for the SubChannel Model.
|
|
"""
|
|
|
|
verbose_name = "SubChannel"
|
|
verbose_name_plural = "SubChannels"
|
|
get_latest_by = "id"
|
|
constraints = [
|
|
# Prevent servers from having subscriptions with duplicate names
|
|
models.UniqueConstraint(
|
|
fields=["channel_id", "subscription"],
|
|
name="unique channel id and subscription pair")
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return self.channel_id
|
|
|
|
|
|
class Filter(models.Model):
|
|
id = models.AutoField(primary_key=True)
|
|
|
|
name = models.CharField(
|
|
max_length=32,
|
|
null=False,
|
|
blank=False
|
|
)
|
|
|
|
keywords = models.CharField(
|
|
max_length=128,
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
regex = models.CharField(
|
|
max_length=128,
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
whitelist = models.BooleanField(default=False)
|
|
|
|
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
|
|
# issue that rounds down the value
|
|
# https://github.com/sequelize/sequelize/issues/9335
|
|
guild_id = models.CharField(
|
|
max_length=128
|
|
)
|
|
|
|
class Meta:
|
|
"""
|
|
Metadata for the Filter Model.
|
|
"""
|
|
|
|
verbose_name = "filter"
|
|
verbose_name_plural = "filters"
|
|
get_latest_by = "id"
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=["name", "guild_id"],
|
|
name="unique name & guild id pair"
|
|
)
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
|
|
class Subscription(models.Model):
|
|
"""
|
|
The Subscription Model.
|
|
'Subscription' in the context of PYRSS is an RSS Feed with various settings.
|
|
"""
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
|
|
name = models.CharField(
|
|
max_length=32,
|
|
null=False,
|
|
blank=False
|
|
)
|
|
|
|
url = models.URLField()
|
|
|
|
# NOTE:
|
|
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
|
|
# issue that rounds down the value
|
|
# https://github.com/sequelize/sequelize/issues/9335
|
|
guild_id = models.CharField(
|
|
max_length=128
|
|
)
|
|
|
|
creation_datetime = models.DateTimeField(
|
|
default=timezone.now,
|
|
editable=False
|
|
)
|
|
|
|
extra_notes = models.CharField(
|
|
max_length=250,
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
filters = models.ManyToManyField(to="home.Filter", blank=True)
|
|
|
|
article_title_mutators = models.ManyToManyField(
|
|
to="home.ArticleMutator",
|
|
related_name="title_mutated_subscriptions",
|
|
blank=True,
|
|
)
|
|
|
|
article_desc_mutators = models.ManyToManyField(
|
|
to="home.ArticleMutator",
|
|
related_name="desc_mutated_subscriptions",
|
|
blank=True,
|
|
)
|
|
|
|
article_fetch_limit = models.PositiveSmallIntegerField(
|
|
validators = [MaxValueValidator(1), MinValueValidator(10)],
|
|
default=10
|
|
)
|
|
|
|
reset_article_fetch_limit = models.BooleanField(default=False)
|
|
|
|
embed_colour = models.CharField(
|
|
max_length=6,
|
|
default="3498db",
|
|
blank=True
|
|
)
|
|
|
|
article_fetch_image = models.BooleanField(
|
|
default=True,
|
|
help_text="Will the resulting article have an image?"
|
|
)
|
|
|
|
active = models.BooleanField(default=True)
|
|
|
|
class Meta:
|
|
"""
|
|
Metadata for the Subscription Model.
|
|
"""
|
|
|
|
verbose_name = "subscription"
|
|
verbose_name_plural = "subscriptions"
|
|
get_latest_by = "-creation_datetime"
|
|
constraints = [
|
|
# Prevent servers from having subscriptions with duplicate names
|
|
models.UniqueConstraint(fields=["name", "guild_id"], name="unique name & server pair")
|
|
]
|
|
|
|
@property
|
|
def channels_count(self) -> int:
|
|
"""
|
|
Returns the number of 'SubChannel' objects assocaited
|
|
with this subscription.
|
|
"""
|
|
|
|
return len(SubChannel.objects.filter(subscription=self))
|
|
|
|
def save(self, *args, **kwargs):
|
|
new_text = "New " if self._state.adding else ""
|
|
log.debug("%sSubscription Saved %s", new_text, self.id)
|
|
super().save(*args, **kwargs)
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
|
|
class TrackedContent(models.Model):
|
|
"""
|
|
Tracked Content Model
|
|
'Tracked Content' identifies articles and tracks them being sent.
|
|
This is used to ensure duplicate articles aren't sent in feeds.
|
|
"""
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
|
|
guid = models.CharField(max_length=256)
|
|
|
|
title = models.CharField(max_length=728)
|
|
|
|
url = models.URLField()
|
|
|
|
subscription = models.ForeignKey(to=Subscription, on_delete=models.CASCADE)
|
|
|
|
channel_id = models.CharField(max_length=128)
|
|
|
|
blocked = models.BooleanField(default=False)
|
|
|
|
creation_datetime = models.DateTimeField(
|
|
default=timezone.now,
|
|
editable=False
|
|
)
|
|
|
|
class Meta:
|
|
|
|
verbose_name = "tracked content"
|
|
verbose_name = "tracked contents"
|
|
get_latest_by = "-creation_datetime"
|
|
constraints = [
|
|
models.UniqueConstraint(fields=["guid", "channel_id"], name="unique guid & channel_id pair"),
|
|
models.UniqueConstraint(fields=["url", "channel_id"], name="unique url & channel_id pair")
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return self.title
|
|
|
|
|
|
class ArticleMutator(models.Model):
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
name = models.CharField(max_length=64)
|
|
value = models.CharField(max_length=32)
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|