aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/FeedSinkPlugin.py
blob: 6785a28a464ebe0705ffa07fae93ded6b550230a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from typing import Any
import xml.etree.ElementTree as ET

from tinydb import Query

from app.Item import Item
from app.PluginInterface import PluginInterface
from app.utils import get_config, get_config_or_default
from app.DatabaseManager import database_manager


class Plugin(PluginInterface):
    def __init__(self, id: str, params: dict[str, Any]) -> None:
        super().__init__(id, params)
        self.feed_id: str = get_config(params, "feed_id")
        self.feed_title: str = get_config(params, "feed_title")
        self.feed_link: str | None = get_config_or_default(params, "feed_link", None)
        self.feed_description: str = get_config(params, "feed_description")
        print(f"[FeedSinkPlugin#{self.id}] initialized")
        print(f"[FeedSinkPlugin#{self.id}] feed will be served at path /{self.feed_id}")

    def build_xml(self, items: list[Item]):
        rss = ET.Element(
            "rss", {"version": "2.0", "xmlns:media": "http://search.yahoo.com/mrss/"}
        )
        channel = ET.SubElement(rss, "channel")
        title = ET.SubElement(channel, "title")
        title.text = self.feed_title

        if self.feed_link is not None:
            link = ET.SubElement(channel, "link")
            link.text = self.feed_link
            favicon_url = f"https://www.google.com/s2/favicons?domain={self.feed_link}"
        else:
            favicon_url = None

        description = ET.SubElement(channel, "description")
        description.text = self.feed_description

        # TODO favicons
        # <image>
        # <link>https://www.aaniwalli.fi/</link>
        # <url>
        # https://www.google.com/s2/favicons?domain=https://www.aaniwalli.fi
        # </url>
        # <title>Ääniwalli</title>
        # </image>

        if favicon_url is not None:
            favicon_image_elem = ET.SubElement(channel, "image")
            favicon_link_elem = ET.SubElement(favicon_image_elem, "link")
            favicon_link_elem.text = self.feed_link
            favicon_url_elem = ET.SubElement(favicon_image_elem, "url")
            favicon_url_elem.text = favicon_url
            favicon_title_elem = ET.SubElement(favicon_image_elem, "title")
            favicon_title_elem.text = self.feed_title

        for item in items:
            item_elem = ET.SubElement(channel, "item")
            if item.title is not None:
                item_title = ET.SubElement(item_elem, "title")
                item_title.text = item.title
            if item.link is not None:
                item_link = ET.SubElement(item_elem, "link")
                item_link.text = item.link
            if item.description is not None:
                item_description = ET.SubElement(item_elem, "description")
                item_description.text = item.description
            if item.category is not None:
                item_category = ET.SubElement(item_elem, "category")
                item_category.text = item.category
            if item.comments is not None:
                item_comments = ET.SubElement(item_elem, "comments")
                item_comments.text = item.comments
            if item.pub_date is not None:
                item_pub_date = ET.SubElement(item_elem, "pubDate")
                item_pub_date.text = item.pub_date.strftime(
                    "%a, %d %b %Y %H:%M:%S +0000"
                )
            if item.author is not None:
                item_author = ET.SubElement(item_elem, "author")
                item_author.text = item.author

            item_guid = ET.SubElement(
                item_elem,
                "guid",
                {"isPermaLink": "true" if item.guid.is_perma_link else "false"},
            )
            item_guid.text = item.guid.value

            if item.media_content is not None:
                item_media_content = ET.SubElement(item_elem, "media:content")
                item_media_content.set("url", item.media_content.url)
                item_media_content.set("medium", item.media_content.medium)

            for enc in item.enclosures:
                ET.SubElement(
                    item_elem,
                    "enclosure",
                    {
                        "length": enc.length,
                        "type": enc.type,
                        "url": enc.url,
                    },
                )

        return ET.tostring(rss, "unicode")

    def process(self, source_id: str | None, items: list[Item]) -> list[Item]:
        print(f"[FeedSinkPlugin#{self.id}] process called, n={len(items)}")

        if source_id is None:
            raise Exception(f"FeedSinkPlugin#{self.id} can not be scheduled")
        if database_manager.db is None:
            raise Exception("Database is not initialized")

        ret = self.build_xml(items)

        Q = Query()
        database_manager.feeds.upsert({"feed_id": self.feed_id, "feed_xml": ret}, Q.feed_id == self.feed_id)  # type: ignore
        print(f"[FeedSinkPlugin#{self.id}] processed")
        return []