#!/bin/bash CACHE_DIR="cache" COUNTRY_FILE="countries.txt" DEFAULT_TOP=100 TOPN=$DEFAULT_TOP mkdir -p "$CACHE_DIR" # Parse -N flag if [[ "$1" =~ ^-([0-9]+)$ ]]; then TOPN="${BASH_REMATCH[1]}" fi # =========================== # LOAD COUNTRY NAMES # =========================== declare -A COUNTRY_NAME while IFS='=' read -r ISO NAME; do [[ -z "$ISO" ]] && continue COUNTRY_NAME["$ISO"]="$NAME" done < "$COUNTRY_FILE" # =========================== # SMART FETCH (ETag-based) # =========================== fetch_if_new() { local url="$1" local outfile="$2" local etagfile="${outfile}.etag" echo "→ Checking $outfile" curl -s \ --etag-save "$etagfile" \ --etag-compare "$etagfile" \ -o "$outfile" \ "$url" echo " Size: $(du -h "$outfile" | cut -f1)" } DETAILS_JSON="$CACHE_DIR/details.json" BANDWIDTH_JSON="$CACHE_DIR/bandwidth.json" MERGED="$CACHE_DIR/merged.txt" echo "=== STEP 1: Downloading (if new) ===" fetch_if_new \ "https://onionoo.torproject.org/details?type=relay&fields=fingerprint,country,nickname" \ "$DETAILS_JSON" fetch_if_new \ "https://onionoo.torproject.org/bandwidth?type=relay&fields=fingerprint,write_history" \ "$BANDWIDTH_JSON" # =========================== # STEP 2: MERGE EVERYTHING IN ONE jq PASS # =========================== echo echo "=== STEP 2: Merging in jq (single pass) ===" jq -s -r ' # Build index of details by fingerprint (.[0].relays | map({ fp: .fingerprint, country: (.country // "??"), nickname: (.nickname // "UnknownRelay") }) | INDEX(.fp) ) as $d # Iterate over bandwidth relays | .[1].relays[] | .fingerprint as $fp | ($d[$fp].country) as $cc | ($d[$fp].nickname) as $nick | (.write_history["1_month"].factor // 0) as $bw # Output: bw fp cc nickname | "\($bw) \($fp) \($cc) \($nick)" ' "$DETAILS_JSON" "$BANDWIDTH_JSON" > "$MERGED" echo " Merged lines: $(wc -l < "$MERGED")" # =========================== # STEP 3: SORT + PRINT # =========================== echo echo "=== STEP 3: Sorting and printing ===" echo echo "=== Top $TOPN Tor Relays (by 1-month write factor) ===" echo sort -nr "$MERGED" | head -n "$TOPN" | while read -r BW FP CC NICK; do CC_UP=$(echo "$CC" | tr '[:lower:]' '[:upper:]') FULL="${COUNTRY_NAME[$CC_UP]}" [[ -z "$FULL" ]] && FULL="$NICK" printf "%-40s | %12.2f | %-20s | %2s (%s)\n" "$FP" "$BW" "$NICK" "$CC_UP" "$FULL" done echo echo "Done."