Files
openclaw-backups/automations/freshrss-digest/daily-digest.sh
2026-02-21 07:01:51 +00:00

255 lines
7.0 KiB
Bash
Executable File

#!/bin/bash
# FreshRSS Smart Digest
# Pulls unread articles, ranks them by relevance, delivers categorized summary
# Runs daily at 7:00 AM (before AI newsletter digest)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../.env" 2>/dev/null || true
# Config
FRESHRSS_URL="${FRESHRSS_URL:-http://freshrss.kangaroo-eel.ts.net}"
FRESHRSS_USER="${FRESHRSS_USER:-anthony}"
TELEGRAM_CHAT="${TELEGRAM_CHAT:-1793951355}"
NTFY_URL="${NTFY_URL:-}"
NTFY_TOPIC="${NTFY_TOPIC:-}"
NTFY_MIN_PRIORITY="${NTFY_MIN_PRIORITY:-4}"
# Your interest keywords for relevance ranking
INTERESTS=(
"AI" "artificial intelligence" "machine learning" "LLM"
"renewable energy" "solar" "wind" "battery" "EV" "electric vehicle"
"politics" "Labor" "election" "government"
"LGBTQ" "LGBT" "queer" "transgender"
"Perth" "Western Australia" "WA"
"climate" "environment"
"mental health" "depression" "therapy"
)
# Priority sources (always include these)
PRIORITY_SOURCES=(
"CNN"
"MSNBC"
"Al Jazeera"
"ABC News"
"The Guardian"
"BlueSky"
)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
fetch_unread() {
log "Fetching unread articles from FreshRSS..."
local raw_output
raw_output=$(/home/openclaw/.openclaw/workspace/skills/freshrss-reader/scripts/freshrss.sh headlines \
--unread --count 50 2>/dev/null) || {
log "ERROR: Failed to fetch from FreshRSS"
return 1
}
echo "$raw_output"
}
# Score an article based on interest keywords
score_article() {
local title="$1"
local source="$2"
local categories="$3"
local score=0
local text="${title,,} ${categories,,}"
# Check interest keywords
for interest in "${INTERESTS[@]}"; do
if [[ "$text" == *"${interest,,}"* ]]; then
((score+=2))
fi
done
# Priority sources get a boost
for priority in "${PRIORITY_SOURCES[@]}"; do
if [[ "$source" == *"$priority"* ]]; then
((score+=3))
fi
done
echo "$score"
}
# Parse articles and build digest
build_digest() {
local raw="$1"
local output=""
local must_read=""
local skimmable=""
local total_count=0
# Parse the output - each article is 3 lines: date/source/title, URL, categories
local current_date=""
local current_source=""
local current_title=""
local current_url=""
local current_cats=""
while IFS= read -r line; do
# Skip empty lines
[[ -z "$line" ]] && continue
# Check if this is a title line (starts with [)
if [[ "$line" =~ ^\[.*\].*:.*$ ]]; then
# Save previous article if exists
if [[ -n "$current_title" ]]; then
local score
score=$(score_article "$current_title" "$current_source" "$current_cats")
local article_formatted="• *${current_source}*: ${current_title}\n ${current_url}\n"
if ((score >= 5)); then
must_read+="$article_formatted\n"
elif ((score >= 2)); then
skimmable+="$article_formatted\n"
fi
((total_count++))
fi
# Parse new article
current_date=$(echo "$line" | sed 's/^\[\([^]]*\)\].*/\1/')
current_source=$(echo "$line" | sed 's/^\[[^]]*\] \([^:]*\):.*/\1/')
current_title=$(echo "$line" | sed 's/^\[[^]]*\] [^:]*: //')
current_url=""
current_cats=""
# Check if this is a URL line
elif [[ "$line" =~ ^https?:// ]]; then
current_url="$line"
# Check if this is categories line
elif [[ "$line" =~ ^Categories:\ ]]; then
current_cats=$(echo "$line" | sed 's/Categories: //')
fi
done <<< "$raw"
# Don't forget the last article
if [[ -n "$current_title" ]]; then
local score
score=$(score_article "$current_title" "$current_source" "$current_cats")
local article_formatted="• *${current_source}*: ${current_title}\n ${current_url}\n"
if ((score >= 5)); then
must_read+="$article_formatted\n"
elif ((score >= 2)); then
skimmable+="$article_formatted\n"
fi
((total_count++))
fi
# Build final message
output="📰 *FreshRSS Daily Digest*\n\n"
output+="Found *$total_count* unread articles.\n\n"
if [[ -n "$must_read" ]]; then
output+="🔥 *Must Read*\n$must_read\n"
fi
if [[ -n "$skimmable" ]]; then
output+="📎 *Skimmable*\n$skimmable\n"
fi
if [[ -z "$must_read" && -z "$skimmable" ]]; then
output+="No high-priority articles today. You're caught up! 🎉\n"
fi
output+="\n📖 [Open FreshRSS]($FRESHRSS_URL)"
echo -e "$output"
}
send_telegram() {
local message="$1"
log "Sending to Telegram..."
if [[ -z "${TELEGRAM_BOT_TOKEN:-}" ]]; then
log "ERROR: TELEGRAM_BOT_TOKEN is not set"
return 1
fi
local tg_response
tg_response=$(curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{
\"chat_id\": \"$TELEGRAM_CHAT\",
\"text\": \"$message\",
\"parse_mode\": \"Markdown\",
\"disable_web_page_preview\": true
}") || {
log "ERROR: Failed to send Telegram message"
return 1
}
if ! echo "$tg_response" | jq -e '.ok == true' > /dev/null 2>&1; then
log "ERROR: Telegram API rejected message: $tg_response"
return 1
fi
log "Sent to Telegram successfully"
}
send_ntfy() {
local title="$1"
local message="$2"
local priority="${3:-4}"
local sound="${4:-default}"
[[ -z "$NTFY_URL" ]] && return 0
[[ -z "$NTFY_TOPIC" ]] && return 0
# Enforce minimum priority (default 4)
if [[ "$priority" =~ ^[0-9]+$ ]] && [[ "$NTFY_MIN_PRIORITY" =~ ^[0-9]+$ ]]; then
if (( priority < NTFY_MIN_PRIORITY )); then
priority="$NTFY_MIN_PRIORITY"
fi
fi
log "Sending to ntfy..."
curl -s -X POST "${NTFY_URL%/}/${NTFY_TOPIC}" \
-H "Title: $title" \
-H "Priority: $priority" \
-H "Sound: $sound" \
-d "$message" > /dev/null || {
log "ERROR: Failed to send ntfy message"
return 1
}
log "Sent to ntfy successfully"
}
main() {
log "Starting FreshRSS digest..."
# Fetch articles
local raw_articles
raw_articles=$(fetch_unread) || exit 1
# Build digest
local digest
digest=$(build_digest "$raw_articles")
# Send to both channels
send_telegram "$digest"
# Plain text version for ntfy (strip markdown)
local plain_digest
plain_digest=$(echo -e "$digest" | sed 's/\*//g' | sed 's/\[\([^]]*\)\]([^)]*)/\1/g')
send_ntfy "FreshRSS Digest" "$plain_digest" 4 default
log "Digest complete!"
}
main "$@"