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

Start building mastodon toots parser

parent ef934652
No related branches found
No related tags found
1 merge request!759Create mastodon block for senate, uniweb and district
......@@ -3,4 +3,4 @@
line_length = 88
multi_line_output = 3
include_trailing_comma = true
known_third_party = PyPDF2,arrow,bleach,bs4,captcha,celery,dateutil,django,environ,faker,fastjsonschema,icalevents,markdown,modelcluster,pirates,pytest,pytz,requests,requests_cache,sentry_sdk,taggit,wagtail,wagtailmetadata,weasyprint,yaml
known_third_party = PyPDF2,arrow,attr,bleach,bs4,captcha,celery,dateutil,django,environ,faker,fastjsonschema,icalevents,markdown,modelcluster,pirates,pytest,pytz,requests,requests_cache,sentry_sdk,taggit,wagtail,wagtailmetadata,weasyprint,yaml
import logging
from time import time
from attr import dataclass
from django.core.files.temp import NamedTemporaryFile
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.forms import ValidationError
from django.utils import timezone
from requests import request
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, PublishingPanel
from wagtail.fields import StreamField
from wagtail.models import Page
......@@ -9,12 +15,104 @@ from wagtail.models import Page
from shared.blocks import (
DEFAULT_CONTENT_BLOCKS,
FooterLinksBlock,
ImageCreatorMixin,
MenuItemBlock,
MenuParentBlock,
)
logger = logging.getLogger(__name__)
FIVE_MINUTES_IN_SECONDS = 300
@dataclass
class MastodonImage:
"""
Partial type definition for mastodon API image link
"""
url: str
@dataclass
class MastodonToot:
"""
Partial type definition for mastodon API toot
"""
id: str
content: str
published: str
attachment: list[MastodonImage]
@dataclass
class MastodonToots:
"""
Partial type definition for mastodon API toots
"""
orderedItems: list[MastodonToot]
@dataclass
class MastodonUser:
"""
Partial type definition for mastodon API user
"""
icon: MastodonImage
summary: str
preferredUsername: str
class Mastodon(ImageCreatorMixin, models.Model):
url = models.URLField()
toots = models.JSONField(encoder=DjangoJSONEncoder, null=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):
"""
Downloads toots from mastodon feed
"""
# https://mastodon.pirati.cz/users/ivanbartos/outbox?page=true
full_url = self.url + "/outbox?page=true"
return request("GET", full_url).json()
def download_user(self):
"""
Downloads user page from mastodon feed
"""
# https://mastodon.pirati.cz/users/ivanbartos.json
full_url = self.url + ".json"
return request("GET", full_url).json()
def parse_url(self, now: float):
toots: MastodonToots = self.download_toots()
user: MastodonUser = self.download_user()
# Taken from: https://gist.github.com/anderser/2172888
temporary_stored_image = NamedTemporaryFile(delete=True)
temporary_stored_image.write(user.icon.url)
temporary_stored_image.flush()
self.user_image.save(f"{user.preferredUsername}.jpg", temporary_stored_image)
self.toots = toots.orderedItems
self.summary = user.summary
self.timestamp = now
def refresh_toots(self):
now = time()
if self.timestamp != None and now - self.timestamp < FIVE_MINUTES_IN_SECONDS:
return
try:
self.parse_url(now)
self.save()
except:
logger.error("Mastodon refresh failed for %s", self.url, exc_info=True)
class MastodonFeedMixin(models.Model):
mastodon_feed = models.URLField(
......@@ -22,6 +120,37 @@ class MastodonFeedMixin(models.Model):
blank=True,
help_text="Zadejte mastodon feed url v tomto formátu: https://mastodon.pirati.cz/users/ivanbartos",
)
mastodon = models.ForeignKey(
Mastodon, null=True, blank=True, on_delete=models.PROTECT
)
def clean(self):
super().clean()
try:
mastodon = (
self.mastodon
if self.mastodon is not None
else Mastodon.objects.create(url=self.calendar_url)
)
mastodon.parse_url(time())
except:
raise ValidationError("Update mastodonu se nepovedl")
def save(self, *args, **kwargs):
if self.mastodon_feed:
if self.mastodon:
if self.mastodon.url != self.mastodon_feed:
self.mastodon.url = self.mastodon_feed
self.mastodon.save()
else:
self.mastodon = Mastodon.objects.create(url=self.mastodon_feed)
self.mastodon.refresh_toots()
elif self.mastodon:
self.mastodon = None
super().save(*args, **kwargs)
class Meta:
abstract = True
......
......@@ -2,24 +2,12 @@
{% if is_link %}
<a href="{{ page.mastodon_feed }}" class="social-icon ">{% include "shared/mastodon_icon_snippet.html" with size=link_size invert=True %}</a>
{% else %}
<iframe
allowfullscreen
sandbox="allow-top-navigation allow-scripts allow-popups allow-popups-to-escape-sandbox"
width="400"
height="400"
src="https://mastofeed.com/apiv2/feed?userurl={{ page.mastodon_feed | urlencode:'' }}&theme=dark&size=100&header=true&replies=false&boosts=false">
</iframe>
{% include "shared/mastodon_feed_toots_snippet.html" with mastodon_feed=page.mastodon_feed %}
{% endif %}
{% elif page.root_page.mastodon_feed %}
{% if is_link %}
<a href="{{ page.root_page.mastodon_feed }}" class="social-icon ">{% include "shared/mastodon_icon_snippet.html" with size=link_size invert=True %}</a>
{% else %}
<iframe
allowfullscreen
sandbox="allow-top-navigation allow-scripts allow-popups allow-popups-to-escape-sandbox"
width="400"
height="400"
src="https://mastofeed.com/apiv2/feed?userurl={{ page.root_page.mastodon_feed | urlencode:'' }}&theme=dark&size=100&header=true&replies=false&boosts=false">
</iframe>
{% include "shared/mastodon_feed_toots_snippet.html" with mastodon_feed=page.root_page.mastodon_feed %}
{% endif %}
{% endif %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment