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"