#!/bin/sh
# ╔═══════════════════════════════════════════════════════════════════╗
# ║ ⚡ CYBERFEED v0.1 - RSS Aggregator for OpenWrt/SecuBox ⚡ ║
# ║ Cyberpunk Social Feed Analyzer with Emoji Enhancement ║
# ║ Author: CyberMind.FR | License: MIT ║
# ╚═══════════════════════════════════════════════════════════════════╝
. /lib/functions.sh
# === CONFIGURATION ===
CYBERFEED_DIR="/tmp/cyberfeed"
CACHE_DIR="${CYBERFEED_DIR}/cache"
OUTPUT_DIR="${CYBERFEED_DIR}/output"
CONFIG_FILE="/etc/cyberfeed/feeds.conf"
TEMPLATE_FILE="/usr/share/cyberfeed/template.html"
MAX_ITEMS=20
CACHE_TTL=300
# Load UCI config
load_config() {
config_load cyberfeed
config_get MAX_ITEMS main max_items 20
config_get CACHE_TTL main cache_ttl 300
config_get OUTPUT_DIR main output_dir "/tmp/cyberfeed/output"
}
# === CYBERPUNK EMOJI MAPPING ===
cyberpunk_emojify() {
local text="$1"
# Security/Hacking themes
echo "$text" | sed -E '
s/(hack|breach|exploit|vulnerab)/\xf0\x9f\x94\x93\1/gi
s/(secur|protect|defense|firewall)/\xf0\x9f\x9b\xa1\xef\xb8\x8f\1/gi
s/(cyber|digital|virtual)/\xe2\x9a\xa1\1/gi
s/(encrypt|crypto|cipher)/\xf0\x9f\x94\x90\1/gi
s/(malware|virus|trojan)/\xe2\x98\xa0\xef\xb8\x8f\1/gi
s/(alert|warning|danger)/\xe2\x9a\xa0\xef\xb8\x8f\1/gi
s/(attack|threat|risk)/\xf0\x9f\x92\x80\1/gi
s/(network|connect|link)/\xf0\x9f\x8c\x90\1/gi
s/(server|cloud|data)/\xf0\x9f\x92\xbe\1/gi
s/(code|program|script)/\xf0\x9f\x92\xbb\1/gi
s/(linux|opensource|github)/\xf0\x9f\x90\xa7\1/gi
s/(robot|automat|ai|machine)/\xf0\x9f\xa4\x96\1/gi
s/(update|upgrade|patch)/\xf0\x9f\x93\xa1\1/gi
s/(launch|deploy|release)/\xf0\x9f\x9a\x80\1/gi
s/(new|annonce|breaking)/\xe2\x9c\xa8\1/gi
s/(success|win|achieve)/\xf0\x9f\x8f\x86\1/gi
s/(fail|error|bug)/\xf0\x9f\x90\x9b\1/gi
s/(magic|mystiq|oracle)/\xf0\x9f\x94\xae\1/gi
s/(energy|power|force)/\xe2\x9a\xa1\1/gi
'
}
# === RSS FETCHER ===
fetch_feed() {
local url="$1"
local name="$2"
local cache_file="${CACHE_DIR}/${name}.xml"
# Check cache freshness
if [ -f "$cache_file" ]; then
local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0)
local now=$(date +%s)
local age=$((now - file_time))
if [ "$age" -lt "$CACHE_TTL" ]; then
cat "$cache_file"
return 0
fi
fi
# Fetch with wget (OpenWrt standard)
wget -q -T 15 -O "$cache_file" "$url" 2>/dev/null
if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then
cat "$cache_file"
else
rm -f "$cache_file"
return 1
fi
}
# === RSS PARSER (Pure AWK for OpenWrt) ===
parse_rss() {
local xml="$1"
local source="$2"
local category="$3"
echo "$xml" | awk -v source="$source" -v category="$category" -v max="$MAX_ITEMS" '
BEGIN {
RS="|"
item_count=0
}
{
title=""
link=""
date=""
desc=""
# Extract title
if (match($0, /
]*>([^<]+)<\/title>/, arr)) title=arr[1]
else if (match($0, /]*><\/title>/, arr)) title=arr[1]
# Extract link
if (match($0, /]*>([^<]+)<\/link>/, arr)) link=arr[1]
else if (match($0, /]*href="([^"]+)"/, arr)) link=arr[1]
# Extract date
if (match($0, /([^<]+), arr)) date=arr[1]
else if (match($0, /([^<]+), arr)) date=arr[1]
else if (match($0, /([^<]+), arr)) date=arr[1]
# Extract description
if (match($0, /]*>([^<]+), arr)) desc=arr[1]
else if (match($0, /]*>([^<]+), arr)) desc=arr[1]
else if (match($0, /]*>]+>/, "", desc)
desc = substr(desc, 1, 280)
printf "{\"title\":\"%s\",\"link\":\"%s\",\"date\":\"%s\",\"desc\":\"%s\",\"source\":\"%s\",\"category\":\"%s\"},", title, link, date, desc, source, category
item_count++
}
}
'
}
# === HTML GENERATOR ===
generate_html() {
local json_file="$1"
local output_file="${OUTPUT_DIR}/index.html"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local feed_count=$(grep -c '"title"' "$json_file" 2>/dev/null || echo 0)
cat > "$output_file" << 'HTMLEOF'
⚡ CYBERFEED ⚡ Neural RSS Matrix
🔮
Awaiting Neural Feed Connection...
HTMLEOF
echo "$output_file"
}
# === STATUS ===
get_status() {
local enabled=$(uci -q get cyberfeed.main.enabled || echo 0)
local feed_count=0
local last_sync="never"
local rssbridge_enabled=$(uci -q get cyberfeed.rssbridge.enabled || echo 0)
local rssbridge_status="stopped"
if [ -f "${OUTPUT_DIR}/feeds.json" ]; then
feed_count=$(grep -c '"title"' "${OUTPUT_DIR}/feeds.json" 2>/dev/null || echo 0)
last_sync=$(stat -c %Y "${OUTPUT_DIR}/feeds.json" 2>/dev/null || echo 0)
fi
if [ "$rssbridge_enabled" = "1" ]; then
if pgrep -f "rss-bridge" >/dev/null 2>&1; then
rssbridge_status="running"
fi
fi
cat << EOF
{
"enabled": $enabled,
"feed_count": $feed_count,
"last_sync": $last_sync,
"cache_ttl": $CACHE_TTL,
"max_items": $MAX_ITEMS,
"rssbridge_enabled": $rssbridge_enabled,
"rssbridge_status": "$rssbridge_status"
}
EOF
}
# === SYNC FEEDS ===
sync_feeds() {
load_config
mkdir -p "$CACHE_DIR" "$OUTPUT_DIR"
if [ ! -f "$CONFIG_FILE" ]; then
echo '[]' > "${OUTPUT_DIR}/feeds.json"
generate_html "${OUTPUT_DIR}/feeds.json"
return 0
fi
local json_items="["
local feed_count=0
while IFS='|' read -r name url type category || [ -n "$name" ]; do
# Skip comments and empty lines
case "$name" in
''|\#*) continue ;;
esac
[ -z "$category" ] && category="custom"
echo "📡 Fetching: $name" >&2
raw_xml=$(fetch_feed "$url" "$name")
if [ -n "$raw_xml" ]; then
parsed=$(parse_rss "$raw_xml" "$name" "$category")
if [ -n "$parsed" ]; then
emojified=$(cyberpunk_emojify "$parsed")
json_items="${json_items}${emojified}"
feed_count=$((feed_count + 1))
fi
fi
done < "$CONFIG_FILE"
# Remove trailing comma, close array
json_items=$(echo "$json_items" | sed 's/,$//')
json_items="${json_items}]"
echo "$json_items" > "${OUTPUT_DIR}/feeds.json"
generate_html "${OUTPUT_DIR}/feeds.json" >/dev/null
# Create symlink for web access
[ -L /www/cyberfeed/index.html ] || ln -sf "${OUTPUT_DIR}/index.html" /www/cyberfeed/index.html 2>/dev/null
[ -L /www/cyberfeed/feeds.json ] || ln -sf "${OUTPUT_DIR}/feeds.json" /www/cyberfeed/feeds.json 2>/dev/null
echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ ⚡ CYBERFEED SYNC COMPLETE ⚡ ║"
echo "╠═══════════════════════════════════════════════════════════╣"
printf "║ 📊 Feeds processed: %-36s ║\n" "$feed_count"
echo "║ 📁 Output: /www/cyberfeed/ ║"
echo "╚═══════════════════════════════════════════════════════════╝"
}
# === LIST FEEDS ===
list_feeds() {
if [ ! -f "$CONFIG_FILE" ]; then
echo "[]"
return
fi
echo "["
local first=1
while IFS='|' read -r name url type category || [ -n "$name" ]; do
case "$name" in
''|\#*) continue ;;
esac
[ "$first" = "1" ] || echo ","
first=0
printf '{"name":"%s","url":"%s","type":"%s","category":"%s"}' \
"$name" "$url" "${type:-rss}" "${category:-custom}"
done < "$CONFIG_FILE"
echo "]"
}
# === ADD FEED ===
add_feed() {
local name="$1"
local url="$2"
local type="${3:-rss}"
local category="${4:-custom}"
[ -z "$name" ] || [ -z "$url" ] && {
echo '{"success":false,"error":"Name and URL required"}'
return 1
}
# Check if feed already exists
if grep -q "^${name}|" "$CONFIG_FILE" 2>/dev/null; then
echo '{"success":false,"error":"Feed already exists"}'
return 1
fi
echo "${name}|${url}|${type}|${category}" >> "$CONFIG_FILE"
echo '{"success":true}'
}
# === DELETE FEED ===
delete_feed() {
local name="$1"
[ -z "$name" ] && {
echo '{"success":false,"error":"Name required"}'
return 1
}
if grep -q "^${name}|" "$CONFIG_FILE" 2>/dev/null; then
sed -i "/^${name}|/d" "$CONFIG_FILE"
rm -f "${CACHE_DIR}/${name}.xml"
echo '{"success":true}'
else
echo '{"success":false,"error":"Feed not found"}'
return 1
fi
}
# === MAIN ===
case "$1" in
sync)
sync_feeds
;;
status)
get_status
;;
list)
list_feeds
;;
add)
add_feed "$2" "$3" "$4" "$5"
;;
delete)
delete_feed "$2"
;;
*)
echo "Usage: $0 {sync|status|list|add|delete}"
echo ""
echo "Commands:"
echo " sync Fetch and process all feeds"
echo " status Show service status (JSON)"
echo " list List configured feeds (JSON)"
echo " add NAME URL [TYPE] [CATEGORY]"
echo " Add a new feed"
echo " delete NAME Remove a feed"
;;
esac