|
|
|
|
|
|
|
|
import feedparser |
|
|
from datetime import datetime |
|
|
from dataclasses import dataclass |
|
|
from typing import List |
|
|
import os |
|
|
|
|
|
|
|
|
from .config import AppConfig |
|
|
from .data_fetcher import format_published_time |
|
|
from .vibe_logic import VibeChecker, VibeResult |
|
|
|
|
|
|
|
|
@dataclass(frozen=True) |
|
|
class FeedEntry: |
|
|
"""Stores necessary data for a single HN story, including its calculated mood.""" |
|
|
title: str |
|
|
link: str |
|
|
comments_link: str |
|
|
published_time_str: str |
|
|
mood: VibeResult |
|
|
|
|
|
|
|
|
class HnMoodReader: |
|
|
"""Handles model initialization and mood scoring for Hacker News titles.""" |
|
|
def __init__(self, model_name: str): |
|
|
try: |
|
|
from sentence_transformers import SentenceTransformer |
|
|
except ImportError as e: |
|
|
raise ImportError("Please install 'sentence-transformers'") from e |
|
|
|
|
|
print(f"Initializing SentenceTransformer with model: {model_name}...") |
|
|
self.model = SentenceTransformer(model_name, truncate_dim=128) |
|
|
print("Model initialized successfully.") |
|
|
|
|
|
self.vibe_checker = VibeChecker( |
|
|
model=self.model, |
|
|
query_anchor=AppConfig.QUERY_ANCHOR, |
|
|
task_name=AppConfig.TASK_NAME |
|
|
) |
|
|
self.model_name = model_name |
|
|
|
|
|
def _get_mood_result(self, title: str) -> VibeResult: |
|
|
"""Calculates the mood for a title using the VibeChecker.""" |
|
|
return self.vibe_checker.check(title) |
|
|
|
|
|
def fetch_and_score_feed(self) -> List[FeedEntry]: |
|
|
"""Fetches, scores, and sorts entries from the HN RSS feed.""" |
|
|
feed = feedparser.parse(AppConfig.HN_RSS_URL) |
|
|
if feed.bozo: |
|
|
raise IOError(f"Error parsing feed from {AppConfig.HN_RSS_URL}.") |
|
|
|
|
|
scored_entries: List[FeedEntry] = [] |
|
|
for entry in feed.entries: |
|
|
title, link = entry.get('title'), entry.get('link') |
|
|
if not title or not link: |
|
|
continue |
|
|
|
|
|
scored_entries.append( |
|
|
FeedEntry( |
|
|
title=title, |
|
|
link=link, |
|
|
comments_link=entry.get('comments', '#'), |
|
|
published_time_str=format_published_time(entry.published_parsed), |
|
|
mood=self._get_mood_result(title) |
|
|
) |
|
|
) |
|
|
|
|
|
scored_entries.sort(key=lambda x: x.mood.raw_score, reverse=True) |
|
|
return scored_entries |
|
|
|