A community based topic aggregation platform built on atproto
1"""
2Data models for Kagi News RSS aggregator.
3"""
4from dataclasses import dataclass, field
5from datetime import datetime
6from typing import List, Optional
7
8
9@dataclass
10class Source:
11 """A news source citation."""
12 title: str
13 url: str
14 domain: str
15
16
17@dataclass
18class Perspective:
19 """A perspective from a particular actor/stakeholder."""
20 actor: str
21 description: str
22 source_url: str
23
24
25@dataclass
26class Quote:
27 """A notable quote from the story."""
28 text: str
29 attribution: str
30
31
32@dataclass
33class KagiStory:
34 """
35 Structured representation of a Kagi News story.
36
37 Parsed from RSS feed item with HTML description.
38 """
39 # RSS metadata
40 title: str
41 link: str # Kagi story permalink
42 guid: str
43 pub_date: datetime
44 categories: List[str] = field(default_factory=list)
45
46 # Parsed from HTML description
47 summary: str = ""
48 highlights: List[str] = field(default_factory=list)
49 perspectives: List[Perspective] = field(default_factory=list)
50 quote: Optional[Quote] = None
51 sources: List[Source] = field(default_factory=list)
52 image_url: Optional[str] = None
53 image_alt: Optional[str] = None
54
55 def __post_init__(self):
56 """Validate required fields."""
57 if not self.title:
58 raise ValueError("title is required")
59 if not self.link:
60 raise ValueError("link is required")
61 if not self.guid:
62 raise ValueError("guid is required")
63
64
65@dataclass
66class FeedConfig:
67 """Configuration for a single RSS feed."""
68 name: str
69 url: str
70 community_handle: str
71 enabled: bool = True
72
73
74@dataclass
75class AggregatorConfig:
76 """Full aggregator configuration."""
77 coves_api_url: str
78 feeds: List[FeedConfig]
79 log_level: str = "info"