Skip to content
Snippets Groups Projects
Verified Commit cfb76c72 authored by jindra12's avatar jindra12
Browse files

Create BE support for mastodon updatable feed

parent 8a0527e5
No related branches found
No related tags found
1 merge request!759Create mastodon block for senate, uniweb and district
...@@ -174,6 +174,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy: ...@@ -174,6 +174,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy:
* `publish_scheduled_pages` - publikuje naplánované stránky (každou hodinu) * `publish_scheduled_pages` - publikuje naplánované stránky (každou hodinu)
* `update_callendars` - stáhne a aktualizuje kalendáře (několikrát denně) * `update_callendars` - stáhne a aktualizuje kalendáře (několikrát denně)
* `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/` * `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/`
* `update_mastodon_feed` - aktualizuje tooty podle nastavení na různých aplikacích
* `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně) * `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně)
* `update_tweets` - aktualizuje tweety podle nastavení na Homepage pirati.cz - vyžaduje mít v .env TWITTER_BEARER_TOKEN, parametr --days určuje stáří tweetů (default 1) * `update_tweets` - aktualizuje tweety podle nastavení na Homepage pirati.cz - vyžaduje mít v .env TWITTER_BEARER_TOKEN, parametr --days určuje stáří tweetů (default 1)
* `update_instagram` - aktualizuje Instagramové posty na Homepage pirati.cz - vyžaduje mít v .env `INSTAGRAM_APP_ID` a `INSTAGRAM_APP_SECRET`. * `update_instagram` - aktualizuje Instagramové posty na Homepage pirati.cz - vyžaduje mít v .env `INSTAGRAM_APP_ID` a `INSTAGRAM_APP_SECRET`.
......
from django.core.management.base import BaseCommand
from shared.services import MastodonFeedDownloadService
class Command(BaseCommand):
def handle(self, *args, **options):
ads = MastodonFeedDownloadService()
ads.perform_update()
self.stdout.write("\nUpdate of mastodon finished!")
import hashlib
import logging import logging
from time import time
from attr import dataclass from attr import dataclass
from django.core.files.temp import NamedTemporaryFile from django.core.files.temp import NamedTemporaryFile
...@@ -15,15 +15,12 @@ from wagtail.models import Page ...@@ -15,15 +15,12 @@ from wagtail.models import Page
from shared.blocks import ( from shared.blocks import (
DEFAULT_CONTENT_BLOCKS, DEFAULT_CONTENT_BLOCKS,
FooterLinksBlock, FooterLinksBlock,
ImageCreatorMixin,
MenuItemBlock, MenuItemBlock,
MenuParentBlock, MenuParentBlock,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
FIVE_MINUTES_IN_SECONDS = 300
@dataclass @dataclass
class MastodonImage: class MastodonImage:
...@@ -66,12 +63,26 @@ class MastodonUser: ...@@ -66,12 +63,26 @@ class MastodonUser:
preferredUsername: str preferredUsername: str
class Mastodon(ImageCreatorMixin, models.Model): class MastodonImageMixin(models.Model):
image = models.ImageField(null=True, blank=True, upload_to="mastodon/")
def download_image(self, url, name):
response = request("GET", url, stream=True)
response.raw.decode_content = True
# Taken from: https://gist.github.com/anderser/2172888
temporary_stored_image = NamedTemporaryFile(delete=True)
temporary_stored_image.write(response.content)
temporary_stored_image.flush()
self.image.save(name, temporary_stored_image)
class Meta:
abstract = True
class Mastodon(MastodonImageMixin, models.Model):
url = models.URLField() url = models.URLField()
toots = models.JSONField(encoder=DjangoJSONEncoder, null=True) toots = models.JSONField(encoder=DjangoJSONEncoder, null=True)
summary = models.TextField(null=True, blank=True) summary = models.TextField(null=True, blank=True)
user_image = models.ImageField(null=True, blank=True, upload_to="mastodon_users/")
timestamp = models.FloatField()
def download_toots(self): def download_toots(self):
""" """
...@@ -89,31 +100,41 @@ class Mastodon(ImageCreatorMixin, models.Model): ...@@ -89,31 +100,41 @@ class Mastodon(ImageCreatorMixin, models.Model):
full_url = self.url + ".json" full_url = self.url + ".json"
return request("GET", full_url).json() return request("GET", full_url).json()
def parse_url(self, now: float): def parse_url(self):
toots: MastodonToots = self.download_toots() toots: MastodonToots = self.download_toots()
user: MastodonUser = self.download_user() user: MastodonUser = self.download_user()
# Taken from: https://gist.github.com/anderser/2172888 self.download_image(f"{user.preferredUsername}.jpg", user["icon"]["url"])
temporary_stored_image = NamedTemporaryFile(delete=True)
temporary_stored_image.write(user.icon.url) for toot in toots["orderedItems"]:
temporary_stored_image.flush() if toot["attachment"] != None:
self.user_image.save(f"{user.preferredUsername}.jpg", temporary_stored_image) for attachment in toot["attachment"]:
mastodon_attachment: MastodonAttachment = (
MastodonAttachment.objects.create(mastodon=self)
)
mastodon_attachment.download_image(
attachment["url"],
hashlib.md5(attachment["url"].encode("ascii")).hexdigest(),
)
attachment["url"] = mastodon_attachment.image.url
self.toots = toots.orderedItems self.toots = toots.orderedItems
self.summary = user.summary self.summary = user.summary
self.timestamp = now
def refresh_toots(self): def refresh_toots(self):
now = time()
if self.timestamp != None and now - self.timestamp < FIVE_MINUTES_IN_SECONDS:
return
try: try:
self.parse_url(now) self.parse_url()
self.save() self.save()
except: except:
logger.error("Mastodon refresh failed for %s", self.url, exc_info=True) logger.error("Mastodon refresh failed for %s", self.url, exc_info=True)
class MastodonAttachment(MastodonImageMixin, models.Model):
mastodon = models.ForeignKey(
Mastodon, null=True, blank=True, on_delete=models.PROTECT
)
class MastodonFeedMixin(models.Model): class MastodonFeedMixin(models.Model):
mastodon_feed = models.URLField( mastodon_feed = models.URLField(
verbose_name="Mastodon feed", verbose_name="Mastodon feed",
...@@ -132,7 +153,7 @@ class MastodonFeedMixin(models.Model): ...@@ -132,7 +153,7 @@ class MastodonFeedMixin(models.Model):
if self.mastodon is not None if self.mastodon is not None
else Mastodon.objects.create(url=self.calendar_url) else Mastodon.objects.create(url=self.calendar_url)
) )
mastodon.parse_url(time()) mastodon.parse_url()
except: except:
raise ValidationError("Update mastodonu se nepovedl") raise ValidationError("Update mastodonu se nepovedl")
......
import logging
from typing import TYPE_CHECKING
from shared.models import Mastodon
if TYPE_CHECKING:
pass
logger = logging.getLogger()
class MastodonFeedDownloadService:
def perform_update(self):
for mastodon in Mastodon.objects.all():
mastodon.refresh_toots()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment