Files
openclaw-backups/skills/freshrss-reader/scripts/freshrss.sh
2026-02-21 07:01:51 +00:00

142 lines
3.9 KiB
Bash
Executable File

#!/bin/bash
# FreshRSS CLI - Query headlines from a FreshRSS instance
# Uses Google Reader compatible API
#
# Requires environment variables:
# FRESHRSS_URL - Your FreshRSS instance URL (e.g., https://freshrss.example.com)
# FRESHRSS_USER - Your FreshRSS username
# FRESHRSS_API_PASSWORD - Your FreshRSS API password (set in FreshRSS → Settings → Profile → API)
#
# Usage:
# freshrss.sh headlines [--count N] [--hours N] [--category NAME] [--unread]
# freshrss.sh categories
# freshrss.sh feeds
set -e
# --- Config ---
CONFIG_FILE="${FRESHRSS_CONFIG:-$HOME/.config/freshrss/config.json}"
# Load config from file if it exists
if [[ -f "$CONFIG_FILE" ]]; then
FRESHRSS_URL="${FRESHRSS_URL:-$(jq -r '.url // empty' "$CONFIG_FILE")}"
FRESHRSS_USER="${FRESHRSS_USER:-$(jq -r '.user // empty' "$CONFIG_FILE")}"
FRESHRSS_API_PASSWORD="${FRESHRSS_API_PASSWORD:-$(jq -r '.password // empty' "$CONFIG_FILE")}"
fi
if [ -z "$FRESHRSS_URL" ] || [ -z "$FRESHRSS_USER" ] || [ -z "$FRESHRSS_API_PASSWORD" ]; then
echo "Error: Required environment variables not set." >&2
echo "Set FRESHRSS_URL, FRESHRSS_USER, and FRESHRSS_API_PASSWORD" >&2
echo "Or configure $CONFIG_FILE" >&2
exit 1
fi
API_BASE="${FRESHRSS_URL}"
# --- Auth ---
auth_login() {
local RESPONSE
RESPONSE=$(curl -s "${API_BASE}/accounts/ClientLogin?Email=${FRESHRSS_USER}&Passwd=${FRESHRSS_API_PASSWORD}")
AUTH_TOKEN=$(echo "$RESPONSE" | grep "Auth=" | cut -d'=' -f2)
if [ -z "$AUTH_TOKEN" ]; then
echo "Error: Authentication failed" >&2
echo "$RESPONSE" >&2
exit 1
fi
}
api_get() {
local ENDPOINT="$1"
curl -s -H "Authorization:GoogleLogin auth=${AUTH_TOKEN}" "${API_BASE}/reader/api/0/${ENDPOINT}"
}
# --- Commands ---
cmd_categories() {
auth_login
api_get "tag/list?output=json" | jq -r '.tags[] | select(.id | contains("/label/")) | .id | split("/label/")[1]'
}
cmd_feeds() {
auth_login
api_get "subscription/list?output=json" | jq -r '.subscriptions[] | "\(.title) [\(.categories[0].label // "uncategorized")]"'
}
cmd_headlines() {
local COUNT=20
local HOURS=""
local CATEGORY=""
local UNREAD_ONLY=false
local STREAM="reading-list"
while [[ $# -gt 0 ]]; do
case "$1" in
--count) COUNT="$2"; shift 2 ;;
--hours) HOURS="$2"; shift 2 ;;
--category) CATEGORY="$2"; shift 2 ;;
--unread) UNREAD_ONLY=true; shift ;;
*) shift ;;
esac
done
auth_login
# Build stream URL
local URL="stream/contents"
if [ -n "$CATEGORY" ]; then
URL="${URL}/user/-/label/${CATEGORY}"
else
URL="${URL}/user/-/state/com.google/reading-list"
fi
URL="${URL}?output=json&n=${COUNT}"
# Add time filter
if [ -n "$HOURS" ]; then
local SINCE
SINCE=$(date -v-${HOURS}H +%s 2>/dev/null || date -d "${HOURS} hours ago" +%s 2>/dev/null)
if [ -n "$SINCE" ]; then
URL="${URL}&ot=${SINCE}"
fi
fi
# Add unread filter
if [ "$UNREAD_ONLY" = true ]; then
URL="${URL}&xt=user/-/state/com.google/read"
fi
local RESPONSE
RESPONSE=$(api_get "$URL")
# Format output
echo "$RESPONSE" | jq -r '
.items[] |
{
title: .title,
source: .origin.title,
url: (.canonical[0].href // .alternate[0].href // ""),
published: (.published | todate),
categories: [.categories[] | select(contains("/label/")) | split("/label/")[1]]
} |
"[\(.published)] [\(.source)] \(.title)\n \(.url)\n Categories: \(.categories | join(", "))\n"
' 2>/dev/null || echo "No articles found or error parsing response" >&2
}
# --- Main ---
COMMAND="${1:-headlines}"
shift 2>/dev/null || true
case "$COMMAND" in
headlines|news|latest) cmd_headlines "$@" ;;
categories|cats) cmd_categories ;;
feeds) cmd_feeds ;;
*)
echo "Usage: $0 {headlines|categories|feeds}" >&2
echo "" >&2
echo " headlines [--count N] [--hours N] [--category NAME] [--unread]" >&2
echo " categories List all categories" >&2
echo " feeds List all feeds" >&2
exit 1
;;
esac