RSS Watch
RSS Watch provides feed browsing, category grouping, and optional AI summaries.
Public Feed Page
- URL:
/feed
- Browse feeds grouped by category
- Default Feeds per page is now configurable from
/feed-admin (query per_page still overrides per request)
- Open feed source or category RSS
- See first and last discovery timestamps
- View cached AI summaries per category
- Feeds marked as hidden are intentionally omitted from
/feed and use a hash-based direct URL instead, for example /feed/key/{public-hash} and /api/rss/feed/{public-hash}
- Analytics feeds are now available as dedicated selectors under
/api/rss/feed/{selector}:
analytics-daily (alias daily-analytics)
analytics-weekly (alias weekly-analytics)
analytics-monthly (alias monthly-analytics)
analytics-yearly (alias yearly-analytics)
analytics-bulk (alias bulk-analytics, combines daily/weekly/monthly/yearly)
- Analytics feed items now link back to related category/site targets when available (
/api/rss/feed/{category-slug} for category analytics and /feed/{site-urlid} for site analytics)
/feed now includes a mini Ask about all open feeds card where visitors can submit free-form questions against the current open RSS dataset
Feed Q&A and history
- Submit endpoint:
POST /feed/user-questions
- History page:
GET /feed/user-questions
- Guests must pass Cloudflare Turnstile before submit and are limited by configurable daily/weekly quotas
- Logged-in users are also quota-limited (higher defaults), with separate configurable daily/weekly limits
- Admin users are unlimited and bypass Turnstile for this feature
- Guest intake also has a configurable concurrent protection (distinct active guest IPs in a short processing window)
- Every question stores timestamp, actor (
user_id or guest), IP, user-agent, status, answer/error, and response metadata for audit/history
/feed question submit is AJAX-first and renders answer/error inline; standard form post/redirect remains as fallback if JavaScript is unavailable
/feed/user-questions now supports pagination with configurable rows-per-page to keep history manageable when question volume grows
- Admins can now delete question rows from
/feed/user-questions via AJAX inline actions (server redirect fallback still works without JavaScript)
- Admin settings on
/feed/user-questions and /feed-admin now also include answer model and answer tone controls for OpenAI question responses
- Admin history view now includes filters for
status, user_id (or guest), ip, and deleted scope (active/deleted/all)
- Admin moderation now supports both single-row and bulk deletion with explicit delete mode:
hard delete removes rows permanently
soft delete keeps rows with deletion metadata (deleted_at, deleted_by_user_id, deleted_reason)
- Soft-deleted rows can now be restored via admin action (
POST /feed/user-questions/{question}/restore) for undo workflow.
Entry-level noisy post controls (/feed/entry/{contentId})
- For admin users, each entry page now includes per-post controls for persistent noisy/cycling links:
- Mark/unmark cautious mode (auto-purge noisy duplicates on future imports)
- Ignore this post at import (feed-scoped or global), so incoming scraper reports for the link are dropped
- Purge noisy duplicates (keeps newest representative per distinct meaningful hash)
- Purge all except one latest row (hard reset mode)
Analytics Cards on /feed
- Weekly/monthly/yearly cards are shown per category only when cached analytics exist
- Cards are color-coded by period
- Click Read more to expand long analyses
- Markdown is rendered as HTML for readability
Editor Page
- URL:
/rss
- Requires
permission:rss
- Manage feed URLs and metadata
- View cached category analytics
Site Type now supports explicit xpath and json flows in addition to rss and wp
- When
sitetype=xpath, elements JSON rules are required and validated server-side before save
elements accepts both supported extraction shapes:
- object format (
begin + table) for JSON payload traversal
- legacy pipeline-array format for HTML/XPath extraction pipelines
- In the
/rss list UI, elements editing is now shown only for xpath rows and uses a collapsible panel with AJAX save on blur
- New visual helper:
/rss/xpath-lab lets editors paste HTML snippets, inspect a DOM outline, run/test XPath queries, and review a SimpleXML-style XML preview before saving rules
Feed Admin
- URL:
/feed-admin
- Generate daily, weekly, monthly, or yearly category analysis
- Generate daily, weekly, monthly, or yearly site-level analysis per feed/news source
- Use the Anchor date calendar to generate for historical periods (for example last week/month/year/day when a cron run was missed)
- Category cards and site cards now also have optional per-segment Anchor date inputs; precedence is
segment anchor -> global anchor -> current date
- Current period semantics are rolling windows anchored to the selected date (
weekly = last 7 days, monthly = last 30 days), while yearly remains year-to-date from the anchor year start
- Choose model per run
- Optionally provide "watch for" analyst guidance before generation
- Existing cached variants now include per-variant actions for Retranslate and Regenerate (replace) (for both category and site analytics)
- When Regenerate (replace) is used, the currently selected variant/period is overwritten instead of creating an extra duplicate variant
- Site-level generation now has a dedicated per-site optional title field (separate from the global title input)
Scheduler note
- Analytics scheduling is now admin-configurable in
/feed-admin with per-period enable/disable and time fields for daily, weekly, monthly, and yearly.
- Cron should run the scheduler check every minute (either via Laravel
schedule:run or directly via php artisan rss:run-scheduled-analytics).
- The scheduler executes each period once per relevant cycle after its configured server time; missed runs are caught up as soon as possible.
- If a period has no previous scheduler timestamp, it is treated as never-run and is executed immediately once.
- Automatic scheduler runs use a dedicated auto variant title (
[AUTO] Scheduled analytics) and always run with overwrite-current semantics so automatic variants update in place.
- Manual CLI/API generations are still available when needed.
- Manual CLI runs now skip unchanged bucket+variant signatures by default to avoid unnecessary repeat AI calls.
- Use
php artisan rss:generate-analytics --period=yearly --force (or --overwrite-current) to explicitly re-run and overwrite the current bucket row even when the underlying snapshot is unchanged.
RSS Maintenance Admin
- URL:
/rss-maintenance-admin
- Lists suspicious links with unusually many revisions over short windows
- Compares raw hash churn vs meaningful hash churn to identify noisy pseudo-updates
- Supports dry-run and live purge per link, keeping the latest representative per meaningful change
Scraper API (/api/rss/urls)
- Endpoint:
GET /api/rss/urls
- Required query parameter:
scraper=1 (otherwise request is rejected)
always=0 returns a limited due-set for the calling scraper agent (agent_id, fallback agent_name):
- Due checks use each URL's
readinterval
- Due checks are evaluated against that agent's own seen-state (last seen + next allowed)
- One scraper's recent claims no longer block other scraper agents
always=1 bypasses interval gating and returns all scrapeable URLs (deleted=0, noscrape=0)
- Recommended: always send a stable
agent_id (for example hostname or worker id)
Subscriptions
Authenticated users can subscribe per:
Available channels depend on your account settings.
On /feed/subscriptions, channel settings are now AJAX-first with autosave:
- Channel checkbox on/off saves immediately via AJAX
- Text/webhook fields save on blur (when leaving the field)
- Pause/Resume and Remove also use AJAX inline updates
- Explicitly unchecking all channels is now supported (all delivery channels disabled)
If JavaScript is unavailable, the same forms still fall back to normal server post/redirect behavior.
Discord setup notes on /feed/subscriptions:
- You can either paste a Discord webhook URL manually or use Connect Discord app (get webhook).
- OAuth flow uses
webhook.incoming and returns to the app callback (/oauth/discord/callback).
- Callback URL is shown on the page so admins know what to register in Discord developer settings.
- After callback, the latest Discord webhook payload is kept in session and can be reused per subscription with one click.
Delivery behavior:
- New RSS imports now attempt an immediate subscription notification pass right after
/api/rss/update converts inbound rows
- Subscription digests now include a direct Tools entry permalink first (
/feed/entry/{contentId}), with the original source URL included as secondary fallback
- The scheduled
rss:notify-subscribers task still runs every 15 minutes as fallback/retry protection if an immediate delivery is missed or a channel fails temporarily