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 source_name: str = "" # Name of the source (e.g., "The Straits Times")
24
25
26@dataclass
27class Quote:
28 """A notable quote from the story."""
29 text: str
30 attribution: str
31
32
33@dataclass
34class KagiStory:
35 """
36 Structured representation of a Kagi News story.
37
38 Parsed from RSS feed item with HTML description.
39 """
40 # RSS metadata
41 title: str
42 link: str # Kagi story permalink
43 guid: str
44 pub_date: datetime
45 categories: List[str] = field(default_factory=list)
46
47 # Parsed from HTML description
48 summary: str = ""
49 highlights: List[str] = field(default_factory=list)
50 perspectives: List[Perspective] = field(default_factory=list)
51 quote: Optional[Quote] = None
52 sources: List[Source] = field(default_factory=list)
53 image_url: Optional[str] = None
54 image_alt: Optional[str] = None
55
56 def __post_init__(self):
57 """Validate required fields."""
58 if not self.title:
59 raise ValueError("title is required")
60 if not self.link:
61 raise ValueError("link is required")
62 if not self.guid:
63 raise ValueError("guid is required")
64
65
66@dataclass
67class FeedConfig:
68 """Configuration for a single RSS feed."""
69 name: str
70 url: str
71 community_handle: str
72 enabled: bool = True
73
74
75@dataclass
76class AggregatorConfig:
77 """Full aggregator configuration."""
78 coves_api_url: str
79 feeds: List[FeedConfig]
80 log_level: str = "info"