feat: add rss_enabled user setting to toggle RSS functionality
RSS is now off by default. When disabled: - Scheduler skips RSS feed sync during compilation slot - Briefing pipeline skips RSS item gathering - RSS LLM tools (get_rss_items, add_rss_feed) are hidden - API routes return empty results for feeds/news - Frontend hides News nav link, RSS Feeds and News Preferences in settings - Briefing view hides news sidebar section Toggle in Settings > Briefing > RSS / News. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,11 +54,17 @@ async def put_config():
|
||||
return jsonify({"ok": True})
|
||||
|
||||
|
||||
async def _rss_enabled() -> bool:
|
||||
return (await get_setting(g.user.id, "rss_enabled", "false")).lower() == "true"
|
||||
|
||||
|
||||
# ── RSS Feeds ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@briefing_bp.route("/feeds", methods=["GET"])
|
||||
@_REQUIRE
|
||||
async def list_feeds():
|
||||
if not await _rss_enabled():
|
||||
return jsonify([])
|
||||
async with async_session() as session:
|
||||
result = await session.execute(
|
||||
select(RssFeed).where(RssFeed.user_id == g.user.id).order_by(RssFeed.id)
|
||||
@@ -70,6 +76,8 @@ async def list_feeds():
|
||||
@briefing_bp.route("/feeds", methods=["POST"])
|
||||
@_REQUIRE
|
||||
async def add_feed():
|
||||
if not await _rss_enabled():
|
||||
return jsonify({"error": "RSS is disabled"}), 403
|
||||
data = await request.get_json()
|
||||
url = (data.get("url") or "").strip()
|
||||
if not url:
|
||||
@@ -120,6 +128,8 @@ async def delete_feed(feed_id: int):
|
||||
@briefing_bp.route("/feeds/refresh", methods=["POST"])
|
||||
@_REQUIRE
|
||||
async def refresh_feeds():
|
||||
if not await _rss_enabled():
|
||||
return jsonify({"feeds_refreshed": 0, "new_items": 0})
|
||||
results = await rss_svc.refresh_all_feeds(g.user.id)
|
||||
total_new = sum(results.values())
|
||||
return jsonify({"feeds_refreshed": len(results), "new_items": total_new})
|
||||
@@ -128,6 +138,8 @@ async def refresh_feeds():
|
||||
@briefing_bp.route("/feeds/recent", methods=["GET"])
|
||||
@_REQUIRE
|
||||
async def recent_items():
|
||||
if not await _rss_enabled():
|
||||
return jsonify({"items": []})
|
||||
limit = min(int(request.args.get("limit", 20)), 100)
|
||||
items = await rss_svc.get_recent_items(g.user.id, limit=limit)
|
||||
return jsonify({"items": items})
|
||||
@@ -454,6 +466,9 @@ async def list_news():
|
||||
offset — pagination offset (default 0)
|
||||
feed_id — optional integer filter by feed
|
||||
"""
|
||||
if not await _rss_enabled():
|
||||
return jsonify({"items": [], "total": 0})
|
||||
|
||||
from sqlalchemy import text as _text
|
||||
|
||||
days = min(int(request.args.get("days", 2)), 90)
|
||||
|
||||
@@ -20,18 +20,26 @@ SLOT_NAMES = ("compilation", "morning", "midday", "afternoon")
|
||||
# ── External data gather ──────────────────────────────────────────────────────
|
||||
|
||||
async def _gather_external(user_id: int) -> dict:
|
||||
"""Collect RSS items and weather."""
|
||||
from fabledassistant.services.rss import get_recent_items
|
||||
"""Collect RSS items (when enabled) and weather."""
|
||||
from fabledassistant.services.weather import get_cached_weather
|
||||
|
||||
rss_items, weather = await asyncio.gather(
|
||||
get_recent_items(user_id, limit=20),
|
||||
get_cached_weather(user_id),
|
||||
return_exceptions=True,
|
||||
)
|
||||
rss_on = (await get_setting(user_id, "rss_enabled", "false")).lower() == "true"
|
||||
rss_items: list = []
|
||||
if rss_on:
|
||||
from fabledassistant.services.rss import get_recent_items
|
||||
try:
|
||||
rss_items = await get_recent_items(user_id, limit=20)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
weather = await get_cached_weather(user_id)
|
||||
except Exception:
|
||||
weather = []
|
||||
|
||||
return {
|
||||
"rss_items": rss_items if not isinstance(rss_items, Exception) else [],
|
||||
"weather": weather if not isinstance(weather, Exception) else [],
|
||||
"rss_items": rss_items,
|
||||
"weather": weather,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -362,10 +362,12 @@ async def _run_slot_for_user(user_id: int, slot: str) -> None:
|
||||
# Refresh external data first
|
||||
try:
|
||||
import json
|
||||
from fabledassistant.services.rss import refresh_all_feeds
|
||||
config_raw = await get_setting(user_id, "briefing_config", "{}")
|
||||
config = json.loads(config_raw) if isinstance(config_raw, str) else {}
|
||||
await refresh_all_feeds(user_id)
|
||||
rss_on = (await get_setting(user_id, "rss_enabled", "false")).lower() == "true"
|
||||
if rss_on:
|
||||
from fabledassistant.services.rss import refresh_all_feeds
|
||||
await refresh_all_feeds(user_id)
|
||||
from fabledassistant.services import weather as wx
|
||||
for key, loc in config.get("locations", {}).items():
|
||||
if loc.get("lat") and loc.get("lon"):
|
||||
|
||||
@@ -84,6 +84,9 @@ async def _check_requires(user_id: int, requires: str) -> bool:
|
||||
return await is_caldav_configured(user_id)
|
||||
if requires == "searxng":
|
||||
return Config.searxng_enabled()
|
||||
if requires == "rss":
|
||||
from fabledassistant.services.settings import get_setting
|
||||
return (await get_setting(user_id, "rss_enabled", "false")).lower() == "true"
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ logger = logging.getLogger(__name__)
|
||||
},
|
||||
read_only=True,
|
||||
briefing=True,
|
||||
requires="rss",
|
||||
)
|
||||
async def get_rss_items_tool(*, user_id, arguments, **_ctx):
|
||||
from fabledassistant.services.rss import get_recent_items
|
||||
@@ -35,6 +36,7 @@ async def get_rss_items_tool(*, user_id, arguments, **_ctx):
|
||||
"category": {"type": "string", "description": "Optional category label (e.g. 'news', 'tech', 'reddit'). Omit if unsure."},
|
||||
},
|
||||
required=["url"],
|
||||
requires="rss",
|
||||
)
|
||||
async def add_rss_feed_tool(*, user_id, arguments, **_ctx):
|
||||
import asyncio as _asyncio
|
||||
|
||||
Reference in New Issue
Block a user