feat: add news module with admin panel, pagination, and service toggle via env flag

This commit is contained in:
2026-05-11 20:12:43 +00:00
parent f7ca123cd6
commit 331e19b14e
18 changed files with 977 additions and 62 deletions
+59 -50
View File
@@ -77,80 +77,85 @@ class NewsService(BaseService):
articles = data.get("articles", [])
self.log(f"Received {len(articles)} articles")
sync_table = get_table("news_sync")
news_table = get_table("news")
images_table = get_table("news_images")
synced = 0
skipped = 0
below = 0
failed = 0
new_count = 0
updated_count = 0
draft_count = 0
failed_count = 0
for article in articles:
external_id = article.get("guid", "")
if not external_id:
continue
existing = sync_table.find_one(external_id=external_id)
if existing is not None:
skipped += 1
continue
grade = await self._grade_article(article, ai_url, ai_model)
now = datetime.utcnow().isoformat()
if grade is None:
failed += 1
sync_table.insert({
grade_val = 0
auto_published = False
failed_count += 1
elif grade < threshold:
grade_val = grade
auto_published = False
draft_count += 1
else:
grade_val = grade
auto_published = True
is_published = "published" if auto_published else "draft"
existing = news_table.find_one(external_id=external_id)
if existing:
news_table.update({
"id": existing["id"],
"grade": grade_val,
"status": is_published,
"title": article.get("title", ""),
"description": (article.get("description", "") or "")[:5000],
"url": article.get("link", ""),
"source_name": article.get("feed_name", ""),
"content": (article.get("content", "") or "")[:10000],
"author": article.get("author", ""),
"article_published": article.get("published", ""),
"synced_at": now,
}, ["id"])
updated_count += 1
images_table.delete(news_uid=existing["uid"])
article_uid = existing["uid"]
else:
article_uid = generate_uid()
news_table.insert({
"uid": article_uid,
"external_id": external_id,
"grade": 0,
"status": "grading_failed",
"title": article.get("title", ""),
"description": (article.get("description", "") or "")[:5000],
"url": article.get("link", ""),
"image_url": "",
"source_name": article.get("feed_name", ""),
"grade": grade_val,
"status": is_published,
"content": (article.get("content", "") or "")[:10000],
"author": article.get("author", ""),
"article_published": article.get("published", ""),
"synced_at": now,
})
continue
sync_table.insert({
"external_id": external_id,
"grade": grade,
"status": "below_threshold" if grade < threshold else "synced",
"synced_at": now,
})
if grade < threshold:
below += 1
continue
news_uid = generate_uid()
news_table.insert({
"uid": news_uid,
"external_id": external_id,
"title": article.get("title", ""),
"description": (article.get("description", "") or "")[:5000],
"url": article.get("link", ""),
"image_url": "",
"source_name": article.get("feed_name", ""),
"grade": grade,
"content": (article.get("content", "") or "")[:10000],
"author": article.get("author", ""),
"published": article.get("published", ""),
"synced_at": now,
})
new_count += 1
link = article.get("link", "")
if link:
images = await _get_article_images(link)
for img in images:
fresh_images = await _get_article_images(link)
for img in fresh_images:
images_table.insert({
"uid": generate_uid(),
"news_uid": news_uid,
"news_uid": article_uid,
"url": img["url"],
"alt_text": img.get("alt_text", ""),
})
synced += 1
self.log(f"Synced {synced}, skipped {skipped}, below {below}, grading failed {failed}")
self.log(f"New {new_count}, updated {updated_count}, draft {draft_count}, grading failed {failed_count}")
async def _grade_article(self, article: dict, ai_url: str, ai_model: str) -> int | None:
title = (article.get("title", "") or "")[:500]
@@ -172,9 +177,13 @@ class NewsService(BaseService):
"temperature": 0.0,
}
headers = {
"Content-Type": "application/json",
}
try:
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.post(ai_url, json=payload)
resp = await client.post(ai_url, json=payload, headers=headers)
resp.raise_for_status()
result = resp.json()
text = result.get("choices", [{}])[0].get("message", {}).get("content", "")