Initial backup 2026-02-17
This commit is contained in:
7
skills/freshrss-reader/.clawhub/origin.json
Normal file
7
skills/freshrss-reader/.clawhub/origin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"registry": "https://clawhub.ai",
|
||||
"slug": "freshrss-reader",
|
||||
"installedVersion": "1.0.0",
|
||||
"installedAt": 1770211204082
|
||||
}
|
||||
5
skills/freshrss-reader/.env
Normal file
5
skills/freshrss-reader/.env
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# FreshRSS Environment Setup
|
||||
export FRESHRSS_URL="http://freshrss.kangaroo-eel.ts.net"
|
||||
export FRESHRSS_USER="Anthony"
|
||||
export FRESHRSS_API_PASSWORD="RecOvery2026!"
|
||||
80
skills/freshrss-reader/SKILL.md
Normal file
80
skills/freshrss-reader/SKILL.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: freshrss
|
||||
description: Query headlines and articles from a self-hosted FreshRSS instance. Use when the user asks for RSS news, latest headlines, feed updates, or wants to browse articles from their FreshRSS reader. Supports filtering by category, time range, and count.
|
||||
---
|
||||
|
||||
# FreshRSS
|
||||
|
||||
Query headlines from a self-hosted FreshRSS instance via the Google Reader compatible API.
|
||||
|
||||
## Setup
|
||||
|
||||
Set these environment variables:
|
||||
|
||||
```bash
|
||||
export FRESHRSS_URL="https://your-freshrss-instance.com"
|
||||
export FRESHRSS_USER="your-username"
|
||||
export FRESHRSS_API_PASSWORD="your-api-password"
|
||||
```
|
||||
|
||||
API password is set in FreshRSS → Settings → Profile → API Management.
|
||||
|
||||
## Commands
|
||||
|
||||
### Get latest headlines
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh headlines --count 10
|
||||
```
|
||||
|
||||
### Get headlines from the last N hours
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh headlines --hours 2
|
||||
```
|
||||
|
||||
### Get headlines from a specific category
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh headlines --category "Technology" --count 15
|
||||
```
|
||||
|
||||
### Get only unread headlines
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh headlines --unread --count 20
|
||||
```
|
||||
|
||||
### Combine filters
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh headlines --category "News" --hours 4 --count 10 --unread
|
||||
```
|
||||
|
||||
### List categories
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh categories
|
||||
```
|
||||
|
||||
### List feeds
|
||||
|
||||
```bash
|
||||
{baseDir}/scripts/freshrss.sh feeds
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Headlines are formatted as:
|
||||
```
|
||||
[date] [source] Title
|
||||
URL
|
||||
Categories: cat1, cat2
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Default count is 20 headlines if not specified
|
||||
- Time filtering uses `--hours` for relative time (e.g., last 2 hours)
|
||||
- Category names are case-sensitive and must match your FreshRSS categories
|
||||
- Use `categories` command first to see available category names
|
||||
141
skills/freshrss-reader/scripts/freshrss.sh
Normal file
141
skills/freshrss-reader/scripts/freshrss.sh
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user