#!/bin/bash # Arch Linux News Notifier # Fetches latest news from Arch Linux RSS feed and shows notifications for new items # Configuration RSS_URL="https://archlinux.org/feeds/news/" CACHE_FILE="$HOME/.cache/arch-news-seen" TEMP_FILE="/tmp/arch-news-latest" ICON_NORMAL="/usr/share/pixmaps/tabler--coffee.svg" ICON_URGENT="/usr/share/pixmaps/charm--circle-warning.svg" # Create cache directory if it doesn't existnClick to open: mkdir -p "$(dirname "$CACHE_FILE")" # Function to extract and format news items parse_rss() { # Debug: show what we're working with if [[ "$DEBUG" == "1" ]]; then echo "=== DEBUG: RSS file content preview ===" >&2 head -20 "$TEMP_FILE" >&2 echo "=== END DEBUG ===" >&2 fi if command -v xmllint >/dev/null 2>&1; then # Use xmllint to extract items properly xmllint --xpath "//item[position()<=5]" "$TEMP_FILE" 2>/dev/null | sed 's||\n|g' | sed 's||\n|g' | grep -A 20 '' | awk -v RS='' ' // { title = ""; link = ""; pubdate = "" # Extract title if (match($0, /]*>([^<]*)<\/title>/, arr)) { title = arr[1] gsub(/>/, ">", title) gsub(/</, "<", title) gsub(/&/, "&", title) } # Extract link (not atom:link) if (match($0, /([^<]*)<\/link>/, arr)) { link = arr[1] } # Extract pubDate if (match($0, /([^<]*)<\/pubDate>/, arr)) { pubdate = arr[1] } if (title && link && pubdate) { print pubdate "|" title "|" link } }' else # Fallback: simple grep approach local temp_items="/tmp/arch-items-$" # Extract each item block awk '//,/<\/item>/' "$TEMP_FILE" >"$temp_items" # Process with simple pattern matching while IFS= read -r line; do if [[ "$line" =~ \([^<]*)\ ]]; then title="${BASH_REMATCH[1]}" title="${title//>/>}" title="${title//</<}" title="${title//&/&}" elif [[ "$line" =~ \([^<]*)\ ]] && [[ ! "$line" =~ atom:link ]]; then link="${BASH_REMATCH[1]}" elif [[ "$line" =~ \([^<]*)\ ]]; then pubdate="${BASH_REMATCH[1]}" # When we have all three, output and reset if [[ -n "$title" && -n "$link" && -n "$pubdate" ]]; then echo "$pubdate|$title|$link" title="" link="" pubdate="" fi fi done <"$temp_items" rm -f "$temp_items" fi } # Function to send notification send_notification() { local title="$1" local body="$2" local url="$3" # Determine urgency level based on content local urgency="normal" local icon=$ICON_NORMAL # Check for urgent intervention keywords (case insensitive) if echo "$title $body" | grep -qi "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)"; then urgency="critical" icon=$ICON_URGENT fi # Send notification with appropriate urgency notify-send \ --urgency="$urgency" \ --app-name="Arch News" \ --icon="$icon" \ --expire-time=20000 \ "$title" \ "$body\n$url" # Debug output if [[ "$DEBUG" == "1" ]]; then echo "=== DEBUG: Notification sent ===" >&2 echo "Title: $title" >&2 echo "Urgency: $urgency" >&2 echo "Reason: $(echo "$title $body" | grep -i "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)" || echo "normal news")" >&2 echo "=== END DEBUG ===" >&2 fi } # Main execution main() { # Parse command line options local force_show=false local show_latest=false while [[ $# -gt 0 ]]; do case $1 in --force) force_show=true shift ;; --show-latest) show_latest=true shift ;; --clear-cache) rm -f "$CACHE_FILE" echo "Cache cleared" exit 0 ;; --help | -h) echo "Usage: $0 [options]" echo "Options:" echo " --force Show notifications for latest news even if already seen" echo " --show-latest Show notification for the latest news item only" echo " --clear-cache Clear the seen items cache" echo " --help Show this help message" exit 0 ;; *) echo "Unknown option: $1" >&2 exit 1 ;; esac done # Check if required tools are available if ! command -v xmllint >/dev/null 2>&1; then echo "Error: xmllint is required (install libxml2-utils)" >&2 exit 1 fi if ! command -v notify-send >/dev/null 2>&1; then echo "Error: notify-send is required" >&2 exit 1 fi # Fetch RSS feed if ! curl -s --max-time 30 "$RSS_URL" >"$TEMP_FILE"; then echo "Error: Failed to fetch RSS feed" >&2 exit 1 fi # Debug: Check if file was created and has content if [[ "$DEBUG" == "1" ]]; then echo "=== DEBUG: Temp file info ===" >&2 ls -la "$TEMP_FILE" >&2 echo "First few lines:" >&2 head -5 "$TEMP_FILE" >&2 echo "=== END DEBUG ===" >&2 fi # Check if file is not empty if [[ ! -s "$TEMP_FILE" ]]; then echo "Error: RSS feed is empty" >&2 exit 1 fi # Create cache file if it doesn't exist [[ ! -f "$CACHE_FILE" ]] && touch "$CACHE_FILE" # Parse RSS and check for new items local new_items=0 local items_processed=0 # Debug: show all parsed items if [[ "$DEBUG" == "1" ]]; then echo "=== DEBUG: All parsed RSS items ===" >&2 parse_rss | nl >&2 echo "=== END DEBUG ===" >&2 fi while IFS='|' read -r pubdate title link; do [[ -z "$title" ]] && continue ((items_processed++)) # Create a unique identifier for this news item local item_id=$(echo "$title$pubdate" | md5sum | cut -d' ' -f1) # Show latest item only if requested if [[ "$show_latest" == true && $items_processed -eq 1 ]]; then local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") send_notification "$title" "Published: $formatted_date" "$link" echo "$item_id" >>"$CACHE_FILE" ((new_items++)) break fi # Check if we've already seen this item (or force showing) if [[ "$force_show" == true ]] || ! grep -q "$item_id" "$CACHE_FILE"; then # New item found - send notification local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") send_notification "$title" "Published: $formatted_date" "$link" # Mark as seen (only if not forcing) if [[ "$force_show" == false ]]; then echo "$item_id" >>"$CACHE_FILE" fi ((new_items++)) # Add delay between notifications for better mako stacking [[ $new_items -gt 1 ]] && sleep 1.5 fi done < <(parse_rss) # Clean up rm -f "$TEMP_FILE" # Limit cache file size (keep last 100 entries) if [[ -f "$CACHE_FILE" ]]; then tail -100 "$CACHE_FILE" >"$CACHE_FILE.tmp" && mv "$CACHE_FILE.tmp" "$CACHE_FILE" fi echo "Checked Arch Linux news - $new_items new items found" } # Run main function main "$@"