diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue
index 4be5a93..d366490 100644
--- a/frontend/src/components/AppHeader.vue
+++ b/frontend/src/components/AppHeader.vue
@@ -5,6 +5,7 @@ import { useTheme } from "@/composables/useTheme";
import { useShortcuts } from "@/composables/useShortcuts";
import { useAuthStore } from "@/stores/auth";
import { useChatStore } from "@/stores/chat";
+import { useSettingsStore } from "@/stores/settings";
import AppLogo from "@/components/AppLogo.vue";
import NotificationBell from "@/components/NotificationBell.vue";
@@ -12,6 +13,7 @@ const { theme, toggleTheme } = useTheme();
const { toggleShortcuts } = useShortcuts();
const authStore = useAuthStore();
const chatStore = useChatStore();
+const settingsStore = useSettingsStore();
const router = useRouter();
const route = useRoute();
@@ -78,7 +80,7 @@ router.afterEach(() => {
Subscribe to RSS/Atom feeds and include news in your briefings.
+Tell the briefing what topics you care about. Topics are matched against diff --git a/src/fabledassistant/routes/briefing.py b/src/fabledassistant/routes/briefing.py index 7b3e6da..0cb56c9 100644 --- a/src/fabledassistant/routes/briefing.py +++ b/src/fabledassistant/routes/briefing.py @@ -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) diff --git a/src/fabledassistant/services/briefing_pipeline.py b/src/fabledassistant/services/briefing_pipeline.py index 02cd6ca..516200e 100644 --- a/src/fabledassistant/services/briefing_pipeline.py +++ b/src/fabledassistant/services/briefing_pipeline.py @@ -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, } diff --git a/src/fabledassistant/services/briefing_scheduler.py b/src/fabledassistant/services/briefing_scheduler.py index 62826c5..fdaee41 100644 --- a/src/fabledassistant/services/briefing_scheduler.py +++ b/src/fabledassistant/services/briefing_scheduler.py @@ -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"): diff --git a/src/fabledassistant/services/tools/_registry.py b/src/fabledassistant/services/tools/_registry.py index 3271383..d47842d 100644 --- a/src/fabledassistant/services/tools/_registry.py +++ b/src/fabledassistant/services/tools/_registry.py @@ -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 diff --git a/src/fabledassistant/services/tools/rss.py b/src/fabledassistant/services/tools/rss.py index 434d301..3627abd 100644 --- a/src/fabledassistant/services/tools/rss.py +++ b/src/fabledassistant/services/tools/rss.py @@ -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